crypt32: Fix Spelling of 'superseded'.
[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 = Context_CreateDataContext(sizeof(CTL_CONTEXT));
386                 if (ctl)
387                 {
388                     BYTE *data = CryptMemAlloc(cbCtlEncoded);
389
390                     if (data)
391                     {
392                         memcpy(data, pbCtlEncoded, cbCtlEncoded);
393                         ctl->dwMsgAndCertEncodingType =
394                          X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
395                         ctl->pbCtlEncoded             = data;
396                         ctl->cbCtlEncoded             = cbCtlEncoded;
397                         ctl->pCtlInfo                 = ctlInfo;
398                         ctl->hCertStore               = NULL;
399                         ctl->hCryptMsg                = msg;
400                         ctl->pbCtlContext             = content;
401                         ctl->cbCtlContext             = contentSize;
402                     }
403                     else
404                     {
405                         SetLastError(ERROR_OUTOFMEMORY);
406                         ret = FALSE;
407                     }
408                 }
409                 else
410                 {
411                     SetLastError(ERROR_OUTOFMEMORY);
412                     ret = FALSE;
413                 }
414             }
415         }
416     }
417     else
418     {
419         SetLastError(ERROR_OUTOFMEMORY);
420         ret = FALSE;
421     }
422
423 end:
424     if (!ret)
425     {
426         CryptMemFree(ctl);
427         ctl = NULL;
428         LocalFree(ctlInfo);
429         CryptMemFree(content);
430         CryptMsgClose(msg);
431     }
432     return (PCCTL_CONTEXT)ctl;
433 }
434
435 PCCTL_CONTEXT WINAPI CertDuplicateCTLContext(PCCTL_CONTEXT pCtlContext)
436 {
437     TRACE("(%p)\n", pCtlContext);
438     Context_AddRef((void *)pCtlContext, sizeof(CTL_CONTEXT));
439     return pCtlContext;
440 }
441
442 static void CTLDataContext_Free(void *context)
443 {
444     PCTL_CONTEXT ctlContext = (PCTL_CONTEXT)context;
445
446     CryptMsgClose(ctlContext->hCryptMsg);
447     CryptMemFree(ctlContext->pbCtlEncoded);
448     CryptMemFree(ctlContext->pbCtlContext);
449     LocalFree(ctlContext->pCtlInfo);
450 }
451
452 BOOL WINAPI CertFreeCTLContext(PCCTL_CONTEXT pCTLContext)
453 {
454     TRACE("(%p)\n", pCTLContext);
455
456     if (pCTLContext)
457         Context_Release((void *)pCTLContext, sizeof(CTL_CONTEXT),
458          CTLDataContext_Free);
459     return TRUE;
460 }
461
462 DWORD WINAPI CertEnumCTLContextProperties(PCCTL_CONTEXT pCTLContext,
463  DWORD dwPropId)
464 {
465     PCONTEXT_PROPERTY_LIST properties = Context_GetProperties(
466      (void *)pCTLContext, sizeof(CTL_CONTEXT));
467     DWORD ret;
468
469     TRACE("(%p, %d)\n", pCTLContext, dwPropId);
470
471     if (properties)
472         ret = ContextPropertyList_EnumPropIDs(properties, dwPropId);
473     else
474         ret = 0;
475     return ret;
476 }
477
478 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
479                                    DWORD dwFlags, const void *pvData);
480
481 static BOOL CTLContext_GetHashProp(PCCTL_CONTEXT context, DWORD dwPropId,
482  ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData,
483  DWORD *pcbData)
484 {
485     BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData,
486      pcbData);
487     if (ret)
488     {
489         CRYPT_DATA_BLOB blob = { *pcbData, pvData };
490
491         ret = CTLContext_SetProperty(context, dwPropId, 0, &blob);
492     }
493     return ret;
494 }
495
496 static BOOL CTLContext_GetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
497                                    void *pvData, DWORD *pcbData)
498 {
499     PCONTEXT_PROPERTY_LIST properties =
500      Context_GetProperties(context, sizeof(CTL_CONTEXT));
501     BOOL ret;
502     CRYPT_DATA_BLOB blob;
503
504     TRACE("(%p, %d, %p, %p)\n", context, dwPropId, pvData, pcbData);
505
506     if (properties)
507         ret = ContextPropertyList_FindProperty(properties, dwPropId, &blob);
508     else
509         ret = FALSE;
510     if (ret)
511     {
512         if (!pvData)
513             *pcbData = blob.cbData;
514         else if (*pcbData < blob.cbData)
515         {
516             SetLastError(ERROR_MORE_DATA);
517             *pcbData = blob.cbData;
518             ret = FALSE;
519         }
520         else
521         {
522             memcpy(pvData, blob.pbData, blob.cbData);
523             *pcbData = blob.cbData;
524         }
525     }
526     else
527     {
528         /* Implicit properties */
529         switch (dwPropId)
530         {
531         case CERT_SHA1_HASH_PROP_ID:
532             ret = CTLContext_GetHashProp(context, dwPropId, CALG_SHA1,
533              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
534             break;
535         case CERT_MD5_HASH_PROP_ID:
536             ret = CTLContext_GetHashProp(context, dwPropId, CALG_MD5,
537              context->pbCtlEncoded, context->cbCtlEncoded, pvData, pcbData);
538             break;
539         default:
540             SetLastError(CRYPT_E_NOT_FOUND);
541         }
542     }
543     TRACE("returning %d\n", ret);
544     return ret;
545 }
546
547 BOOL WINAPI CertGetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
548  DWORD dwPropId, void *pvData, DWORD *pcbData)
549 {
550     BOOL ret;
551
552     TRACE("(%p, %d, %p, %p)\n", pCTLContext, dwPropId, pvData, pcbData);
553
554     switch (dwPropId)
555     {
556     case 0:
557     case CERT_CERT_PROP_ID:
558     case CERT_CRL_PROP_ID:
559     case CERT_CTL_PROP_ID:
560         SetLastError(E_INVALIDARG);
561         ret = FALSE;
562         break;
563     case CERT_ACCESS_STATE_PROP_ID:
564         if (!pvData)
565         {
566             *pcbData = sizeof(DWORD);
567             ret = TRUE;
568         }
569         else if (*pcbData < sizeof(DWORD))
570         {
571             SetLastError(ERROR_MORE_DATA);
572             *pcbData = sizeof(DWORD);
573             ret = FALSE;
574         }
575         else
576         {
577             if (pCTLContext->hCertStore)
578                 ret = CertGetStoreProperty(pCTLContext->hCertStore, dwPropId,
579                  pvData, pcbData);
580             else
581                 *(DWORD *)pvData = 0;
582             ret = TRUE;
583         }
584         break;
585     default:
586         ret = CTLContext_GetProperty(pCTLContext, dwPropId, pvData,
587          pcbData);
588     }
589     return ret;
590 }
591
592 static BOOL CTLContext_SetProperty(PCCTL_CONTEXT context, DWORD dwPropId,
593  DWORD dwFlags, const void *pvData)
594 {
595     PCONTEXT_PROPERTY_LIST properties =
596      Context_GetProperties(context, sizeof(CTL_CONTEXT));
597     BOOL ret;
598
599     TRACE("(%p, %d, %08x, %p)\n", context, dwPropId, dwFlags, pvData);
600
601     if (!properties)
602         ret = FALSE;
603     else if (!pvData)
604     {
605         ContextPropertyList_RemoveProperty(properties, dwPropId);
606         ret = TRUE;
607     }
608     else
609     {
610         switch (dwPropId)
611         {
612         case CERT_AUTO_ENROLL_PROP_ID:
613         case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */
614         case CERT_DESCRIPTION_PROP_ID:
615         case CERT_FRIENDLY_NAME_PROP_ID:
616         case CERT_HASH_PROP_ID:
617         case CERT_KEY_IDENTIFIER_PROP_ID:
618         case CERT_MD5_HASH_PROP_ID:
619         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
620         case CERT_PUBKEY_ALG_PARA_PROP_ID:
621         case CERT_PVK_FILE_PROP_ID:
622         case CERT_SIGNATURE_HASH_PROP_ID:
623         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
624         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
625         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
626         case CERT_ENROLLMENT_PROP_ID:
627         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
628         case CERT_RENEWAL_PROP_ID:
629         {
630             PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvData;
631
632             ret = ContextPropertyList_SetProperty(properties, dwPropId,
633              blob->pbData, blob->cbData);
634             break;
635         }
636         case CERT_DATE_STAMP_PROP_ID:
637             ret = ContextPropertyList_SetProperty(properties, dwPropId,
638              (const BYTE *)pvData, sizeof(FILETIME));
639             break;
640         default:
641             FIXME("%d: stub\n", dwPropId);
642             ret = FALSE;
643         }
644     }
645     TRACE("returning %d\n", ret);
646     return ret;
647 }
648
649 BOOL WINAPI CertSetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
650  DWORD dwPropId, DWORD dwFlags, const void *pvData)
651 {
652     BOOL ret;
653
654     TRACE("(%p, %d, %08x, %p)\n", pCTLContext, dwPropId, dwFlags, pvData);
655
656     /* Handle special cases for "read-only"/invalid prop IDs.  Windows just
657      * crashes on most of these, I'll be safer.
658      */
659     switch (dwPropId)
660     {
661     case 0:
662     case CERT_ACCESS_STATE_PROP_ID:
663     case CERT_CERT_PROP_ID:
664     case CERT_CRL_PROP_ID:
665     case CERT_CTL_PROP_ID:
666         SetLastError(E_INVALIDARG);
667         return FALSE;
668     }
669     ret = CTLContext_SetProperty(pCTLContext, dwPropId, dwFlags, pvData);
670     TRACE("returning %d\n", ret);
671     return ret;
672 }