msxml3: Added partial implementation of ISAXXMLReader_parse.
[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 = (PWINE_COLLECTIONSTORE)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((HCERTSTORE)entry->store, dwFlags);
55         CryptMemFree(entry);
56     }
57     cs->cs.DebugInfo->Spare[0] = 0;
58     DeleteCriticalSection(&cs->cs);
59     CRYPT_FreeStore((PWINECRYPT_CERTSTORE)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, 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((PCCERT_CONTEXT)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((PCCERT_CONTEXT)
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((PCCRL_CONTEXT)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((PCCRL_CONTEXT)
340      Context_GetLinkedContext(pCrlContext, sizeof(CRL_CONTEXT)));
341     return ret;
342 }
343
344 PWINECRYPT_CERTSTORE CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
345  DWORD dwFlags, const void *pvPara)
346 {
347     PWINE_COLLECTIONSTORE store;
348
349     if (dwFlags & CERT_STORE_DELETE_FLAG)
350     {
351         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
352         store = NULL;
353     }
354     else
355     {
356         store = CryptMemAlloc(sizeof(WINE_COLLECTIONSTORE));
357         if (store)
358         {
359             memset(store, 0, sizeof(WINE_COLLECTIONSTORE));
360             CRYPT_InitStore(&store->hdr, dwFlags, StoreTypeCollection);
361             store->hdr.closeStore          = CRYPT_CollectionCloseStore;
362             store->hdr.certs.addContext    = CRYPT_CollectionAddCert;
363             store->hdr.certs.enumContext   = CRYPT_CollectionEnumCert;
364             store->hdr.certs.deleteContext = CRYPT_CollectionDeleteCert;
365             store->hdr.crls.addContext     = CRYPT_CollectionAddCRL;
366             store->hdr.crls.enumContext    = CRYPT_CollectionEnumCRL;
367             store->hdr.crls.deleteContext  = CRYPT_CollectionDeleteCRL;
368             InitializeCriticalSection(&store->cs);
369             store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs");
370             list_init(&store->stores);
371         }
372     }
373     return (PWINECRYPT_CERTSTORE)store;
374 }
375
376 BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore,
377  HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority)
378 {
379     PWINE_COLLECTIONSTORE collection = (PWINE_COLLECTIONSTORE)hCollectionStore;
380     WINECRYPT_CERTSTORE *sibling = (WINECRYPT_CERTSTORE *)hSiblingStore;
381     PWINE_STORE_LIST_ENTRY entry;
382     BOOL ret;
383
384     TRACE("(%p, %p, %08x, %d)\n", hCollectionStore, hSiblingStore,
385      dwUpdateFlags, dwPriority);
386
387     if (!collection || !sibling)
388         return TRUE;
389     if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
390     {
391         SetLastError(E_INVALIDARG);
392         return FALSE;
393     }
394     if (collection->hdr.type != StoreTypeCollection)
395     {
396         SetLastError(E_INVALIDARG);
397         return FALSE;
398     }
399     if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
400     {
401         SetLastError(E_INVALIDARG);
402         return FALSE;
403     }
404
405     entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY));
406     if (entry)
407     {
408         InterlockedIncrement(&sibling->ref);
409         TRACE("sibling %p's ref count is %d\n", sibling, sibling->ref);
410         entry->store = sibling;
411         entry->dwUpdateFlags = dwUpdateFlags;
412         entry->dwPriority = dwPriority;
413         list_init(&entry->entry);
414         TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority);
415         EnterCriticalSection(&collection->cs);
416         if (dwPriority)
417         {
418             PWINE_STORE_LIST_ENTRY cursor;
419             BOOL added = FALSE;
420
421             LIST_FOR_EACH_ENTRY(cursor, &collection->stores,
422              WINE_STORE_LIST_ENTRY, entry)
423             {
424                 if (cursor->dwPriority < dwPriority)
425                 {
426                     list_add_before(&cursor->entry, &entry->entry);
427                     added = TRUE;
428                     break;
429                 }
430             }
431             if (!added)
432                 list_add_tail(&collection->stores, &entry->entry);
433         }
434         else
435             list_add_tail(&collection->stores, &entry->entry);
436         LeaveCriticalSection(&collection->cs);
437         ret = TRUE;
438     }
439     else
440         ret = FALSE;
441     return ret;
442 }
443
444 void WINAPI CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore,
445  HCERTSTORE hSiblingStore)
446 {
447     PWINE_COLLECTIONSTORE collection = (PWINE_COLLECTIONSTORE)hCollectionStore;
448     WINECRYPT_CERTSTORE *sibling = (WINECRYPT_CERTSTORE *)hSiblingStore;
449     PWINE_STORE_LIST_ENTRY store, next;
450
451     TRACE("(%p, %p)\n", hCollectionStore, hSiblingStore);
452
453     if (!collection || !sibling)
454         return;
455     if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
456     {
457         SetLastError(E_INVALIDARG);
458         return;
459     }
460     if (collection->hdr.type != StoreTypeCollection)
461         return;
462     if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
463     {
464         SetLastError(E_INVALIDARG);
465         return;
466     }
467     EnterCriticalSection(&collection->cs);
468     LIST_FOR_EACH_ENTRY_SAFE(store, next, &collection->stores,
469      WINE_STORE_LIST_ENTRY, entry)
470     {
471         if (store->store == sibling)
472         {
473             list_remove(&store->entry);
474             CertCloseStore(store->store, 0);
475             CryptMemFree(store);
476             break;
477         }
478     }
479     LeaveCriticalSection(&collection->cs);
480 }