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
Collection_addref(WINECRYPT_CERTSTORE * store)42 static void Collection_addref(WINECRYPT_CERTSTORE *store)
43 {
44 LONG ref = InterlockedIncrement(&store->ref);
45 TRACE("ref = %d\n", ref);
46 }
47
Collection_release(WINECRYPT_CERTSTORE * store,DWORD flags)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
Collection_releaseContext(WINECRYPT_CERTSTORE * store,context_t * context)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
CRYPT_CollectionCreateContextFromChild(WINE_COLLECTIONSTORE * store,WINE_STORE_LIST_ENTRY * storeEntry,context_t * child)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
CRYPT_CollectionAddContext(WINE_COLLECTIONSTORE * store,unsigned int contextFuncsOffset,context_t * context,context_t * toReplace,context_t ** pChildContext)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 */
CRYPT_CollectionAdvanceEnum(WINE_COLLECTIONSTORE * store,WINE_STORE_LIST_ENTRY * storeEntry,const CONTEXT_FUNCS * contextFuncs,context_t * prev)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
Collection_addCert(WINECRYPT_CERTSTORE * store,context_t * cert,context_t * toReplace,context_t ** ppStoreContext,BOOL use_link)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
Collection_enumCert(WINECRYPT_CERTSTORE * store,context_t * prev)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
Collection_deleteCert(WINECRYPT_CERTSTORE * store,context_t * context)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
Collection_addCRL(WINECRYPT_CERTSTORE * store,context_t * crl,context_t * toReplace,context_t ** ppStoreContext,BOOL use_link)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
Collection_enumCRL(WINECRYPT_CERTSTORE * store,context_t * prev)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
Collection_deleteCRL(WINECRYPT_CERTSTORE * store,context_t * context)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
Collection_addCTL(WINECRYPT_CERTSTORE * store,context_t * ctl,context_t * toReplace,context_t ** ppStoreContext,BOOL use_link)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
Collection_enumCTL(WINECRYPT_CERTSTORE * store,context_t * prev)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
Collection_deleteCTL(WINECRYPT_CERTSTORE * store,context_t * context)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
Collection_control(WINECRYPT_CERTSTORE * cert_store,DWORD dwFlags,DWORD dwCtrlType,void const * pvCtrlPara)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
CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,DWORD dwFlags,const void * pvPara)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
CertAddStoreToCollection(HCERTSTORE hCollectionStore,HCERTSTORE hSiblingStore,DWORD dwUpdateFlags,DWORD dwPriority)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
CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore,HCERTSTORE hSiblingStore)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