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