crypt32/tests: Fix a test on Vista.
[wine] / dlls / crypt32 / ctl.c
1 /*
2  * Copyright 2008 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19
20 #include <assert.h>
21 #include <stdarg.h>
22
23 #define NONAMELESSUNION
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wincrypt.h"
27 #include "wine/debug.h"
28 #include "crypt32_private.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
31
32 #define CtlContext_CopyProperties(to, from) \
33  Context_CopyProperties((to), (from), sizeof(CTL_CONTEXT))
34
35 BOOL WINAPI CertAddCTLContextToStore(HCERTSTORE hCertStore,
36  PCCTL_CONTEXT pCtlContext, DWORD dwAddDisposition,
37  PCCTL_CONTEXT* ppStoreContext)
38 {
39     PWINECRYPT_CERTSTORE store = (PWINECRYPT_CERTSTORE)hCertStore;
40     BOOL ret = TRUE;
41     PCCTL_CONTEXT toAdd = NULL, existing = NULL;
42
43     TRACE("(%p, %p, %08x, %p)\n", hCertStore, pCtlContext, dwAddDisposition,
44      ppStoreContext);
45
46     if (dwAddDisposition != CERT_STORE_ADD_ALWAYS)
47     {
48         existing = CertFindCTLInStore(hCertStore, 0, 0, CTL_FIND_EXISTING,
49          pCtlContext, NULL);
50     }
51
52     switch (dwAddDisposition)
53     {
54     case CERT_STORE_ADD_ALWAYS:
55         toAdd = CertDuplicateCTLContext(pCtlContext);
56         break;
57     case CERT_STORE_ADD_NEW:
58         if (existing)
59         {
60             TRACE("found matching CTL, not adding\n");
61             SetLastError(CRYPT_E_EXISTS);
62             ret = FALSE;
63         }
64         else
65             toAdd = CertDuplicateCTLContext(pCtlContext);
66         break;
67     case CERT_STORE_ADD_NEWER:
68         if (existing)
69         {
70             LONG newer = CompareFileTime(&existing->pCtlInfo->ThisUpdate,
71              &pCtlContext->pCtlInfo->ThisUpdate);
72
73             if (newer < 0)
74                 toAdd = CertDuplicateCTLContext(pCtlContext);
75             else
76             {
77                 TRACE("existing CTL is newer, not adding\n");
78                 SetLastError(CRYPT_E_EXISTS);
79                 ret = FALSE;
80             }
81         }
82         else
83             toAdd = CertDuplicateCTLContext(pCtlContext);
84         break;
85     case CERT_STORE_ADD_REPLACE_EXISTING:
86         toAdd = CertDuplicateCTLContext(pCtlContext);
87         break;
88     case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES:
89         toAdd = CertDuplicateCTLContext(pCtlContext);
90         if (existing)
91             CtlContext_CopyProperties(toAdd, existing);
92         break;
93     case CERT_STORE_ADD_USE_EXISTING:
94         if (existing)
95             CtlContext_CopyProperties(existing, pCtlContext);
96         break;
97     default:
98         FIXME("Unimplemented add disposition %d\n", dwAddDisposition);
99         ret = FALSE;
100     }
101
102     if (toAdd)
103     {
104         if (store)
105             ret = store->ctls.addContext(store, (void *)toAdd,
106              (void *)existing, (const void **)ppStoreContext);
107         else if (ppStoreContext)
108             *ppStoreContext = CertDuplicateCTLContext(toAdd);
109         CertFreeCTLContext(toAdd);
110     }
111     CertFreeCTLContext(existing);
112
113     TRACE("returning %d\n", ret);
114     return ret;
115 }
116
117 BOOL WINAPI CertAddEncodedCTLToStore(HCERTSTORE hCertStore,
118  DWORD dwMsgAndCertEncodingType, const BYTE *pbCtlEncoded, DWORD cbCtlEncoded,
119  DWORD dwAddDisposition, PCCTL_CONTEXT *ppCtlContext)
120 {
121     PCCTL_CONTEXT ctl = CertCreateCTLContext(dwMsgAndCertEncodingType,
122      pbCtlEncoded, cbCtlEncoded);
123     BOOL ret;
124
125     TRACE("(%p, %08x, %p, %d, %08x, %p)\n", hCertStore,
126      dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, dwAddDisposition,
127      ppCtlContext);
128
129     if (ctl)
130     {
131         ret = CertAddCTLContextToStore(hCertStore, ctl, dwAddDisposition,
132          ppCtlContext);
133         CertFreeCTLContext(ctl);
134     }
135     else
136         ret = FALSE;
137     return ret;
138 }
139
140 PCCTL_CONTEXT WINAPI CertEnumCTLsInStore(HCERTSTORE hCertStore,
141  PCCTL_CONTEXT pPrev)
142 {
143     WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore;
144     PCCTL_CONTEXT ret;
145
146     TRACE("(%p, %p)\n", hCertStore, pPrev);
147     if (!hCertStore)
148         ret = NULL;
149     else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
150         ret = NULL;
151     else
152         ret = (PCCTL_CONTEXT)hcs->ctls.enumContext(hcs, (void *)pPrev);
153     return ret;
154 }
155
156 typedef BOOL (*CtlCompareFunc)(PCCTL_CONTEXT pCtlContext, DWORD dwType,
157  DWORD dwFlags, const void *pvPara);
158
159 static BOOL compare_ctl_any(PCCTL_CONTEXT pCtlContext, DWORD dwType,
160  DWORD dwFlags, const void *pvPara)
161 {
162     return TRUE;
163 }
164
165 static BOOL compare_ctl_by_md5_hash(PCCTL_CONTEXT pCtlContext, DWORD dwType,
166  DWORD dwFlags, const void *pvPara)
167 {
168     BOOL ret;
169     BYTE hash[16];
170     DWORD size = sizeof(hash);
171
172     ret = CertGetCTLContextProperty(pCtlContext, CERT_MD5_HASH_PROP_ID, hash,
173      &size);
174     if (ret)
175     {
176         const CRYPT_HASH_BLOB *pHash = (const CRYPT_HASH_BLOB *)pvPara;
177
178         if (size == pHash->cbData)
179             ret = !memcmp(pHash->pbData, hash, size);
180         else
181             ret = FALSE;
182     }
183     return ret;
184 }
185
186 static BOOL compare_ctl_by_sha1_hash(PCCTL_CONTEXT pCtlContext, DWORD dwType,
187  DWORD dwFlags, const void *pvPara)
188 {
189     BOOL ret;
190     BYTE hash[20];
191     DWORD size = sizeof(hash);
192
193     ret = CertGetCTLContextProperty(pCtlContext, CERT_SHA1_HASH_PROP_ID, hash,
194      &size);
195     if (ret)
196     {
197         const CRYPT_HASH_BLOB *pHash = (const CRYPT_HASH_BLOB *)pvPara;
198
199         if (size == pHash->cbData)
200             ret = !memcmp(pHash->pbData, hash, size);
201         else
202             ret = FALSE;
203     }
204     return ret;
205 }
206
207 static BOOL compare_ctl_existing(PCCTL_CONTEXT pCtlContext, DWORD dwType,
208  DWORD dwFlags, const void *pvPara)
209 {
210     BOOL ret;
211
212     if (pvPara)
213     {
214         PCCTL_CONTEXT ctl = (PCCTL_CONTEXT)pvPara;
215
216         if (pCtlContext->cbCtlContext == ctl->cbCtlContext)
217         {
218             if (ctl->cbCtlContext)
219                 ret = !memcmp(pCtlContext->pbCtlContext, ctl->pbCtlContext,
220                  ctl->cbCtlContext);
221             else
222                 ret = TRUE;
223         }
224         else
225             ret = FALSE;
226     }
227     else
228         ret = FALSE;
229     return ret;
230 }
231
232 PCCTL_CONTEXT WINAPI CertFindCTLInStore(HCERTSTORE hCertStore,
233  DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType,
234  const void *pvFindPara, PCCTL_CONTEXT pPrevCtlContext)
235 {
236     PCCTL_CONTEXT ret;
237     CtlCompareFunc compare;
238
239     TRACE("(%p, %d, %d, %d, %p, %p)\n", hCertStore, dwCertEncodingType,
240          dwFindFlags, dwFindType, pvFindPara, pPrevCtlContext);
241
242     switch (dwFindType)
243     {
244     case CTL_FIND_ANY:
245         compare = compare_ctl_any;
246         break;
247     case CTL_FIND_SHA1_HASH:
248         compare = compare_ctl_by_sha1_hash;
249         break;
250     case CTL_FIND_MD5_HASH:
251         compare = compare_ctl_by_md5_hash;
252         break;
253     case CTL_FIND_EXISTING:
254         compare = compare_ctl_existing;
255         break;
256     default:
257         FIXME("find type %08x unimplemented\n", dwFindType);
258         compare = NULL;
259     }
260
261     if (compare)
262     {
263         BOOL matches = FALSE;
264
265         ret = pPrevCtlContext;
266         do {
267             ret = CertEnumCTLsInStore(hCertStore, ret);
268             if (ret)
269                 matches = compare(ret, dwFindType, dwFindFlags, pvFindPara);
270         } while (ret != NULL && !matches);
271         if (!ret)
272             SetLastError(CRYPT_E_NOT_FOUND);
273     }
274     else
275     {
276         SetLastError(CRYPT_E_NOT_FOUND);
277         ret = NULL;
278     }
279     return ret;
280 }
281
282 BOOL WINAPI CertDeleteCTLFromStore(PCCTL_CONTEXT pCtlContext)
283 {
284     BOOL ret;
285
286     TRACE("(%p)\n", pCtlContext);
287
288     if (!pCtlContext)
289         ret = TRUE;
290     else if (!pCtlContext->hCertStore)
291     {
292         ret = TRUE;
293         CertFreeCTLContext(pCtlContext);
294     }
295     else
296     {
297         PWINECRYPT_CERTSTORE hcs =
298          (PWINECRYPT_CERTSTORE)pCtlContext->hCertStore;
299
300         if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
301             ret = FALSE;
302         else
303             ret = hcs->ctls.deleteContext(hcs, (void *)pCtlContext);
304         CertFreeCTLContext(pCtlContext);
305     }
306     return ret;
307 }
308
309 PCCTL_CONTEXT WINAPI CertCreateCTLContext(DWORD dwMsgAndCertEncodingType,
310  const BYTE *pbCtlEncoded, DWORD cbCtlEncoded)
311 {
312     PCTL_CONTEXT ctl = NULL;
313     HCRYPTMSG msg;
314     BOOL ret;
315     BYTE *content = NULL;
316     DWORD contentSize = 0, size;
317     PCTL_INFO ctlInfo = NULL;
318
319     TRACE("(%08x, %p, %d)\n", dwMsgAndCertEncodingType, pbCtlEncoded,
320      cbCtlEncoded);
321
322     if (GET_CERT_ENCODING_TYPE(dwMsgAndCertEncodingType) != X509_ASN_ENCODING)
323     {
324         SetLastError(E_INVALIDARG);
325         return NULL;
326     }
327     if (!pbCtlEncoded || !cbCtlEncoded)
328     {
329         SetLastError(ERROR_INVALID_DATA);
330         return NULL;
331     }
332     msg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, 0,
333      0, NULL, NULL);
334     if (!msg)
335         return NULL;
336     ret = CryptMsgUpdate(msg, pbCtlEncoded, cbCtlEncoded, TRUE);
337     if (!ret)
338     {
339         SetLastError(ERROR_INVALID_DATA);
340         goto end;
341     }
342     /* Check that it's really a CTL */
343     ret = CryptMsgGetParam(msg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, NULL, &size);
344     if (ret)
345     {
346         char *innerContent = CryptMemAlloc(size);
347
348         if (innerContent)
349         {
350             ret = CryptMsgGetParam(msg, CMSG_INNER_CONTENT_TYPE_PARAM, 0,
351              innerContent, &size);
352             if (ret)
353             {
354                 if (strcmp(innerContent, szOID_CTL))
355                 {
356                     SetLastError(ERROR_INVALID_DATA);
357                     ret = FALSE;
358                 }
359             }
360             CryptMemFree(innerContent);
361         }
362         else
363         {
364             SetLastError(ERROR_OUTOFMEMORY);
365             ret = FALSE;
366         }
367     }
368     if (!ret)
369         goto end;
370     ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, NULL, &contentSize);
371     if (!ret)
372         goto end;
373     content = CryptMemAlloc(contentSize);
374     if (content)
375     {
376         ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, content,
377          &contentSize);
378         if (ret)
379         {
380             ret = CryptDecodeObjectEx(dwMsgAndCertEncodingType, PKCS_CTL,
381              content, contentSize, CRYPT_DECODE_ALLOC_FLAG, NULL,
382              (BYTE *)&ctlInfo, &size);
383             if (ret)
384             {
385                 ctl = (PCTL_CONTEXT)Context_CreateDataContext(
386                  sizeof(CTL_CONTEXT));
387                 if (ctl)
388                 {
389                     BYTE *data = CryptMemAlloc(cbCtlEncoded);
390
391                     if (data)
392                     {
393                         memcpy(data, pbCtlEncoded, cbCtlEncoded);
394                         ctl->dwMsgAndCertEncodingType =
395                          X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
396                         ctl->pbCtlEncoded             = data;
397                         ctl->cbCtlEncoded             = cbCtlEncoded;
398                         ctl->pCtlInfo                 = ctlInfo;
399                         ctl->hCertStore               = NULL;
400                         ctl->hCryptMsg                = msg;
401                         ctl->pbCtlContext             = content;
402                         ctl->cbCtlContext             = contentSize;
403                     }
404                     else
405                     {
406                         SetLastError(ERROR_OUTOFMEMORY);
407                         ret = FALSE;
408                     }
409                 }
410                 else
411                 {
412                     SetLastError(ERROR_OUTOFMEMORY);
413                     ret = FALSE;
414                 }
415             }
416         }
417     }
418     else
419     {
420         SetLastError(ERROR_OUTOFMEMORY);
421         ret = FALSE;
422     }
423
424 end:
425     if (!ret)
426     {
427         CryptMemFree(ctl);
428         ctl = NULL;
429         LocalFree(ctlInfo);
430         CryptMemFree(content);
431         CryptMsgClose(msg);
432     }
433     return (PCCTL_CONTEXT)ctl;
434 }
435
436 PCCTL_CONTEXT WINAPI CertDuplicateCTLContext(PCCTL_CONTEXT pCtlContext)
437 {
438     TRACE("(%p)\n", pCtlContext);
439     Context_AddRef((void *)pCtlContext, sizeof(CTL_CONTEXT));
440     return pCtlContext;
441 }
442
443 static void CTLDataContext_Free(void *context)
444 {
445     PCTL_CONTEXT ctlContext = (PCTL_CONTEXT)context;
446
447     CryptMsgClose(ctlContext->hCryptMsg);
448     CryptMemFree(ctlContext->pbCtlEncoded);
449     CryptMemFree(ctlContext->pbCtlContext);
450     LocalFree(ctlContext->pCtlInfo);
451 }
452
453 BOOL WINAPI CertFreeCTLContext(PCCTL_CONTEXT pCTLContext)
454 {
455     TRACE("(%p)\n", pCTLContext);
456
457     if (pCTLContext)
458         Context_Release((void *)pCTLContext, sizeof(CTL_CONTEXT),
459          CTLDataContext_Free);
460     return TRUE;
461 }
462
463 DWORD WINAPI CertEnumCTLContextProperties(PCCTL_CONTEXT pCTLContext,
464  DWORD dwPropId)
465 {
466     PCONTEXT_PROPERTY_LIST properties = Context_GetProperties(
467      (void *)pCTLContext, sizeof(CTL_CONTEXT));
468     DWORD ret;
469
470     TRACE("(%p, %d)\n", pCTLContext, dwPropId);
471
472     if (properties)
473         ret = ContextPropertyList_EnumPropIDs(properties, dwPropId);
474     else
475         ret = 0;
476     return ret;
477 }
478
479 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
480                                    DWORD dwFlags, const void *pvData);
481
482 static BOOL CTLContext_GetHashProp(PCCTL_CONTEXT context, DWORD dwPropId,
483  ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData,
484  DWORD *pcbData)
485 {
486     BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData,
487      pcbData);
488     if (ret)
489     {
490         CRYPT_DATA_BLOB blob = { *pcbData, pvData };
491
492         ret = CTLContext_SetProperty(context, dwPropId, 0, &blob);
493     }
494     return ret;
495 }
496
497 static BOOL CTLContext_GetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
498                                    void *pvData, DWORD *pcbData)
499 {
500     PCONTEXT_PROPERTY_LIST properties =
501      Context_GetProperties(context, sizeof(CTL_CONTEXT));
502     BOOL ret;
503     CRYPT_DATA_BLOB blob;
504
505     TRACE("(%p, %d, %p, %p)\n", context, dwPropId, pvData, pcbData);
506
507     if (properties)
508         ret = ContextPropertyList_FindProperty(properties, dwPropId, &blob);
509     else
510         ret = FALSE;
511     if (ret)
512     {
513         if (!pvData)
514             *pcbData = blob.cbData;
515         else if (*pcbData < blob.cbData)
516         {
517             SetLastError(ERROR_MORE_DATA);
518             *pcbData = blob.cbData;
519             ret = FALSE;
520         }
521         else
522         {
523             memcpy(pvData, blob.pbData, blob.cbData);
524             *pcbData = blob.cbData;
525         }
526     }
527     else
528     {
529         /* Implicit properties */
530         switch (dwPropId)
531         {
532         case CERT_SHA1_HASH_PROP_ID:
533             ret = CTLContext_GetHashProp(context, dwPropId, CALG_SHA1,
534              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
535             break;
536         case CERT_MD5_HASH_PROP_ID:
537             ret = CTLContext_GetHashProp(context, dwPropId, CALG_MD5,
538              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
539             break;
540         default:
541             SetLastError(CRYPT_E_NOT_FOUND);
542         }
543     }
544     TRACE("returning %d\n", ret);
545     return ret;
546 }
547
548 BOOL WINAPI CertGetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
549  DWORD dwPropId, void *pvData, DWORD *pcbData)
550 {
551     BOOL ret;
552
553     TRACE("(%p, %d, %p, %p)\n", pCTLContext, dwPropId, pvData, pcbData);
554
555     switch (dwPropId)
556     {
557     case 0:
558     case CERT_CERT_PROP_ID:
559     case CERT_CRL_PROP_ID:
560     case CERT_CTL_PROP_ID:
561         SetLastError(E_INVALIDARG);
562         ret = FALSE;
563         break;
564     case CERT_ACCESS_STATE_PROP_ID:
565         if (!pvData)
566         {
567             *pcbData = sizeof(DWORD);
568             ret = TRUE;
569         }
570         else if (*pcbData < sizeof(DWORD))
571         {
572             SetLastError(ERROR_MORE_DATA);
573             *pcbData = sizeof(DWORD);
574             ret = FALSE;
575         }
576         else
577         {
578             if (pCTLContext->hCertStore)
579                 ret = CertGetStoreProperty(pCTLContext->hCertStore, dwPropId,
580                  pvData, pcbData);
581             else
582                 *(DWORD *)pvData = 0;
583             ret = TRUE;
584         }
585         break;
586     default:
587         ret = CTLContext_GetProperty(pCTLContext, dwPropId, pvData,
588          pcbData);
589     }
590     return ret;
591 }
592
593 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
594  DWORD dwFlags, const void *pvData)
595 {
596     PCONTEXT_PROPERTY_LIST properties =
597      Context_GetProperties(context, sizeof(CTL_CONTEXT));
598     BOOL ret;
599
600     TRACE("(%p, %d, %08x, %p)\n", context, dwPropId, dwFlags, pvData);
601
602     if (!properties)
603         ret = FALSE;
604     else if (!pvData)
605     {
606         ContextPropertyList_RemoveProperty(properties, dwPropId);
607         ret = TRUE;
608     }
609     else
610     {
611         switch (dwPropId)
612         {
613         case CERT_AUTO_ENROLL_PROP_ID:
614         case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */
615         case CERT_DESCRIPTION_PROP_ID:
616         case CERT_FRIENDLY_NAME_PROP_ID:
617         case CERT_HASH_PROP_ID:
618         case CERT_KEY_IDENTIFIER_PROP_ID:
619         case CERT_MD5_HASH_PROP_ID:
620         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
621         case CERT_PUBKEY_ALG_PARA_PROP_ID:
622         case CERT_PVK_FILE_PROP_ID:
623         case CERT_SIGNATURE_HASH_PROP_ID:
624         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
625         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
626         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
627         case CERT_ENROLLMENT_PROP_ID:
628         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
629         case CERT_RENEWAL_PROP_ID:
630         {
631             PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvData;
632
633             ret = ContextPropertyList_SetProperty(properties, dwPropId,
634              blob->pbData, blob->cbData);
635             break;
636         }
637         case CERT_DATE_STAMP_PROP_ID:
638             ret = ContextPropertyList_SetProperty(properties, dwPropId,
639              (const BYTE *)pvData, sizeof(FILETIME));
640             break;
641         default:
642             FIXME("%d: stub\n", dwPropId);
643             ret = FALSE;
644         }
645     }
646     TRACE("returning %d\n", ret);
647     return ret;
648 }
649
650 BOOL WINAPI CertSetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
651  DWORD dwPropId, DWORD dwFlags, const void *pvData)
652 {
653     BOOL ret;
654
655     TRACE("(%p, %d, %08x, %p)\n", pCTLContext, dwPropId, dwFlags, pvData);
656
657     /* Handle special cases for "read-only"/invalid prop IDs.  Windows just
658      * crashes on most of these, I'll be safer.
659      */
660     switch (dwPropId)
661     {
662     case 0:
663     case CERT_ACCESS_STATE_PROP_ID:
664     case CERT_CERT_PROP_ID:
665     case CERT_CRL_PROP_ID:
666     case CERT_CTL_PROP_ID:
667         SetLastError(E_INVALIDARG);
668         return FALSE;
669     }
670     ret = CTLContext_SetProperty(pCTLContext, dwPropId, dwFlags, pvData);
671     TRACE("returning %d\n", ret);
672     return ret;
673 }