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