crypt32/tests: Fix a test failure on Win9x.
[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, PCONTEXT_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 = (LPBYTE)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
259     TRACE("(%p, %p)\n", store, pCertContext);
260
261     ret = CertDeleteCertificateFromStore(
262      Context_GetLinkedContext(pCertContext, sizeof(CERT_CONTEXT)));
263     return ret;
264 }
265
266 static BOOL CRYPT_CollectionAddCRL(PWINECRYPT_CERTSTORE store, void *crl,
267  void *toReplace, const void **ppStoreContext)
268 {
269     BOOL ret;
270     void *childContext = NULL;
271     PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
272
273     ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, crls),
274      crl, toReplace, sizeof(CRL_CONTEXT), &childContext);
275     if (ppStoreContext && childContext)
276     {
277         PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)
278          Context_GetExtra(childContext, sizeof(CRL_CONTEXT));
279         PCRL_CONTEXT context =
280          CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext,
281          sizeof(CRL_CONTEXT), TRUE);
282
283         if (context)
284             context->hCertStore = store;
285         *ppStoreContext = context;
286     }
287     CertFreeCRLContext(childContext);
288     return ret;
289 }
290
291 static void *CRYPT_CollectionEnumCRL(PWINECRYPT_CERTSTORE store, void *pPrev)
292 {
293     PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
294     void *ret;
295
296     TRACE("(%p, %p)\n", store, pPrev);
297
298     EnterCriticalSection(&cs->cs);
299     if (pPrev)
300     {
301         PWINE_STORE_LIST_ENTRY storeEntry =
302          *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev,
303          sizeof(CRL_CONTEXT));
304
305         ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
306          &storeEntry->store->crls, pCRLInterface, pPrev, sizeof(CRL_CONTEXT));
307     }
308     else
309     {
310         if (!list_empty(&cs->stores))
311         {
312             PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next,
313              WINE_STORE_LIST_ENTRY, entry);
314
315             ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
316              &storeEntry->store->crls, pCRLInterface, NULL,
317              sizeof(CRL_CONTEXT));
318         }
319         else
320         {
321             SetLastError(CRYPT_E_NOT_FOUND);
322             ret = NULL;
323         }
324     }
325     LeaveCriticalSection(&cs->cs);
326     if (ret)
327         ((PCRL_CONTEXT)ret)->hCertStore = store;
328     TRACE("returning %p\n", ret);
329     return ret;
330 }
331
332 static BOOL CRYPT_CollectionDeleteCRL(PWINECRYPT_CERTSTORE store,
333  void *pCrlContext)
334 {
335     BOOL ret;
336
337     TRACE("(%p, %p)\n", store, pCrlContext);
338
339     ret = CertDeleteCRLFromStore(
340      Context_GetLinkedContext(pCrlContext, sizeof(CRL_CONTEXT)));
341     return ret;
342 }
343
344 static BOOL CRYPT_CollectionAddCTL(PWINECRYPT_CERTSTORE store, void *ctl,
345  void *toReplace, const void **ppStoreContext)
346 {
347     BOOL ret;
348     void *childContext = NULL;
349     PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
350
351     ret = CRYPT_CollectionAddContext(cs, offsetof(WINECRYPT_CERTSTORE, ctls),
352      ctl, toReplace, sizeof(CTL_CONTEXT), &childContext);
353     if (ppStoreContext && childContext)
354     {
355         PWINE_STORE_LIST_ENTRY storeEntry = *(PWINE_STORE_LIST_ENTRY *)
356          Context_GetExtra(childContext, sizeof(CTL_CONTEXT));
357         PCTL_CONTEXT context =
358          CRYPT_CollectionCreateContextFromChild(cs, storeEntry, childContext,
359          sizeof(CTL_CONTEXT), TRUE);
360
361         if (context)
362             context->hCertStore = store;
363         *ppStoreContext = context;
364     }
365     CertFreeCTLContext(childContext);
366     return ret;
367 }
368
369 static void *CRYPT_CollectionEnumCTL(PWINECRYPT_CERTSTORE store, void *pPrev)
370 {
371     PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
372     void *ret;
373
374     TRACE("(%p, %p)\n", store, pPrev);
375
376     EnterCriticalSection(&cs->cs);
377     if (pPrev)
378     {
379         PWINE_STORE_LIST_ENTRY storeEntry =
380          *(PWINE_STORE_LIST_ENTRY *)Context_GetExtra(pPrev,
381          sizeof(CTL_CONTEXT));
382
383         ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
384          &storeEntry->store->ctls, pCTLInterface, pPrev, sizeof(CTL_CONTEXT));
385     }
386     else
387     {
388         if (!list_empty(&cs->stores))
389         {
390             PWINE_STORE_LIST_ENTRY storeEntry = LIST_ENTRY(cs->stores.next,
391              WINE_STORE_LIST_ENTRY, entry);
392
393             ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
394              &storeEntry->store->ctls, pCTLInterface, NULL,
395              sizeof(CTL_CONTEXT));
396         }
397         else
398         {
399             SetLastError(CRYPT_E_NOT_FOUND);
400             ret = NULL;
401         }
402     }
403     LeaveCriticalSection(&cs->cs);
404     if (ret)
405         ((PCTL_CONTEXT)ret)->hCertStore = store;
406     TRACE("returning %p\n", ret);
407     return ret;
408 }
409
410 static BOOL CRYPT_CollectionDeleteCTL(PWINECRYPT_CERTSTORE store,
411  void *pCtlContext)
412 {
413     BOOL ret;
414
415     TRACE("(%p, %p)\n", store, pCtlContext);
416
417     ret = CertDeleteCTLFromStore(
418      Context_GetLinkedContext(pCtlContext, sizeof(CTL_CONTEXT)));
419     return ret;
420 }
421
422 PWINECRYPT_CERTSTORE CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
423  DWORD dwFlags, const void *pvPara)
424 {
425     PWINE_COLLECTIONSTORE store;
426
427     if (dwFlags & CERT_STORE_DELETE_FLAG)
428     {
429         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
430         store = NULL;
431     }
432     else
433     {
434         store = CryptMemAlloc(sizeof(WINE_COLLECTIONSTORE));
435         if (store)
436         {
437             memset(store, 0, sizeof(WINE_COLLECTIONSTORE));
438             CRYPT_InitStore(&store->hdr, dwFlags, StoreTypeCollection);
439             store->hdr.closeStore          = CRYPT_CollectionCloseStore;
440             store->hdr.certs.addContext    = CRYPT_CollectionAddCert;
441             store->hdr.certs.enumContext   = CRYPT_CollectionEnumCert;
442             store->hdr.certs.deleteContext = CRYPT_CollectionDeleteCert;
443             store->hdr.crls.addContext     = CRYPT_CollectionAddCRL;
444             store->hdr.crls.enumContext    = CRYPT_CollectionEnumCRL;
445             store->hdr.crls.deleteContext  = CRYPT_CollectionDeleteCRL;
446             store->hdr.ctls.addContext     = CRYPT_CollectionAddCTL;
447             store->hdr.ctls.enumContext    = CRYPT_CollectionEnumCTL;
448             store->hdr.ctls.deleteContext  = CRYPT_CollectionDeleteCTL;
449             InitializeCriticalSection(&store->cs);
450             store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs");
451             list_init(&store->stores);
452         }
453     }
454     return (PWINECRYPT_CERTSTORE)store;
455 }
456
457 BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore,
458  HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority)
459 {
460     PWINE_COLLECTIONSTORE collection = hCollectionStore;
461     WINECRYPT_CERTSTORE *sibling = hSiblingStore;
462     PWINE_STORE_LIST_ENTRY entry;
463     BOOL ret;
464
465     TRACE("(%p, %p, %08x, %d)\n", hCollectionStore, hSiblingStore,
466      dwUpdateFlags, dwPriority);
467
468     if (!collection || !sibling)
469         return TRUE;
470     if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
471     {
472         SetLastError(E_INVALIDARG);
473         return FALSE;
474     }
475     if (collection->hdr.type != StoreTypeCollection)
476     {
477         SetLastError(E_INVALIDARG);
478         return FALSE;
479     }
480     if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
481     {
482         SetLastError(E_INVALIDARG);
483         return FALSE;
484     }
485
486     entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY));
487     if (entry)
488     {
489         InterlockedIncrement(&sibling->ref);
490         TRACE("sibling %p's ref count is %d\n", sibling, sibling->ref);
491         entry->store = sibling;
492         entry->dwUpdateFlags = dwUpdateFlags;
493         entry->dwPriority = dwPriority;
494         list_init(&entry->entry);
495         TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority);
496         EnterCriticalSection(&collection->cs);
497         if (dwPriority)
498         {
499             PWINE_STORE_LIST_ENTRY cursor;
500             BOOL added = FALSE;
501
502             LIST_FOR_EACH_ENTRY(cursor, &collection->stores,
503              WINE_STORE_LIST_ENTRY, entry)
504             {
505                 if (cursor->dwPriority < dwPriority)
506                 {
507                     list_add_before(&cursor->entry, &entry->entry);
508                     added = TRUE;
509                     break;
510                 }
511             }
512             if (!added)
513                 list_add_tail(&collection->stores, &entry->entry);
514         }
515         else
516             list_add_tail(&collection->stores, &entry->entry);
517         LeaveCriticalSection(&collection->cs);
518         ret = TRUE;
519     }
520     else
521         ret = FALSE;
522     return ret;
523 }
524
525 void WINAPI CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore,
526  HCERTSTORE hSiblingStore)
527 {
528     PWINE_COLLECTIONSTORE collection = hCollectionStore;
529     WINECRYPT_CERTSTORE *sibling = hSiblingStore;
530     PWINE_STORE_LIST_ENTRY store, next;
531
532     TRACE("(%p, %p)\n", hCollectionStore, hSiblingStore);
533
534     if (!collection || !sibling)
535         return;
536     if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
537     {
538         SetLastError(E_INVALIDARG);
539         return;
540     }
541     if (collection->hdr.type != StoreTypeCollection)
542         return;
543     if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
544     {
545         SetLastError(E_INVALIDARG);
546         return;
547     }
548     EnterCriticalSection(&collection->cs);
549     LIST_FOR_EACH_ENTRY_SAFE(store, next, &collection->stores,
550      WINE_STORE_LIST_ENTRY, entry)
551     {
552         if (store->store == sibling)
553         {
554             list_remove(&store->entry);
555             CertCloseStore(store->store, 0);
556             CryptMemFree(store);
557             break;
558         }
559     }
560     LeaveCriticalSection(&collection->cs);
561 }