source: trunk/src/crypt32/ctl.c@ 21354

Last change on this file since 21354 was 21354, checked in by rlwalsh, 16 years ago

eliminate VACPP warning & info msgs - see Ticket #1

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