mapi32: Fully merge the IMAPIProp ifaces into IPropData.
[wine] / dlls / crypt32 / collectionstore.c
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 }