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