msvcrt: Handle the SIGBREAK signal.
[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 = 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_NEWER_INHERIT_PROPERTIES:
86         if (existing)
87         {
88             LONG newer = CompareFileTime(&existing->pCtlInfo->ThisUpdate,
89              &pCtlContext->pCtlInfo->ThisUpdate);
90
91             if (newer < 0)
92             {
93                 toAdd = CertDuplicateCTLContext(pCtlContext);
94                 CtlContext_CopyProperties(existing, pCtlContext);
95             }
96             else
97             {
98                 TRACE("existing CTL is newer, not adding\n");
99                 SetLastError(CRYPT_E_EXISTS);
100                 ret = FALSE;
101             }
102         }
103         else
104             toAdd = CertDuplicateCTLContext(pCtlContext);
105         break;
106     case CERT_STORE_ADD_REPLACE_EXISTING:
107         toAdd = CertDuplicateCTLContext(pCtlContext);
108         break;
109     case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES:
110         toAdd = CertDuplicateCTLContext(pCtlContext);
111         if (existing)
112             CtlContext_CopyProperties(toAdd, existing);
113         break;
114     case CERT_STORE_ADD_USE_EXISTING:
115         if (existing)
116             CtlContext_CopyProperties(existing, pCtlContext);
117         break;
118     default:
119         FIXME("Unimplemented add disposition %d\n", dwAddDisposition);
120         ret = FALSE;
121     }
122
123     if (toAdd)
124     {
125         if (store)
126             ret = store->ctls.addContext(store, (void *)toAdd,
127              (void *)existing, (const void **)ppStoreContext);
128         else if (ppStoreContext)
129             *ppStoreContext = CertDuplicateCTLContext(toAdd);
130         CertFreeCTLContext(toAdd);
131     }
132     CertFreeCTLContext(existing);
133
134     TRACE("returning %d\n", ret);
135     return ret;
136 }
137
138 BOOL WINAPI CertAddEncodedCTLToStore(HCERTSTORE hCertStore,
139  DWORD dwMsgAndCertEncodingType, const BYTE *pbCtlEncoded, DWORD cbCtlEncoded,
140  DWORD dwAddDisposition, PCCTL_CONTEXT *ppCtlContext)
141 {
142     PCCTL_CONTEXT ctl = CertCreateCTLContext(dwMsgAndCertEncodingType,
143      pbCtlEncoded, cbCtlEncoded);
144     BOOL ret;
145
146     TRACE("(%p, %08x, %p, %d, %08x, %p)\n", hCertStore,
147      dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, dwAddDisposition,
148      ppCtlContext);
149
150     if (ctl)
151     {
152         ret = CertAddCTLContextToStore(hCertStore, ctl, dwAddDisposition,
153          ppCtlContext);
154         CertFreeCTLContext(ctl);
155     }
156     else
157         ret = FALSE;
158     return ret;
159 }
160
161 PCCTL_CONTEXT WINAPI CertEnumCTLsInStore(HCERTSTORE hCertStore,
162  PCCTL_CONTEXT pPrev)
163 {
164     WINECRYPT_CERTSTORE *hcs = hCertStore;
165     PCCTL_CONTEXT ret;
166
167     TRACE("(%p, %p)\n", hCertStore, pPrev);
168     if (!hCertStore)
169         ret = NULL;
170     else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
171         ret = NULL;
172     else
173         ret = (PCCTL_CONTEXT)hcs->ctls.enumContext(hcs, (void *)pPrev);
174     return ret;
175 }
176
177 typedef BOOL (*CtlCompareFunc)(PCCTL_CONTEXT pCtlContext, DWORD dwType,
178  DWORD dwFlags, const void *pvPara);
179
180 static BOOL compare_ctl_any(PCCTL_CONTEXT pCtlContext, DWORD dwType,
181  DWORD dwFlags, const void *pvPara)
182 {
183     return TRUE;
184 }
185
186 static BOOL compare_ctl_by_md5_hash(PCCTL_CONTEXT pCtlContext, DWORD dwType,
187  DWORD dwFlags, const void *pvPara)
188 {
189     BOOL ret;
190     BYTE hash[16];
191     DWORD size = sizeof(hash);
192
193     ret = CertGetCTLContextProperty(pCtlContext, CERT_MD5_HASH_PROP_ID, hash,
194      &size);
195     if (ret)
196     {
197         const CRYPT_HASH_BLOB *pHash = 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_by_sha1_hash(PCCTL_CONTEXT pCtlContext, DWORD dwType,
208  DWORD dwFlags, const void *pvPara)
209 {
210     BOOL ret;
211     BYTE hash[20];
212     DWORD size = sizeof(hash);
213
214     ret = CertGetCTLContextProperty(pCtlContext, CERT_SHA1_HASH_PROP_ID, hash,
215      &size);
216     if (ret)
217     {
218         const CRYPT_HASH_BLOB *pHash = pvPara;
219
220         if (size == pHash->cbData)
221             ret = !memcmp(pHash->pbData, hash, size);
222         else
223             ret = FALSE;
224     }
225     return ret;
226 }
227
228 static BOOL compare_ctl_existing(PCCTL_CONTEXT pCtlContext, DWORD dwType,
229  DWORD dwFlags, const void *pvPara)
230 {
231     BOOL ret;
232
233     if (pvPara)
234     {
235         PCCTL_CONTEXT ctl = pvPara;
236
237         if (pCtlContext->cbCtlContext == ctl->cbCtlContext)
238         {
239             if (ctl->cbCtlContext)
240                 ret = !memcmp(pCtlContext->pbCtlContext, ctl->pbCtlContext,
241                  ctl->cbCtlContext);
242             else
243                 ret = TRUE;
244         }
245         else
246             ret = FALSE;
247     }
248     else
249         ret = FALSE;
250     return ret;
251 }
252
253 PCCTL_CONTEXT WINAPI CertFindCTLInStore(HCERTSTORE hCertStore,
254  DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType,
255  const void *pvFindPara, PCCTL_CONTEXT pPrevCtlContext)
256 {
257     PCCTL_CONTEXT ret;
258     CtlCompareFunc compare;
259
260     TRACE("(%p, %d, %d, %d, %p, %p)\n", hCertStore, dwCertEncodingType,
261          dwFindFlags, dwFindType, pvFindPara, pPrevCtlContext);
262
263     switch (dwFindType)
264     {
265     case CTL_FIND_ANY:
266         compare = compare_ctl_any;
267         break;
268     case CTL_FIND_SHA1_HASH:
269         compare = compare_ctl_by_sha1_hash;
270         break;
271     case CTL_FIND_MD5_HASH:
272         compare = compare_ctl_by_md5_hash;
273         break;
274     case CTL_FIND_EXISTING:
275         compare = compare_ctl_existing;
276         break;
277     default:
278         FIXME("find type %08x unimplemented\n", dwFindType);
279         compare = NULL;
280     }
281
282     if (compare)
283     {
284         BOOL matches = FALSE;
285
286         ret = pPrevCtlContext;
287         do {
288             ret = CertEnumCTLsInStore(hCertStore, ret);
289             if (ret)
290                 matches = compare(ret, dwFindType, dwFindFlags, pvFindPara);
291         } while (ret != NULL && !matches);
292         if (!ret)
293             SetLastError(CRYPT_E_NOT_FOUND);
294     }
295     else
296     {
297         SetLastError(CRYPT_E_NOT_FOUND);
298         ret = NULL;
299     }
300     return ret;
301 }
302
303 BOOL WINAPI CertDeleteCTLFromStore(PCCTL_CONTEXT pCtlContext)
304 {
305     BOOL ret;
306
307     TRACE("(%p)\n", pCtlContext);
308
309     if (!pCtlContext)
310         ret = TRUE;
311     else if (!pCtlContext->hCertStore)
312         ret = CertFreeCTLContext(pCtlContext);
313     else
314     {
315         PWINECRYPT_CERTSTORE hcs = pCtlContext->hCertStore;
316
317         if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
318             ret = FALSE;
319         else
320             ret = hcs->ctls.deleteContext(hcs, (void *)pCtlContext);
321         if (ret)
322             ret = CertFreeCTLContext(pCtlContext);
323     }
324     return ret;
325 }
326
327 PCCTL_CONTEXT WINAPI CertCreateCTLContext(DWORD dwMsgAndCertEncodingType,
328  const BYTE *pbCtlEncoded, DWORD cbCtlEncoded)
329 {
330     PCTL_CONTEXT ctl = NULL;
331     HCRYPTMSG msg;
332     BOOL ret;
333     BYTE *content = NULL;
334     DWORD contentSize = 0, size;
335     PCTL_INFO ctlInfo = NULL;
336
337     TRACE("(%08x, %p, %d)\n", dwMsgAndCertEncodingType, pbCtlEncoded,
338      cbCtlEncoded);
339
340     if (GET_CERT_ENCODING_TYPE(dwMsgAndCertEncodingType) != X509_ASN_ENCODING)
341     {
342         SetLastError(E_INVALIDARG);
343         return NULL;
344     }
345     if (!pbCtlEncoded || !cbCtlEncoded)
346     {
347         SetLastError(ERROR_INVALID_DATA);
348         return NULL;
349     }
350     msg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, 0,
351      0, NULL, NULL);
352     if (!msg)
353         return NULL;
354     ret = CryptMsgUpdate(msg, pbCtlEncoded, cbCtlEncoded, TRUE);
355     if (!ret)
356     {
357         SetLastError(ERROR_INVALID_DATA);
358         goto end;
359     }
360     /* Check that it's really a CTL */
361     ret = CryptMsgGetParam(msg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, NULL, &size);
362     if (ret)
363     {
364         char *innerContent = CryptMemAlloc(size);
365
366         if (innerContent)
367         {
368             ret = CryptMsgGetParam(msg, CMSG_INNER_CONTENT_TYPE_PARAM, 0,
369              innerContent, &size);
370             if (ret)
371             {
372                 if (strcmp(innerContent, szOID_CTL))
373                 {
374                     SetLastError(ERROR_INVALID_DATA);
375                     ret = FALSE;
376                 }
377             }
378             CryptMemFree(innerContent);
379         }
380         else
381         {
382             SetLastError(ERROR_OUTOFMEMORY);
383             ret = FALSE;
384         }
385     }
386     if (!ret)
387         goto end;
388     ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, NULL, &contentSize);
389     if (!ret)
390         goto end;
391     content = CryptMemAlloc(contentSize);
392     if (content)
393     {
394         ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, content,
395          &contentSize);
396         if (ret)
397         {
398             ret = CryptDecodeObjectEx(dwMsgAndCertEncodingType, PKCS_CTL,
399              content, contentSize, CRYPT_DECODE_ALLOC_FLAG, NULL,
400              &ctlInfo, &size);
401             if (ret)
402             {
403                 ctl = Context_CreateDataContext(sizeof(CTL_CONTEXT));
404                 if (ctl)
405                 {
406                     BYTE *data = CryptMemAlloc(cbCtlEncoded);
407
408                     if (data)
409                     {
410                         memcpy(data, pbCtlEncoded, cbCtlEncoded);
411                         ctl->dwMsgAndCertEncodingType =
412                          X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
413                         ctl->pbCtlEncoded             = data;
414                         ctl->cbCtlEncoded             = cbCtlEncoded;
415                         ctl->pCtlInfo                 = ctlInfo;
416                         ctl->hCertStore               = NULL;
417                         ctl->hCryptMsg                = msg;
418                         ctl->pbCtlContext             = content;
419                         ctl->cbCtlContext             = contentSize;
420                     }
421                     else
422                     {
423                         SetLastError(ERROR_OUTOFMEMORY);
424                         ret = FALSE;
425                     }
426                 }
427                 else
428                 {
429                     SetLastError(ERROR_OUTOFMEMORY);
430                     ret = FALSE;
431                 }
432             }
433         }
434     }
435     else
436     {
437         SetLastError(ERROR_OUTOFMEMORY);
438         ret = FALSE;
439     }
440
441 end:
442     if (!ret)
443     {
444         CryptMemFree(ctl);
445         ctl = NULL;
446         LocalFree(ctlInfo);
447         CryptMemFree(content);
448         CryptMsgClose(msg);
449     }
450     return ctl;
451 }
452
453 PCCTL_CONTEXT WINAPI CertDuplicateCTLContext(PCCTL_CONTEXT pCtlContext)
454 {
455     TRACE("(%p)\n", pCtlContext);
456     if (pCtlContext)
457         Context_AddRef((void *)pCtlContext, sizeof(CTL_CONTEXT));
458     return pCtlContext;
459 }
460
461 static void CTLDataContext_Free(void *context)
462 {
463     PCTL_CONTEXT ctlContext = context;
464
465     CryptMsgClose(ctlContext->hCryptMsg);
466     CryptMemFree(ctlContext->pbCtlEncoded);
467     CryptMemFree(ctlContext->pbCtlContext);
468     LocalFree(ctlContext->pCtlInfo);
469 }
470
471 BOOL WINAPI CertFreeCTLContext(PCCTL_CONTEXT pCTLContext)
472 {
473     BOOL ret = TRUE;
474
475     TRACE("(%p)\n", pCTLContext);
476
477     if (pCTLContext)
478         ret = Context_Release((void *)pCTLContext, sizeof(CTL_CONTEXT),
479          CTLDataContext_Free);
480     return ret;
481 }
482
483 DWORD WINAPI CertEnumCTLContextProperties(PCCTL_CONTEXT pCTLContext,
484  DWORD dwPropId)
485 {
486     PCONTEXT_PROPERTY_LIST properties = Context_GetProperties(
487      pCTLContext, sizeof(CTL_CONTEXT));
488     DWORD ret;
489
490     TRACE("(%p, %d)\n", pCTLContext, dwPropId);
491
492     if (properties)
493         ret = ContextPropertyList_EnumPropIDs(properties, dwPropId);
494     else
495         ret = 0;
496     return ret;
497 }
498
499 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
500                                    DWORD dwFlags, const void *pvData);
501
502 static BOOL CTLContext_GetHashProp(PCCTL_CONTEXT context, DWORD dwPropId,
503  ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData,
504  DWORD *pcbData)
505 {
506     BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData,
507      pcbData);
508     if (ret && pvData)
509     {
510         CRYPT_DATA_BLOB blob = { *pcbData, pvData };
511
512         ret = CTLContext_SetProperty(context, dwPropId, 0, &blob);
513     }
514     return ret;
515 }
516
517 static BOOL CTLContext_GetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
518                                    void *pvData, DWORD *pcbData)
519 {
520     PCONTEXT_PROPERTY_LIST properties =
521      Context_GetProperties(context, sizeof(CTL_CONTEXT));
522     BOOL ret;
523     CRYPT_DATA_BLOB blob;
524
525     TRACE("(%p, %d, %p, %p)\n", context, dwPropId, pvData, pcbData);
526
527     if (properties)
528         ret = ContextPropertyList_FindProperty(properties, dwPropId, &blob);
529     else
530         ret = FALSE;
531     if (ret)
532     {
533         if (!pvData)
534             *pcbData = blob.cbData;
535         else if (*pcbData < blob.cbData)
536         {
537             SetLastError(ERROR_MORE_DATA);
538             *pcbData = blob.cbData;
539             ret = FALSE;
540         }
541         else
542         {
543             memcpy(pvData, blob.pbData, blob.cbData);
544             *pcbData = blob.cbData;
545         }
546     }
547     else
548     {
549         /* Implicit properties */
550         switch (dwPropId)
551         {
552         case CERT_SHA1_HASH_PROP_ID:
553             ret = CTLContext_GetHashProp(context, dwPropId, CALG_SHA1,
554              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
555             break;
556         case CERT_MD5_HASH_PROP_ID:
557             ret = CTLContext_GetHashProp(context, dwPropId, CALG_MD5,
558              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
559             break;
560         default:
561             SetLastError(CRYPT_E_NOT_FOUND);
562         }
563     }
564     TRACE("returning %d\n", ret);
565     return ret;
566 }
567
568 BOOL WINAPI CertGetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
569  DWORD dwPropId, void *pvData, DWORD *pcbData)
570 {
571     BOOL ret;
572
573     TRACE("(%p, %d, %p, %p)\n", pCTLContext, dwPropId, pvData, pcbData);
574
575     switch (dwPropId)
576     {
577     case 0:
578     case CERT_CERT_PROP_ID:
579     case CERT_CRL_PROP_ID:
580     case CERT_CTL_PROP_ID:
581         SetLastError(E_INVALIDARG);
582         ret = FALSE;
583         break;
584     case CERT_ACCESS_STATE_PROP_ID:
585         if (!pvData)
586         {
587             *pcbData = sizeof(DWORD);
588             ret = TRUE;
589         }
590         else if (*pcbData < sizeof(DWORD))
591         {
592             SetLastError(ERROR_MORE_DATA);
593             *pcbData = sizeof(DWORD);
594             ret = FALSE;
595         }
596         else
597         {
598             if (pCTLContext->hCertStore)
599                 ret = CertGetStoreProperty(pCTLContext->hCertStore, dwPropId,
600                  pvData, pcbData);
601             else
602                 *(DWORD *)pvData = 0;
603             ret = TRUE;
604         }
605         break;
606     default:
607         ret = CTLContext_GetProperty(pCTLContext, dwPropId, pvData,
608          pcbData);
609     }
610     return ret;
611 }
612
613 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
614  DWORD dwFlags, const void *pvData)
615 {
616     PCONTEXT_PROPERTY_LIST properties =
617      Context_GetProperties(context, sizeof(CTL_CONTEXT));
618     BOOL ret;
619
620     TRACE("(%p, %d, %08x, %p)\n", context, dwPropId, dwFlags, pvData);
621
622     if (!properties)
623         ret = FALSE;
624     else if (!pvData)
625     {
626         ContextPropertyList_RemoveProperty(properties, dwPropId);
627         ret = TRUE;
628     }
629     else
630     {
631         switch (dwPropId)
632         {
633         case CERT_AUTO_ENROLL_PROP_ID:
634         case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */
635         case CERT_DESCRIPTION_PROP_ID:
636         case CERT_FRIENDLY_NAME_PROP_ID:
637         case CERT_HASH_PROP_ID:
638         case CERT_KEY_IDENTIFIER_PROP_ID:
639         case CERT_MD5_HASH_PROP_ID:
640         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
641         case CERT_PUBKEY_ALG_PARA_PROP_ID:
642         case CERT_PVK_FILE_PROP_ID:
643         case CERT_SIGNATURE_HASH_PROP_ID:
644         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
645         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
646         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
647         case CERT_ENROLLMENT_PROP_ID:
648         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
649         case CERT_RENEWAL_PROP_ID:
650         {
651             PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvData;
652
653             ret = ContextPropertyList_SetProperty(properties, dwPropId,
654              blob->pbData, blob->cbData);
655             break;
656         }
657         case CERT_DATE_STAMP_PROP_ID:
658             ret = ContextPropertyList_SetProperty(properties, dwPropId,
659              pvData, sizeof(FILETIME));
660             break;
661         default:
662             FIXME("%d: stub\n", dwPropId);
663             ret = FALSE;
664         }
665     }
666     TRACE("returning %d\n", ret);
667     return ret;
668 }
669
670 BOOL WINAPI CertSetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
671  DWORD dwPropId, DWORD dwFlags, const void *pvData)
672 {
673     BOOL ret;
674
675     TRACE("(%p, %d, %08x, %p)\n", pCTLContext, dwPropId, dwFlags, pvData);
676
677     /* Handle special cases for "read-only"/invalid prop IDs.  Windows just
678      * crashes on most of these, I'll be safer.
679      */
680     switch (dwPropId)
681     {
682     case 0:
683     case CERT_ACCESS_STATE_PROP_ID:
684     case CERT_CERT_PROP_ID:
685     case CERT_CRL_PROP_ID:
686     case CERT_CTL_PROP_ID:
687         SetLastError(E_INVALIDARG);
688         return FALSE;
689     }
690     ret = CTLContext_SetProperty(pCTLContext, dwPropId, dwFlags, pvData);
691     TRACE("returning %d\n", ret);
692     return ret;
693 }