ole32: Move private data structure out of header file.
[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     {
313         ret = TRUE;
314         CertFreeCTLContext(pCtlContext);
315     }
316     else
317     {
318         PWINECRYPT_CERTSTORE hcs = pCtlContext->hCertStore;
319
320         if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
321             ret = FALSE;
322         else
323             ret = hcs->ctls.deleteContext(hcs, (void *)pCtlContext);
324         CertFreeCTLContext(pCtlContext);
325     }
326     return ret;
327 }
328
329 PCCTL_CONTEXT WINAPI CertCreateCTLContext(DWORD dwMsgAndCertEncodingType,
330  const BYTE *pbCtlEncoded, DWORD cbCtlEncoded)
331 {
332     PCTL_CONTEXT ctl = NULL;
333     HCRYPTMSG msg;
334     BOOL ret;
335     BYTE *content = NULL;
336     DWORD contentSize = 0, size;
337     PCTL_INFO ctlInfo = NULL;
338
339     TRACE("(%08x, %p, %d)\n", dwMsgAndCertEncodingType, pbCtlEncoded,
340      cbCtlEncoded);
341
342     if (GET_CERT_ENCODING_TYPE(dwMsgAndCertEncodingType) != X509_ASN_ENCODING)
343     {
344         SetLastError(E_INVALIDARG);
345         return NULL;
346     }
347     if (!pbCtlEncoded || !cbCtlEncoded)
348     {
349         SetLastError(ERROR_INVALID_DATA);
350         return NULL;
351     }
352     msg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, 0,
353      0, NULL, NULL);
354     if (!msg)
355         return NULL;
356     ret = CryptMsgUpdate(msg, pbCtlEncoded, cbCtlEncoded, TRUE);
357     if (!ret)
358     {
359         SetLastError(ERROR_INVALID_DATA);
360         goto end;
361     }
362     /* Check that it's really a CTL */
363     ret = CryptMsgGetParam(msg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, NULL, &size);
364     if (ret)
365     {
366         char *innerContent = CryptMemAlloc(size);
367
368         if (innerContent)
369         {
370             ret = CryptMsgGetParam(msg, CMSG_INNER_CONTENT_TYPE_PARAM, 0,
371              innerContent, &size);
372             if (ret)
373             {
374                 if (strcmp(innerContent, szOID_CTL))
375                 {
376                     SetLastError(ERROR_INVALID_DATA);
377                     ret = FALSE;
378                 }
379             }
380             CryptMemFree(innerContent);
381         }
382         else
383         {
384             SetLastError(ERROR_OUTOFMEMORY);
385             ret = FALSE;
386         }
387     }
388     if (!ret)
389         goto end;
390     ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, NULL, &contentSize);
391     if (!ret)
392         goto end;
393     content = CryptMemAlloc(contentSize);
394     if (content)
395     {
396         ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, content,
397          &contentSize);
398         if (ret)
399         {
400             ret = CryptDecodeObjectEx(dwMsgAndCertEncodingType, PKCS_CTL,
401              content, contentSize, CRYPT_DECODE_ALLOC_FLAG, NULL,
402              &ctlInfo, &size);
403             if (ret)
404             {
405                 ctl = Context_CreateDataContext(sizeof(CTL_CONTEXT));
406                 if (ctl)
407                 {
408                     BYTE *data = CryptMemAlloc(cbCtlEncoded);
409
410                     if (data)
411                     {
412                         memcpy(data, pbCtlEncoded, cbCtlEncoded);
413                         ctl->dwMsgAndCertEncodingType =
414                          X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
415                         ctl->pbCtlEncoded             = data;
416                         ctl->cbCtlEncoded             = cbCtlEncoded;
417                         ctl->pCtlInfo                 = ctlInfo;
418                         ctl->hCertStore               = NULL;
419                         ctl->hCryptMsg                = msg;
420                         ctl->pbCtlContext             = content;
421                         ctl->cbCtlContext             = contentSize;
422                     }
423                     else
424                     {
425                         SetLastError(ERROR_OUTOFMEMORY);
426                         ret = FALSE;
427                     }
428                 }
429                 else
430                 {
431                     SetLastError(ERROR_OUTOFMEMORY);
432                     ret = FALSE;
433                 }
434             }
435         }
436     }
437     else
438     {
439         SetLastError(ERROR_OUTOFMEMORY);
440         ret = FALSE;
441     }
442
443 end:
444     if (!ret)
445     {
446         CryptMemFree(ctl);
447         ctl = NULL;
448         LocalFree(ctlInfo);
449         CryptMemFree(content);
450         CryptMsgClose(msg);
451     }
452     return ctl;
453 }
454
455 PCCTL_CONTEXT WINAPI CertDuplicateCTLContext(PCCTL_CONTEXT pCtlContext)
456 {
457     TRACE("(%p)\n", pCtlContext);
458     Context_AddRef((void *)pCtlContext, sizeof(CTL_CONTEXT));
459     return pCtlContext;
460 }
461
462 static void CTLDataContext_Free(void *context)
463 {
464     PCTL_CONTEXT ctlContext = context;
465
466     CryptMsgClose(ctlContext->hCryptMsg);
467     CryptMemFree(ctlContext->pbCtlEncoded);
468     CryptMemFree(ctlContext->pbCtlContext);
469     LocalFree(ctlContext->pCtlInfo);
470 }
471
472 BOOL WINAPI CertFreeCTLContext(PCCTL_CONTEXT pCTLContext)
473 {
474     TRACE("(%p)\n", pCTLContext);
475
476     if (pCTLContext)
477         Context_Release((void *)pCTLContext, sizeof(CTL_CONTEXT),
478          CTLDataContext_Free);
479     return TRUE;
480 }
481
482 DWORD WINAPI CertEnumCTLContextProperties(PCCTL_CONTEXT pCTLContext,
483  DWORD dwPropId)
484 {
485     PCONTEXT_PROPERTY_LIST properties = Context_GetProperties(
486      pCTLContext, sizeof(CTL_CONTEXT));
487     DWORD ret;
488
489     TRACE("(%p, %d)\n", pCTLContext, dwPropId);
490
491     if (properties)
492         ret = ContextPropertyList_EnumPropIDs(properties, dwPropId);
493     else
494         ret = 0;
495     return ret;
496 }
497
498 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
499                                    DWORD dwFlags, const void *pvData);
500
501 static BOOL CTLContext_GetHashProp(PCCTL_CONTEXT context, DWORD dwPropId,
502  ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData,
503  DWORD *pcbData)
504 {
505     BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData,
506      pcbData);
507     if (ret && pvData)
508     {
509         CRYPT_DATA_BLOB blob = { *pcbData, pvData };
510
511         ret = CTLContext_SetProperty(context, dwPropId, 0, &blob);
512     }
513     return ret;
514 }
515
516 static BOOL CTLContext_GetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
517                                    void *pvData, DWORD *pcbData)
518 {
519     PCONTEXT_PROPERTY_LIST properties =
520      Context_GetProperties(context, sizeof(CTL_CONTEXT));
521     BOOL ret;
522     CRYPT_DATA_BLOB blob;
523
524     TRACE("(%p, %d, %p, %p)\n", context, dwPropId, pvData, pcbData);
525
526     if (properties)
527         ret = ContextPropertyList_FindProperty(properties, dwPropId, &blob);
528     else
529         ret = FALSE;
530     if (ret)
531     {
532         if (!pvData)
533             *pcbData = blob.cbData;
534         else if (*pcbData < blob.cbData)
535         {
536             SetLastError(ERROR_MORE_DATA);
537             *pcbData = blob.cbData;
538             ret = FALSE;
539         }
540         else
541         {
542             memcpy(pvData, blob.pbData, blob.cbData);
543             *pcbData = blob.cbData;
544         }
545     }
546     else
547     {
548         /* Implicit properties */
549         switch (dwPropId)
550         {
551         case CERT_SHA1_HASH_PROP_ID:
552             ret = CTLContext_GetHashProp(context, dwPropId, CALG_SHA1,
553              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
554             break;
555         case CERT_MD5_HASH_PROP_ID:
556             ret = CTLContext_GetHashProp(context, dwPropId, CALG_MD5,
557              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
558             break;
559         default:
560             SetLastError(CRYPT_E_NOT_FOUND);
561         }
562     }
563     TRACE("returning %d\n", ret);
564     return ret;
565 }
566
567 BOOL WINAPI CertGetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
568  DWORD dwPropId, void *pvData, DWORD *pcbData)
569 {
570     BOOL ret;
571
572     TRACE("(%p, %d, %p, %p)\n", pCTLContext, dwPropId, pvData, pcbData);
573
574     switch (dwPropId)
575     {
576     case 0:
577     case CERT_CERT_PROP_ID:
578     case CERT_CRL_PROP_ID:
579     case CERT_CTL_PROP_ID:
580         SetLastError(E_INVALIDARG);
581         ret = FALSE;
582         break;
583     case CERT_ACCESS_STATE_PROP_ID:
584         if (!pvData)
585         {
586             *pcbData = sizeof(DWORD);
587             ret = TRUE;
588         }
589         else if (*pcbData < sizeof(DWORD))
590         {
591             SetLastError(ERROR_MORE_DATA);
592             *pcbData = sizeof(DWORD);
593             ret = FALSE;
594         }
595         else
596         {
597             if (pCTLContext->hCertStore)
598                 ret = CertGetStoreProperty(pCTLContext->hCertStore, dwPropId,
599                  pvData, pcbData);
600             else
601                 *(DWORD *)pvData = 0;
602             ret = TRUE;
603         }
604         break;
605     default:
606         ret = CTLContext_GetProperty(pCTLContext, dwPropId, pvData,
607          pcbData);
608     }
609     return ret;
610 }
611
612 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
613  DWORD dwFlags, const void *pvData)
614 {
615     PCONTEXT_PROPERTY_LIST properties =
616      Context_GetProperties(context, sizeof(CTL_CONTEXT));
617     BOOL ret;
618
619     TRACE("(%p, %d, %08x, %p)\n", context, dwPropId, dwFlags, pvData);
620
621     if (!properties)
622         ret = FALSE;
623     else if (!pvData)
624     {
625         ContextPropertyList_RemoveProperty(properties, dwPropId);
626         ret = TRUE;
627     }
628     else
629     {
630         switch (dwPropId)
631         {
632         case CERT_AUTO_ENROLL_PROP_ID:
633         case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */
634         case CERT_DESCRIPTION_PROP_ID:
635         case CERT_FRIENDLY_NAME_PROP_ID:
636         case CERT_HASH_PROP_ID:
637         case CERT_KEY_IDENTIFIER_PROP_ID:
638         case CERT_MD5_HASH_PROP_ID:
639         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
640         case CERT_PUBKEY_ALG_PARA_PROP_ID:
641         case CERT_PVK_FILE_PROP_ID:
642         case CERT_SIGNATURE_HASH_PROP_ID:
643         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
644         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
645         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
646         case CERT_ENROLLMENT_PROP_ID:
647         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
648         case CERT_RENEWAL_PROP_ID:
649         {
650             PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvData;
651
652             ret = ContextPropertyList_SetProperty(properties, dwPropId,
653              blob->pbData, blob->cbData);
654             break;
655         }
656         case CERT_DATE_STAMP_PROP_ID:
657             ret = ContextPropertyList_SetProperty(properties, dwPropId,
658              pvData, sizeof(FILETIME));
659             break;
660         default:
661             FIXME("%d: stub\n", dwPropId);
662             ret = FALSE;
663         }
664     }
665     TRACE("returning %d\n", ret);
666     return ret;
667 }
668
669 BOOL WINAPI CertSetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
670  DWORD dwPropId, DWORD dwFlags, const void *pvData)
671 {
672     BOOL ret;
673
674     TRACE("(%p, %d, %08x, %p)\n", pCTLContext, dwPropId, dwFlags, pvData);
675
676     /* Handle special cases for "read-only"/invalid prop IDs.  Windows just
677      * crashes on most of these, I'll be safer.
678      */
679     switch (dwPropId)
680     {
681     case 0:
682     case CERT_ACCESS_STATE_PROP_ID:
683     case CERT_CERT_PROP_ID:
684     case CERT_CRL_PROP_ID:
685     case CERT_CTL_PROP_ID:
686         SetLastError(E_INVALIDARG);
687         return FALSE;
688     }
689     ret = CTLContext_SetProperty(pCTLContext, dwPropId, dwFlags, pvData);
690     TRACE("returning %d\n", ret);
691     return ret;
692 }