1 /*
2 * Copyright 2004-2007 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 #include <stdarg.h>
19 #include "windef.h"
20 #include "winbase.h"
21 #include "wincrypt.h"
22 #include "wine/debug.h"
23 #include "wine/list.h"
24 #include "crypt32_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
27
28 typedef struct _WINE_STORE_LIST_ENTRY
29 {
30 PWINECRYPT_CERTSTORE store;
31 DWORD dwUpdateFlags;
32 DWORD dwPriority;
33 struct list entry;
34 } WINE_STORE_LIST_ENTRY, *PWINE_STORE_LIST_ENTRY;
35
36 typedef struct _WINE_COLLECTIONSTORE
37 {
38 WINECRYPT_CERTSTORE hdr;
39 CRITICAL_SECTION cs;
40 struct list stores;
41 } WINE_COLLECTIONSTORE, *PWINE_COLLECTIONSTORE;
42
43 static void WINAPI CRYPT_CollectionCloseStore(HCERTSTORE store, DWORD dwFlags)
44 {
45 PWINE_COLLECTIONSTORE cs = store;
46 PWINE_STORE_LIST_ENTRY entry, next;
47
48 TRACE("(%p, %08x)\n", store, dwFlags);
49
50 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY,
51 entry)
52 {
53 TRACE("closing %p\n", entry);
54 CertCloseStore(entry->store, dwFlags);
55 CryptMemFree(entry);
56 }
57 cs->cs.DebugInfo->Spare[0] = 0;
58 DeleteCriticalSection(&cs->cs);
59 CRYPT_FreeStore(store);
60 }
61
62 static void *CRYPT_CollectionCreateContextFromChild(PWINE_COLLECTIONSTORE store,
63 PWINE_STORE_LIST_ENTRY storeEntry, void *child, size_t contextSize,
64 BOOL addRef)
65 {
66 void *ret = Context_CreateLinkContext(contextSize, child,
67 sizeof(PWINE_STORE_LIST_ENTRY), addRef);
68
69 if (ret)
70 *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(ret, contextSize)
71 = storeEntry;
72
73 return ret;
74 }
75
76 static BOOL CRYPT_CollectionAddContext(PWINE_COLLECTIONSTORE store,
77 unsigned int contextFuncsOffset, void *context, void *toReplace, unsigned int contextSize,
78 void **pChildContext)
79 {
80 BOOL ret;
81 void *childContext = NULL;
82 PWINE_STORE_LIST_ENTRY storeEntry = NULL;
83
84 TRACE("(%p, %d, %p, %p, %d)\n", store, contextFuncsOffset, context,
85 toReplace, contextSize);
86
87 ret = FALSE;
88 if (toReplace)
89 {
90 void *existingLinked = Context_GetLinkedContext(toReplace, contextSize);
91 PCONTEXT_FUNCS contextFuncs;
92
93 storeEntry = *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(toReplace,
94 contextSize);
95 contextFuncs = (PCONTEXT_FUNCS)((LPBYTE)storeEntry->store +
96 contextFuncsOffset);
97 ret = contextFuncs->addContext(storeEntry->store, context,
98 existingLinked, (const void **)&childContext);
99 }
100 else
101 {
102 PWINE_STORE_LIST_ENTRY entry, next;
103
104 EnterCriticalSection(&store->cs);
105 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &store->stores,
106 WINE_STORE_LIST_ENTRY, entry)
107 {
108 if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)
109 {
110 PCONTEXT_FUNCS contextFuncs = (PCONTEXT_FUNCS)(
111 (LPBYTE)entry->store + contextFuncsOffset);
112
113 storeEntry = entry;
114 ret = contextFuncs->addContext(entry->store, context, NULL,
115 (const void **)&childContext);
116 break;
117 }
118 }
119 LeaveCriticalSection(&store->cs);
120 if (!storeEntry)
121 SetLastError(E_ACCESSDENIED);
122 }
123 *pChildContext = childContext;
124 return ret;
125 }
126
127 /* Advances a collection enumeration by one context, if possible, where
128 * advancing means:
129 * - calling the current store's enumeration function once, and returning
130 * the enumerated context if one is returned
131 * - moving to the next store if the current store has no more items, and
132 * recursively calling itself to get the next item.
133 * Returns NULL if the collection contains no more items or on error.
134 * Assumes the collection store's lock is held.
135 */
136 static void *CRYPT_CollectionAdvanceEnum(PWINE_COLLECTIONSTORE store,
137 PWINE_STORE_LIST_ENTRY storeEntry, const CONTEXT_FUNCS *contextFuncs,
138 PCWINE_CONTEXT_INTERFACE contextInterface, void *pPrev, size_t contextSize)
139 {
140 void *ret, *child;
141 struct list *storeNext = list_next(&store->stores, &storeEntry->entry);
142
143 TRACE("(%p, %p, %p)\n", store, storeEntry, pPrev);
144
145 if (pPrev)
146 {
147 /* Ref-counting funny business: "duplicate" (addref) the child, because
148 * the free(pPrev) below can cause the ref count to become negative.
149 */
150 child = Context_GetLinkedContext(pPrev, contextSize);
151 contextInterface->duplicate(child);
152 child = contextFuncs->enumContext(storeEntry->store, child);
153 contextInterface->free(pPrev);
154 pPrev = NULL;
155 }
156 else
157 child = contextFuncs->enumContext(storeEntry->store, NULL);
158 if (child)
159 ret = CRYPT_CollectionCreateContextFromChild(store, storeEntry, child,
160 contextSize, FALSE);
161 else
162 {
163 if (storeNext)
164 {
165 /* We always want the same function pointers (from certs, crls)
166 * in the next store, so use the same offset into the next store.
167 */
168 size_t offset = (const BYTE *)contextFuncs - (LPBYTE)storeEntry->store;
169 PWINE_STORE_LIST_ENTRY storeNextEntry =
170 LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, entry);
171 PCONTEXT_FUNCS storeNextContexts =
172 (PCONTEXT_FUNCS)((LPBYTE)storeNextEntry->store + offset);
173
174 ret = CRYPT_CollectionAdvanceEnum(store, storeNextEntry,
175 storeNextContexts, contextInterface, NULL, contextSize);
176 }
177 else
178 {
179 SetLastError(CRYPT_E_NOT_FOUND);
180 ret = NULL;
181 }
182 }
183 TRACE("returning %p\n", ret);
184 return ret;
185 }
186
187 static BOOL CRYPT_CollectionAddCert(PWINECRYPT_CERTSTORE store, void *cert,
188 void *toReplace, const void **ppStoreContext)
189 {
190 BOOL ret;
191 void *childContext = NULL;
192 PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
193
194 ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, certs),
195 cert, toReplace, sizeof(CERT_CONTEXT), &childContext);
196 if (ppStoreContext && childContext)
197 {
198 PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)
199 Context_GetExtra(childContext, sizeof(CERT_CONTEXT));
200 PCERT_CONTEXT context =
201 CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext,
202 sizeof(CERT_CONTEXT), TRUE);
203
204 if (context)
205 context->hCertStore = store;
206 *ppStoreContext = context;
207 }
208 CertFreeCertificateContext(childContext);
209 return ret;
210 }
211
212 static void *CRYPT_CollectionEnumCert(PWINECRYPT_CERTSTORE store, void *pPrev)
213 {
214 PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
215 void *ret;
216
217 TRACE("(%p, %p)\n", store, pPrev);
218
219 EnterCriticalSection(&cs->cs);
220 if (pPrev)
221 {
222 PWINE_STORE_LIST_ENTRY storeEntry =
223 *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev,
224 sizeof(CERT_CONTEXT));
225
226 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
227 &storeEntry->store->certs, pCertInterface, pPrev,
228 sizeof(CERT_CONTEXT));
229 }
230 else
231 {
232 if (!list_empty(&cs->stores))
233 {
234 PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next,
235 WINE_STORE_LIST_ENTRY, entry);
236
237 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
238 &storeEntry->store->certs, pCertInterface, NULL,
239 sizeof(CERT_CONTEXT));
240 }
241 else
242 {
243 SetLastError(CRYPT_E_NOT_FOUND);
244 ret = NULL;
245 }
246 }
247 LeaveCriticalSection(&cs->cs);
248 if (ret)
249 ((PCERT_CONTEXT)ret)->hCertStore = store;
250 TRACE("returning %p\n", ret);
251 return ret;
252 }
253
254 static BOOL CRYPT_CollectionDeleteCert(PWINECRYPT_CERTSTORE store,
255 void *pCertContext)
256 {
257 BOOL ret;
258 PCCERT_CONTEXT linked;
259
260 TRACE("(%p, %p)\n", store, pCertContext);
261
262 /* Deleting the linked context results in its ref count getting
263 * decreased, but the caller of this (CertDeleteCertificateFromStore) also
264 * decreases pCertContext's ref count, by calling
265 * CertFreeCertificateContext. Increase ref count of linked context to
266 * compensate.
267 */
268 linked = Context_GetLinkedContext(pCertContext, sizeof(CERT_CONTEXT));
269 CertDuplicateCertificateContext(linked);
270 ret = CertDeleteCertificateFromStore(linked);
271 return ret;
272 }
273
274 static BOOL CRYPT_CollectionAddCRL(PWINECRYPT_CERTSTORE store, void *crl,
275 void *toReplace, const void **ppStoreContext)
276 {
277 BOOL ret;
278 void *childContext = NULL;
279 PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
280
281 ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, crls),
282 crl, toReplace, sizeof(CRL_CONTEXT), &childContext);
283 if (ppStoreContext && childContext)
284 {
285 PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)
286 Context_GetExtra(childContext, sizeof(CRL_CONTEXT));
287 PCRL_CONTEXT context =
288 CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext,
289 sizeof(CRL_CONTEXT), TRUE);
290
291 if (context)
292 context->hCertStore = store;
293 *ppStoreContext = context;
294 }
295 CertFreeCRLContext(childContext);
296 return ret;
297 }
298
299 static void *CRYPT_CollectionEnumCRL(PWINECRYPT_CERTSTORE store, void *pPrev)
300 {
301 PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
302 void *ret;
303
304 TRACE("(%p, %p)\n", store, pPrev);
305
306 EnterCriticalSection(&cs->cs);
307 if (pPrev)
308 {
309 PWINE_STORE_LIST_ENTRY storeEntry =
310 *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev,
311 sizeof(CRL_CONTEXT));
312
313 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
314 &storeEntry->store->crls, pCRLInterface, pPrev, sizeof(CRL_CONTEXT));
315 }
316 else
317 {
318 if (!list_empty(&cs->stores))
319 {
320 PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next,
321 WINE_STORE_LIST_ENTRY, entry);
322
323 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
324 &storeEntry->store->crls, pCRLInterface, NULL,
325 sizeof(CRL_CONTEXT));
326 }
327 else
328 {
329 SetLastError(CRYPT_E_NOT_FOUND);
330 ret = NULL;
331 }
332 }
333 LeaveCriticalSection(&cs->cs);
334 if (ret)
335 ((PCRL_CONTEXT)ret)->hCertStore = store;
336 TRACE("returning %p\n", ret);
337 return ret;
338 }
339
340 static BOOL CRYPT_CollectionDeleteCRL(PWINECRYPT_CERTSTORE store,
341 void *pCrlContext)
342 {
343 BOOL ret;
344 PCCRL_CONTEXT linked;
345
346 TRACE("(%p, %p)\n", store, pCrlContext);
347
348 /* Deleting the linked context results in its ref count getting
349 * decreased, but the caller of this (CertDeleteCRLFromStore) also
350 * decreases pCrlContext's ref count, by calling CertFreeCRLContext.
351 * Increase ref count of linked context to compensate.
352 */
353 linked = Context_GetLinkedContext(pCrlContext, sizeof(CRL_CONTEXT));
354 CertDuplicateCRLContext(linked);
355 ret = CertDeleteCRLFromStore(linked);
356 return ret;
357 }
358
359 static BOOL CRYPT_CollectionAddCTL(PWINECRYPT_CERTSTORE store, void *ctl,
360 void *toReplace, const void **ppStoreContext)
361 {
362 BOOL ret;
363 void *childContext = NULL;
364 PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
365
366 ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, ctls),
367 ctl, toReplace, sizeof(CTL_CONTEXT), &childContext);
368 if (ppStoreContext && childContext)
369 {
370 PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)
371 Context_GetExtra(childContext, sizeof(CTL_CONTEXT));
372 PCTL_CONTEXT context =
373 CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext,
374 sizeof(CTL_CONTEXT), TRUE);
375
376 if (context)
377 context->hCertStore = store;
378 *ppStoreContext = context;
379 }
380 CertFreeCTLContext(childContext);
381 return ret;
382 }
383
384 static void *CRYPT_CollectionEnumCTL(PWINECRYPT_CERTSTORE store, void *pPrev)
385 {
386 PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
387 void *ret;
388
389 TRACE("(%p, %p)\n", store, pPrev);
390
391 EnterCriticalSection(&cs->cs);
392 if (pPrev)
393 {
394 PWINE_STORE_LIST_ENTRY storeEntry =
395 *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev,
396 sizeof(CTL_CONTEXT));
397
398 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
399 &storeEntry->store->ctls, pCTLInterface, pPrev, sizeof(CTL_CONTEXT));
400 }
401 else
402 {
403 if (!list_empty(&cs->stores))
404 {
405 PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next,
406 WINE_STORE_LIST_ENTRY, entry);
407
408 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
409 &storeEntry->store->ctls, pCTLInterface, NULL,
410 sizeof(CTL_CONTEXT));
411 }
412 else
413 {
414 SetLastError(CRYPT_E_NOT_FOUND);
415 ret = NULL;
416 }
417 }
418 LeaveCriticalSection(&cs->cs);
419 if (ret)
420 ((PCTL_CONTEXT)ret)->hCertStore = store;
421 TRACE("returning %p\n", ret);
422 return ret;
423 }
424
425 static BOOL CRYPT_CollectionDeleteCTL(PWINECRYPT_CERTSTORE store,
426 void *pCtlContext)
427 {
428 BOOL ret;
429 PCCTL_CONTEXT linked;
430
431 TRACE("(%p, %p)\n", store, pCtlContext);
432
433 /* Deleting the linked context results in its ref count getting
434 * decreased, but the caller of this (CertDeleteCTLFromStore) also
435 * decreases pCtlContext's ref count, by calling CertFreeCTLContext.
436 * Increase ref count of linked context to compensate.
437 */
438 linked = Context_GetLinkedContext(pCtlContext, sizeof(CTL_CONTEXT));
439 CertDuplicateCTLContext(linked);
440 ret = CertDeleteCTLFromStore(linked);
441 return ret;
442 }
443
444 static BOOL WINAPI CRYPT_CollectionControl(HCERTSTORE hCertStore, DWORD dwFlags,
445 DWORD dwCtrlType, void const *pvCtrlPara)
446 {
447 BOOL ret;
448 PWINE_COLLECTIONSTORE store = hCertStore;
449 PWINE_STORE_LIST_ENTRY entry;
450
451 TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType,
452 pvCtrlPara);
453
454 if (!store)
455 return TRUE;
456 if (store->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
457 {
458 SetLastError(E_INVALIDARG);
459 return FALSE;
460 }
461 if (store->hdr.type != StoreTypeCollection)
462 {
463 SetLastError(E_INVALIDARG);
464 return FALSE;
465 }
466
467 ret = TRUE;
468 EnterCriticalSection(&store->cs);
469 LIST_FOR_EACH_ENTRY(entry, &store->stores, WINE_STORE_LIST_ENTRY, entry)
470 {
471 if (entry->store->control)
472 {
473 ret = entry->store->control(entry->store, dwFlags, dwCtrlType,
474 pvCtrlPara);
475 if (!ret)
476 break;
477 }
478 }
479 LeaveCriticalSection(&store->cs);
480 return ret;
481 }
482
483 PWINECRYPT_CERTSTORE CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
484 DWORD dwFlags, const void *pvPara)
485 {
486 PWINE_COLLECTIONSTORE store;
487
488 if (dwFlags & CERT_STORE_DELETE_FLAG)
489 {
490 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
491 store = NULL;
492 }
493 else
494 {
495 store = CryptMemAlloc(sizeof(WINE_COLLECTIONSTORE));
496 if (store)
497 {
498 memset(store, 0, sizeof(WINE_COLLECTIONSTORE));
499 CRYPT_InitStore(&store->hdr, dwFlags, StoreTypeCollection);
500 store->hdr.closeStore = CRYPT_CollectionCloseStore;
501 store->hdr.certs.addContext = CRYPT_CollectionAddCert;
502 store->hdr.certs.enumContext = CRYPT_CollectionEnumCert;
503 store->hdr.certs.deleteContext = CRYPT_CollectionDeleteCert;
504 store->hdr.crls.addContext = CRYPT_CollectionAddCRL;
505 store->hdr.crls.enumContext = CRYPT_CollectionEnumCRL;
506 store->hdr.crls.deleteContext = CRYPT_CollectionDeleteCRL;
507 store->hdr.ctls.addContext = CRYPT_CollectionAddCTL;
508 store->hdr.ctls.enumContext = CRYPT_CollectionEnumCTL;
509 store->hdr.ctls.deleteContext = CRYPT_CollectionDeleteCTL;
510 store->hdr.control = CRYPT_CollectionControl;
511 InitializeCriticalSection(&store->cs);
512 store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs");
513 list_init(&store->stores);
514 }
515 }
516 return (PWINECRYPT_CERTSTORE)store;
517 }
518
519 BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore,
520 HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority)
521 {
522 PWINE_COLLECTIONSTORE collection = hCollectionStore;
523 WINECRYPT_CERTSTORE *sibling = hSiblingStore;
524 PWINE_STORE_LIST_ENTRY entry;
525 BOOL ret;
526
527 TRACE("(%p, %p, %08x, %d)\n", hCollectionStore, hSiblingStore,
528 dwUpdateFlags, dwPriority);
529
530 if (!collection || !sibling)
531 return TRUE;
532 if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
533 {
534 SetLastError(E_INVALIDARG);
535 return FALSE;
536 }
537 if (collection->hdr.type != StoreTypeCollection)
538 {
539 SetLastError(E_INVALIDARG);
540 return FALSE;
541 }
542 if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
543 {
544 SetLastError(E_INVALIDARG);
545 return FALSE;
546 }
547
548 entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY));
549 if (entry)
550 {
551 InterlockedIncrement(&sibling->ref);
552 TRACE("sibling %p's ref count is %d\n", sibling, sibling->ref);
553 entry->store = sibling;
554 entry->dwUpdateFlags = dwUpdateFlags;
555 entry->dwPriority = dwPriority;
556 list_init(&entry->entry);
557 TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority);
558 EnterCriticalSection(&collection->cs);
559 if (dwPriority)
560 {
561 PWINE_STORE_LIST_ENTRY cursor;
562 BOOL added = FALSE;
563
564 LIST_FOR_EACH_ENTRY(cursor, &collection->stores,
565 WINE_STORE_LIST_ENTRY, entry)
566 {
567 if (cursor->dwPriority < dwPriority)
568 {
569 list_add_before(&cursor->entry, &entry->entry);
570 added = TRUE;
571 break;
572 }
573 }
574 if (!added)
575 list_add_tail(&collection->stores, &entry->entry);
576 }
577 else
578 list_add_tail(&collection->stores, &entry->entry);
579 LeaveCriticalSection(&collection->cs);
580 ret = TRUE;
581 }
582 else
583 ret = FALSE;
584 return ret;
585 }
586
587 void WINAPI CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore,
588 HCERTSTORE hSiblingStore)
589 {
590 PWINE_COLLECTIONSTORE collection = hCollectionStore;
591 WINECRYPT_CERTSTORE *sibling = hSiblingStore;
592 PWINE_STORE_LIST_ENTRY store, next;
593
594 TRACE("(%p, %p)\n", hCollectionStore, hSiblingStore);
595
596 if (!collection || !sibling)
597 return;
598 if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
599 {
600 SetLastError(E_INVALIDARG);
601 return;
602 }
603 if (collection->hdr.type != StoreTypeCollection)
604 return;
605 if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
606 {
607 SetLastError(E_INVALIDARG);
608 return;
609 }
610 EnterCriticalSection(&collection->cs);
611 LIST_FOR_EACH_ENTRY_SAFE(store, next, &collection->stores,
612 WINE_STORE_LIST_ENTRY, entry)
613 {
614 if (store->store == sibling)
615 {
616 list_remove(&store->entry);
617 CertCloseStore(store->store, 0);
618 CryptMemFree(store);
619 break;
620 }
621 }
622 LeaveCriticalSection(&collection->cs);
623 }
624
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.