secur32: Update ntlm_auth version detection to detect new samba4 version numbers.
[wine] / dlls / crypt32 / store.c
1 /*
2  * Copyright 2002 Mike McCormack for CodeWeavers
3  * Copyright 2004-2006 Juan Lang
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * FIXME:
20  * - As you can see in the stubs below, support for CRLs and CTLs is missing.
21  *   Mostly this should be copy-paste work, and some code (e.g. extended
22  *   properties) could be shared between them.
23  * - The concept of physical stores and locations isn't implemented.  (This
24  *   doesn't mean registry stores et al aren't implemented.  See the PSDK for
25  *   registering and enumerating physical stores and locations.)
26  * - Many flags, options and whatnot are unimplemented.
27  */
28
29 #include <assert.h>
30 #include <stdarg.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winnls.h"
34 #include "winreg.h"
35 #include "winuser.h"
36 #include "wincrypt.h"
37 #include "wine/debug.h"
38 #include "wine/list.h"
39 #include "excpt.h"
40 #include "wine/exception.h"
41 #include "crypt32_private.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
44
45 #define WINE_CRYPTCERTSTORE_MAGIC 0x74726563
46
47 /* Some typedefs that make it easier to abstract which type of context we're
48  * working with.
49  */
50 typedef const void *(WINAPI *CreateContextFunc)(DWORD dwCertEncodingType,
51  const BYTE *pbCertEncoded, DWORD cbCertEncoded);
52 typedef BOOL (WINAPI *AddContextToStoreFunc)(HCERTSTORE hCertStore,
53  const void *context, DWORD dwAddDisposition, const void **ppStoreContext);
54 typedef BOOL (WINAPI *AddEncodedContextToStoreFunc)(HCERTSTORE hCertStore,
55  DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded,
56  DWORD dwAddDisposition, const void **ppContext);
57 typedef const void *(WINAPI *EnumContextsInStoreFunc)(HCERTSTORE hCertStore,
58  const void *pPrevContext);
59 typedef BOOL (WINAPI *GetContextPropertyFunc)(const void *context,
60  DWORD dwPropID, void *pvData, DWORD *pcbData);
61 typedef BOOL (WINAPI *SetContextPropertyFunc)(const void *context,
62  DWORD dwPropID, DWORD dwFlags, const void *pvData);
63 typedef BOOL (WINAPI *SerializeElementFunc)(const void *context, DWORD dwFlags,
64  BYTE *pbElement, DWORD *pcbElement);
65 typedef BOOL (WINAPI *FreeContextFunc)(const void *context);
66 typedef BOOL (WINAPI *DeleteContextFunc)(const void *context);
67
68 /* An abstract context (certificate, CRL, or CTL) interface */
69 typedef struct _WINE_CONTEXT_INTERFACE
70 {
71     CreateContextFunc            create;
72     AddContextToStoreFunc        addContextToStore;
73     AddEncodedContextToStoreFunc addEncodedToStore;
74     EnumContextsInStoreFunc      enumContextsInStore;
75     GetContextPropertyFunc       getProp;
76     SetContextPropertyFunc       setProp;
77     SerializeElementFunc         serialize;
78     FreeContextFunc              free;
79     DeleteContextFunc            deleteFromStore;
80 } WINE_CONTEXT_INTERFACE, *PWINE_CONTEXT_INTERFACE;
81
82 static const WINE_CONTEXT_INTERFACE gCertInterface = {
83     (CreateContextFunc)CertCreateCertificateContext,
84     (AddContextToStoreFunc)CertAddCertificateContextToStore,
85     (AddEncodedContextToStoreFunc)CertAddEncodedCertificateToStore,
86     (EnumContextsInStoreFunc)CertEnumCertificatesInStore,
87     (GetContextPropertyFunc)CertGetCertificateContextProperty,
88     (SetContextPropertyFunc)CertSetCertificateContextProperty,
89     (SerializeElementFunc)CertSerializeCertificateStoreElement,
90     (FreeContextFunc)CertFreeCertificateContext,
91     (DeleteContextFunc)CertDeleteCertificateFromStore,
92 };
93
94 static const WINE_CONTEXT_INTERFACE gCRLInterface = {
95     (CreateContextFunc)CertCreateCRLContext,
96     (AddContextToStoreFunc)CertAddCRLContextToStore,
97     (AddEncodedContextToStoreFunc)CertAddEncodedCRLToStore,
98     (EnumContextsInStoreFunc)CertEnumCRLsInStore,
99     (GetContextPropertyFunc)CertGetCRLContextProperty,
100     (SetContextPropertyFunc)CertSetCRLContextProperty,
101     (SerializeElementFunc)CertSerializeCRLStoreElement,
102     (FreeContextFunc)CertFreeCRLContext,
103     (DeleteContextFunc)CertDeleteCRLFromStore,
104 };
105
106 static const WINE_CONTEXT_INTERFACE gCTLInterface = {
107     (CreateContextFunc)CertCreateCTLContext,
108     (AddContextToStoreFunc)CertAddCTLContextToStore,
109     (AddEncodedContextToStoreFunc)CertAddEncodedCTLToStore,
110     (EnumContextsInStoreFunc)CertEnumCTLsInStore,
111     (GetContextPropertyFunc)CertGetCTLContextProperty,
112     (SetContextPropertyFunc)CertSetCTLContextProperty,
113     (SerializeElementFunc)CertSerializeCTLStoreElement,
114     (FreeContextFunc)CertFreeCTLContext,
115     (DeleteContextFunc)CertDeleteCTLFromStore,
116 };
117
118 struct WINE_CRYPTCERTSTORE;
119
120 typedef struct WINE_CRYPTCERTSTORE * (*StoreOpenFunc)(HCRYPTPROV hCryptProv,
121  DWORD dwFlags, const void *pvPara);
122
123 struct _WINE_CERT_CONTEXT;
124
125 /* Called to enumerate the next certificate in a store. */
126 typedef struct _WINE_CERT_CONTEXT * (*EnumCertFunc)
127  (struct WINE_CRYPTCERTSTORE *store, struct _WINE_CERT_CONTEXT *pPrev);
128
129 /* Called to add a new certificate context to a store.  If ppStoreContext is
130  * not NULL, the added context should be returned in *ppStoreContext.
131  */
132 typedef BOOL (*AddCertFunc)(struct WINE_CRYPTCERTSTORE *store,
133  struct _WINE_CERT_CONTEXT *context, DWORD dwAddDisposition,
134  PCCERT_CONTEXT *ppStoreContext);
135
136 typedef enum _CertStoreType {
137     StoreTypeMem,
138     StoreTypeCollection,
139     StoreTypeProvider,
140 } CertStoreType;
141
142 /* A cert store is polymorphic through the use of function pointers.  A type
143  * is still needed to distinguish collection stores from other types.
144  * On the function pointers:
145  * - closeStore is called when the store's ref count becomes 0
146  * - control is optional, but should be implemented by any store that supports
147  *   persistence
148  */
149 typedef struct WINE_CRYPTCERTSTORE
150 {
151     DWORD                           dwMagic;
152     LONG                            ref;
153     DWORD                           dwOpenFlags;
154     HCRYPTPROV                      cryptProv;
155     CertStoreType                   type;
156     PFN_CERT_STORE_PROV_CLOSE       closeStore;
157     AddCertFunc                     addCert;
158     EnumCertFunc                    enumCert;
159     PFN_CERT_STORE_PROV_DELETE_CERT deleteCert;
160     PFN_CERT_STORE_PROV_CONTROL     control;    /* optional */
161 } WINECRYPT_CERTSTORE, *PWINECRYPT_CERTSTORE;
162
163 typedef enum _ContextType {
164     ContextTypeData,
165     ContextTypeLink,
166 } ContextType;
167
168 /* A certificate context.  This is the base type, and the two real types
169  * (data and link) derive from it.  Each one can be cast to a PCCERT_CONTEXT.
170  */
171 typedef struct _WINE_CERT_CONTEXT
172 {
173     CERT_CONTEXT cert;
174     LONG         ref;
175     ContextType  type;
176 } WINE_CERT_CONTEXT, *PWINE_CERT_CONTEXT;
177 typedef const struct _WINE_CERT_CONTEXT PCWINE_CERT_CONTEXT;
178
179 typedef struct _WINE_CERT_CONTEXT_DATA
180 {
181     CERT_CONTEXT           cert;
182     LONG                   ref;
183     ContextType            type; /* always ContextTypeData */
184     PCONTEXT_PROPERTY_LIST properties;
185 } WINE_CERT_CONTEXT_DATA, *PWINE_CERT_CONTEXT_DATA;
186 typedef const struct _WINE_CERT_CONTEXT_DATA PCWINE_CERT_CONTEXT_DATA;
187
188 typedef struct _WINE_CERT_CONTEXT_LINK
189 {
190     CERT_CONTEXT       cert;
191     LONG               ref;
192     ContextType        type; /* always ContextTypeLink */
193     PWINE_CERT_CONTEXT linked;
194 } WINE_CERT_CONTEXT_LINK, *PWINE_CERT_CONTEXT_LINK;
195 typedef const struct _WINE_CERT_CONTEXT_LINK PCWINE_CERT_CONTEXT_LINK;
196
197 /* A mem store has a list of these.  They're also returned by the mem store
198  * during enumeration.
199  */
200 typedef struct _WINE_CERT_LIST_ENTRY
201 {
202     WINE_CERT_CONTEXT_LINK cert;
203     struct list entry;
204 } WINE_CERT_LIST_ENTRY, *PWINE_CERT_LIST_ENTRY;
205
206 typedef struct _WINE_MEMSTORE
207 {
208     WINECRYPT_CERTSTORE hdr;
209     CRITICAL_SECTION    cs;
210     struct list         certs;
211 } WINE_MEMSTORE, *PWINE_MEMSTORE;
212
213 typedef struct _WINE_HASH_TO_DELETE
214 {
215     BYTE        hash[20];
216     struct list entry;
217 } WINE_HASH_TO_DELETE, *PWINE_HASH_TO_DELETE;
218
219 typedef struct _WINE_REGSTOREINFO
220 {
221     DWORD                dwOpenFlags;
222     HCRYPTPROV           cryptProv;
223     PWINECRYPT_CERTSTORE memStore;
224     HKEY                 key;
225     BOOL                 dirty;
226     CRITICAL_SECTION     cs;
227     struct list          certsToDelete;
228 } WINE_REGSTOREINFO, *PWINE_REGSTOREINFO;
229
230 typedef struct _WINE_STORE_LIST_ENTRY
231 {
232     PWINECRYPT_CERTSTORE store;
233     DWORD                dwUpdateFlags;
234     DWORD                dwPriority;
235     struct list          entry;
236 } WINE_STORE_LIST_ENTRY, *PWINE_STORE_LIST_ENTRY;
237
238 /* Returned by a collection store during enumeration.
239  * Note: relies on the list entry being valid after use, which a number of
240  * conditions might make untrue (reentrancy, closing a collection store before
241  * continuing an enumeration on it, ...).  The tests seem to indicate this
242  * sort of unsafety is okay, since Windows isn't well-behaved in these
243  * scenarios either.
244  */
245 typedef struct _WINE_COLLECTION_CERT_CONTEXT
246 {
247     WINE_CERT_CONTEXT_LINK  cert;
248     PWINE_STORE_LIST_ENTRY storeEntry;
249 } WINE_COLLECTION_CERT_CONTEXT, *PWINE_COLLECTION_CERT_CONTEXT;
250
251 typedef struct _WINE_COLLECTIONSTORE
252 {
253     WINECRYPT_CERTSTORE hdr;
254     CRITICAL_SECTION    cs;
255     struct list         stores;
256 } WINE_COLLECTIONSTORE, *PWINE_COLLECTIONSTORE;
257
258 typedef struct _WINE_PROVIDERSTORE
259 {
260     WINECRYPT_CERTSTORE             hdr;
261     DWORD                           dwStoreProvFlags;
262     PWINECRYPT_CERTSTORE            memStore;
263     HCERTSTOREPROV                  hStoreProv;
264     PFN_CERT_STORE_PROV_CLOSE       provCloseStore;
265     PFN_CERT_STORE_PROV_WRITE_CERT  provWriteCert;
266     PFN_CERT_STORE_PROV_DELETE_CERT provDeleteCert;
267     PFN_CERT_STORE_PROV_CONTROL     provControl;
268 } WINE_PROVIDERSTORE, *PWINE_PROVIDERSTORE;
269
270 /* Internal version of CertGetCertificateContextProperty that gets properties
271  * directly from the context (or the context it's linked to, depending on its
272  * type.) Doesn't handle special-case properties, since they are handled by
273  * CertGetCertificateContextProperty, and are particular to the store in which
274  * the property exists (which is separate from the context.)
275  */
276 static BOOL WINAPI CRYPT_GetCertificateContextProperty(
277  PWINE_CERT_CONTEXT context, DWORD dwPropId, void *pvData, DWORD *pcbData);
278
279 /* Internal version of CertSetCertificateContextProperty that sets properties
280  * directly on the context (or the context it's linked to, depending on its
281  * type.) Doesn't handle special cases, since they're handled by
282  * CertSetCertificateContextProperty anyway.
283  */
284 static BOOL WINAPI CRYPT_SetCertificateContextProperty(
285  PWINE_CERT_CONTEXT context, DWORD dwPropId, DWORD dwFlags,
286  const void *pvData);
287
288 static void CRYPT_InitStore(WINECRYPT_CERTSTORE *store, HCRYPTPROV hCryptProv,
289  DWORD dwFlags, CertStoreType type)
290 {
291     store->ref = 1;
292     store->dwMagic = WINE_CRYPTCERTSTORE_MAGIC;
293     store->type = type;
294     if (!hCryptProv)
295     {
296         hCryptProv = CRYPT_GetDefaultProvider();
297         dwFlags |= CERT_STORE_NO_CRYPT_RELEASE_FLAG;
298     }
299     store->cryptProv = hCryptProv;
300     store->dwOpenFlags = dwFlags;
301 }
302
303 /* Initializes the reference ref to point to context, and increments context's
304  * reference count.  Also sets the hCertStore member of the reference to store.
305  */
306 static void CRYPT_InitCertRef(PWINE_CERT_CONTEXT_LINK ref,
307  PWINE_CERT_CONTEXT context, HCERTSTORE store)
308 {
309     TRACE("(%p, %p)\n", ref, context);
310     memcpy(&ref->cert, context, sizeof(ref->cert));
311     ref->ref = 1;
312     ref->type = ContextTypeLink;
313     ref->linked = context;
314     InterlockedIncrement(&context->ref);
315     TRACE("%p's ref count is %ld\n", context, context->ref);
316     ref->cert.hCertStore = store;
317 }
318
319 static BOOL CRYPT_MemAddCert(PWINECRYPT_CERTSTORE store,
320  PWINE_CERT_CONTEXT cert, DWORD dwAddDisposition,
321  PCCERT_CONTEXT *ppStoreContext)
322 {
323     WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store;
324     BOOL add = FALSE, ret;
325     PCCERT_CONTEXT existing = NULL;
326
327     TRACE("(%p, %p, %ld, %p)\n", store, cert, dwAddDisposition, ppStoreContext);
328
329     if (dwAddDisposition != CERT_STORE_ADD_ALWAYS)
330     {
331         BYTE hashToAdd[20];
332         DWORD size = sizeof(hashToAdd);
333
334         ret = CRYPT_GetCertificateContextProperty((PWINE_CERT_CONTEXT)cert,
335          CERT_HASH_PROP_ID, hashToAdd, &size);
336         if (ret)
337         {
338             CRYPT_HASH_BLOB blob = { sizeof(hashToAdd), hashToAdd };
339
340             existing = CertFindCertificateInStore(store,
341              cert->cert.dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
342              NULL);
343         }
344     }
345     switch (dwAddDisposition)
346     {
347     case CERT_STORE_ADD_ALWAYS:
348         add = TRUE;
349         break;
350     case CERT_STORE_ADD_NEW:
351     {
352         if (existing)
353         {
354             TRACE("found matching certificate, not adding\n");
355             SetLastError(CRYPT_E_EXISTS);
356             add = FALSE;
357         }
358         else
359             add = TRUE;
360         break;
361     }
362     case CERT_STORE_ADD_REPLACE_EXISTING:
363     {
364         add = TRUE;
365         if (existing)
366         {
367             TRACE("found matching certificate, replacing\n");
368             CertDeleteCertificateFromStore(existing);
369         }
370         break;
371     }
372     default:
373         FIXME("Unimplemented add disposition %ld\n", dwAddDisposition);
374         add = FALSE;
375     }
376     if (existing)
377         CertFreeCertificateContext(existing);
378     if (add)
379     {
380         PWINE_CERT_LIST_ENTRY entry = CryptMemAlloc(
381          sizeof(WINE_CERT_LIST_ENTRY));
382
383         if (entry)
384         {
385             TRACE("adding %p\n", entry);
386             CRYPT_InitCertRef(&entry->cert, (PWINE_CERT_CONTEXT)cert, store);
387             EnterCriticalSection(&ms->cs);
388             list_add_tail(&ms->certs, &entry->entry);
389             LeaveCriticalSection(&ms->cs);
390             if (ppStoreContext)
391                 *ppStoreContext =
392                  CertDuplicateCertificateContext((PCCERT_CONTEXT)entry);
393             ret = TRUE;
394         }
395         else
396             ret = FALSE;
397     }
398     else
399         ret = FALSE;
400     TRACE("returning %d\n", ret);
401     return ret;
402 }
403
404 static PWINE_CERT_CONTEXT CRYPT_MemEnumCert(PWINECRYPT_CERTSTORE store,
405  PWINE_CERT_CONTEXT pPrev)
406 {
407     WINE_MEMSTORE *ms = (WINE_MEMSTORE *)store;
408     PWINE_CERT_LIST_ENTRY prevEntry = (PWINE_CERT_LIST_ENTRY)pPrev;
409     PWINE_CERT_CONTEXT ret;
410     struct list *listNext;
411
412     TRACE("(%p, %p)\n", store, pPrev);
413     EnterCriticalSection(&ms->cs);
414     if (prevEntry)
415     {
416         listNext = list_next(&ms->certs, &prevEntry->entry);
417         CertFreeCertificateContext((PCCERT_CONTEXT)pPrev);
418     }
419     else
420         listNext = list_next(&ms->certs, &ms->certs);
421     if (listNext)
422         ret = (PWINE_CERT_CONTEXT)CertDuplicateCertificateContext(
423          (PCCERT_CONTEXT)LIST_ENTRY(listNext, WINE_CERT_LIST_ENTRY, entry));
424     else
425     {
426         SetLastError(CRYPT_E_NOT_FOUND);
427         ret = NULL;
428     }
429     LeaveCriticalSection(&ms->cs);
430
431     TRACE("returning %p\n", ret);
432     return ret;
433 }
434
435 static BOOL WINAPI CRYPT_MemDeleteCert(HCERTSTORE hCertStore,
436  PCCERT_CONTEXT pCertContext, DWORD dwFlags)
437 {
438     WINE_MEMSTORE *store = (WINE_MEMSTORE *)hCertStore;
439     PWINE_CERT_LIST_ENTRY cert = (PWINE_CERT_LIST_ENTRY)pCertContext;
440     BOOL ret;
441
442     /* The passed-in context is itself a list entry, so just remove it. */
443     EnterCriticalSection(&store->cs);
444     list_remove(&cert->entry);
445     ret = CertFreeCertificateContext(pCertContext);
446     LeaveCriticalSection(&store->cs);
447     return ret;
448 }
449
450 static void CRYPT_MemEmptyStore(PWINE_MEMSTORE store)
451 {
452     PWINE_CERT_LIST_ENTRY cert, next;
453
454     EnterCriticalSection(&store->cs);
455     LIST_FOR_EACH_ENTRY_SAFE(cert, next, &store->certs, WINE_CERT_LIST_ENTRY,
456      entry)
457     {
458         TRACE("removing %p\n", cert);
459         list_remove(&cert->entry);
460         CertFreeCertificateContext((PCCERT_CONTEXT)cert);
461     }
462     LeaveCriticalSection(&store->cs);
463 }
464
465 static void WINAPI CRYPT_MemCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
466 {
467     WINE_MEMSTORE *store = (WINE_MEMSTORE *)hCertStore;
468
469     TRACE("(%p, %08lx)\n", store, dwFlags);
470     if (dwFlags)
471         FIXME("Unimplemented flags: %08lx\n", dwFlags);
472
473     CRYPT_MemEmptyStore(store);
474     DeleteCriticalSection(&store->cs);
475     CryptMemFree(store);
476 }
477
478 static WINECRYPT_CERTSTORE *CRYPT_MemOpenStore(HCRYPTPROV hCryptProv,
479  DWORD dwFlags, const void *pvPara)
480 {
481     PWINE_MEMSTORE store;
482
483     TRACE("(%ld, %08lx, %p)\n", hCryptProv, dwFlags, pvPara);
484
485     if (dwFlags & CERT_STORE_DELETE_FLAG)
486     {
487         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
488         store = NULL;
489     }
490     else
491     {
492         store = CryptMemAlloc(sizeof(WINE_MEMSTORE));
493         if (store)
494         {
495             memset(store, 0, sizeof(WINE_MEMSTORE));
496             CRYPT_InitStore(&store->hdr, hCryptProv, dwFlags, StoreTypeMem);
497             store->hdr.closeStore    = CRYPT_MemCloseStore;
498             store->hdr.addCert       = CRYPT_MemAddCert;
499             store->hdr.enumCert      = CRYPT_MemEnumCert;
500             store->hdr.deleteCert    = CRYPT_MemDeleteCert;
501             store->hdr.control       = NULL;
502             InitializeCriticalSection(&store->cs);
503             list_init(&store->certs);
504         }
505     }
506     return (PWINECRYPT_CERTSTORE)store;
507 }
508
509 static BOOL CRYPT_CollectionAddCert(PWINECRYPT_CERTSTORE store,
510  PWINE_CERT_CONTEXT cert, DWORD dwAddDisposition,
511  PCCERT_CONTEXT *ppStoreContext)
512 {
513     PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
514     PWINE_STORE_LIST_ENTRY entry, next;
515     BOOL ret;
516
517     TRACE("(%p, %p, %ld, %p)\n", store, cert, dwAddDisposition, ppStoreContext);
518
519     ret = FALSE;
520     EnterCriticalSection(&cs->cs);
521     LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY,
522      entry)
523     {
524         if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)
525         {
526             ret = entry->store->addCert(entry->store, cert, dwAddDisposition,
527              ppStoreContext);
528             break;
529         }
530     }
531     LeaveCriticalSection(&cs->cs);
532     SetLastError(ret ? ERROR_SUCCESS : HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
533     return ret;
534 }
535
536 static void WINAPI CRYPT_CollectionCloseStore(HCERTSTORE store, DWORD dwFlags)
537 {
538     PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
539     PWINE_STORE_LIST_ENTRY entry, next;
540
541     TRACE("(%p, %08lx)\n", store, dwFlags);
542
543     LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY,
544      entry)
545     {
546         TRACE("closing %p\n", entry);
547         CertCloseStore((HCERTSTORE)entry->store, dwFlags);
548         CryptMemFree(entry);
549     }
550     DeleteCriticalSection(&cs->cs);
551     CryptMemFree(cs);
552 }
553
554 /* Advances a collection enumeration by one cert, if possible, where advancing
555  * means:
556  * - calling the current store's enumeration function once, and returning
557  *   the enumerated cert if one is returned
558  * - moving to the next store if the current store has no more items, and
559  *   recursively calling itself to get the next item.
560  * Returns NULL if the collection contains no more items or on error.
561  * Assumes the collection store's lock is held.
562  */
563 static PWINE_COLLECTION_CERT_CONTEXT CRYPT_CollectionAdvanceEnum(
564  PWINE_COLLECTIONSTORE store, PWINE_STORE_LIST_ENTRY storeEntry,
565  PWINE_COLLECTION_CERT_CONTEXT pPrev)
566 {
567     PWINE_COLLECTION_CERT_CONTEXT ret;
568     PWINE_CERT_CONTEXT child;
569     struct list *storeNext = list_next(&store->stores, &storeEntry->entry);
570
571     TRACE("(%p, %p, %p)\n", store, storeEntry, pPrev);
572
573     if (pPrev)
574     {
575         /* Ref-counting funny business: "duplicate" (addref) the child, because
576          * the CertFreeCertificateContext(pPrev) below can cause the ref count
577          * to become negative.  See comment below as well.
578          */
579         child = ((PWINE_COLLECTION_CERT_CONTEXT)pPrev)->cert.linked;
580         CertDuplicateCertificateContext((PCCERT_CONTEXT)child);
581         child = storeEntry->store->enumCert((HCERTSTORE)storeEntry->store,
582          child);
583         CertFreeCertificateContext((PCCERT_CONTEXT)pPrev);
584         pPrev = NULL;
585     }
586     else
587         child = storeEntry->store->enumCert((HCERTSTORE)storeEntry->store,
588          NULL);
589     if (child)
590     {
591         ret = CryptMemAlloc(sizeof(WINE_COLLECTION_CERT_CONTEXT));
592         if (ret)
593         {
594             CRYPT_InitCertRef((PWINE_CERT_CONTEXT_LINK)ret, child, store);
595             /* enumCert already addref'd once, and CRYPT_InitCertRef does again,
596              * so free child once to get the ref count right.  (Not doing so
597              * will leak memory if the caller calls CertFreeCertificateContext
598              * rather than CertEnumCertificatesInStore.)
599              */
600             CertFreeCertificateContext((PCCERT_CONTEXT)child);
601             ret->storeEntry = storeEntry;
602         }
603         else
604             CertFreeCertificateContext((PCCERT_CONTEXT)child);
605     }
606     else
607     {
608         if (storeNext)
609         {
610             storeEntry = LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, entry);
611             ret = CRYPT_CollectionAdvanceEnum(store, storeEntry, NULL);
612         }
613         else
614         {
615             SetLastError(CRYPT_E_NOT_FOUND);
616             ret = NULL;
617         }
618     }
619     TRACE("returning %p\n", ret);
620     return ret;
621 }
622
623 static PWINE_CERT_CONTEXT CRYPT_CollectionEnumCert(PWINECRYPT_CERTSTORE store,
624  PWINE_CERT_CONTEXT pPrev)
625 {
626     PWINE_COLLECTIONSTORE cs = (PWINE_COLLECTIONSTORE)store;
627     PWINE_COLLECTION_CERT_CONTEXT prevEntry =
628      (PWINE_COLLECTION_CERT_CONTEXT)pPrev, ret;
629
630     TRACE("(%p, %p)\n", store, pPrev);
631
632     if (prevEntry)
633     {
634         EnterCriticalSection(&cs->cs);
635         ret = CRYPT_CollectionAdvanceEnum(cs, prevEntry->storeEntry, prevEntry);
636         LeaveCriticalSection(&cs->cs);
637     }
638     else
639     {
640         EnterCriticalSection(&cs->cs);
641         if (!list_empty(&cs->stores))
642         {
643             PWINE_STORE_LIST_ENTRY storeEntry;
644
645             storeEntry = LIST_ENTRY(cs->stores.next, WINE_STORE_LIST_ENTRY,
646              entry);
647             ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, NULL);
648         }
649         else
650         {
651             SetLastError(CRYPT_E_NOT_FOUND);
652             ret = NULL;
653         }
654         LeaveCriticalSection(&cs->cs);
655     }
656     TRACE("returning %p\n", ret);
657     return (PWINE_CERT_CONTEXT)ret;
658 }
659
660 static BOOL WINAPI CRYPT_CollectionDeleteCert(HCERTSTORE hCertStore,
661  PCCERT_CONTEXT pCertContext, DWORD dwFlags)
662 {
663     PWINE_COLLECTION_CERT_CONTEXT context =
664      (PWINE_COLLECTION_CERT_CONTEXT)pCertContext;
665     BOOL ret;
666
667     TRACE("(%p, %p, %08lx)\n", hCertStore, pCertContext, dwFlags);
668
669     ret = CertDeleteCertificateFromStore((PCCERT_CONTEXT)context->cert.linked);
670     return ret;
671 }
672
673 static WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
674  DWORD dwFlags, const void *pvPara)
675 {
676     PWINE_COLLECTIONSTORE store;
677
678     if (dwFlags & CERT_STORE_DELETE_FLAG)
679     {
680         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
681         store = NULL;
682     }
683     else
684     {
685         store = CryptMemAlloc(sizeof(WINE_COLLECTIONSTORE));
686         if (store)
687         {
688             memset(store, 0, sizeof(WINE_COLLECTIONSTORE));
689             CRYPT_InitStore(&store->hdr, hCryptProv, dwFlags,
690              StoreTypeCollection);
691             store->hdr.closeStore    = CRYPT_CollectionCloseStore;
692             store->hdr.addCert       = CRYPT_CollectionAddCert;
693             store->hdr.enumCert      = CRYPT_CollectionEnumCert;
694             store->hdr.deleteCert    = CRYPT_CollectionDeleteCert;
695             InitializeCriticalSection(&store->cs);
696             list_init(&store->stores);
697         }
698     }
699     return (PWINECRYPT_CERTSTORE)store;
700 }
701
702 static void WINAPI CRYPT_ProvCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
703 {
704     PWINE_PROVIDERSTORE store = (PWINE_PROVIDERSTORE)hCertStore;
705
706     TRACE("(%p, %08lx)\n", store, dwFlags);
707
708     if (store->provCloseStore)
709         store->provCloseStore(store->hStoreProv, dwFlags);
710     if (!(store->dwStoreProvFlags & CERT_STORE_PROV_EXTERNAL_FLAG))
711         CertCloseStore(store->memStore, dwFlags);
712     CryptMemFree(store);
713 }
714
715 static BOOL CRYPT_ProvAddCert(PWINECRYPT_CERTSTORE store,
716  PWINE_CERT_CONTEXT cert, DWORD dwAddDisposition,
717  PCCERT_CONTEXT *ppStoreContext)
718 {
719     PWINE_PROVIDERSTORE ps = (PWINE_PROVIDERSTORE)store;
720     BOOL ret;
721
722     TRACE("(%p, %p, %ld, %p)\n", store, cert, dwAddDisposition, ppStoreContext);
723
724     if (ps->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG)
725     {
726         SetLastError(ERROR_ACCESS_DENIED);
727         ret = FALSE;
728     }
729     else
730     {
731         ret = TRUE;
732         if (ps->provWriteCert)
733             ret = ps->provWriteCert(ps->hStoreProv, (PCCERT_CONTEXT)cert,
734              CERT_STORE_PROV_WRITE_ADD_FLAG);
735         if (ret)
736         {
737             ret = ps->memStore->addCert(ps->memStore, cert,
738              dwAddDisposition, ppStoreContext);
739             /* dirty trick: replace the returned context's hCertStore with
740              * store.
741              */
742             if (ppStoreContext)
743                 (*(PCERT_CONTEXT *)ppStoreContext)->hCertStore = store;
744         }
745     }
746     return ret;
747 }
748
749 static PWINE_CERT_CONTEXT CRYPT_ProvEnumCert(PWINECRYPT_CERTSTORE store,
750  PWINE_CERT_CONTEXT pPrev)
751 {
752     PWINE_PROVIDERSTORE ps = (PWINE_PROVIDERSTORE)store;
753     PWINE_CERT_CONTEXT ret;
754
755     ret = ps->memStore->enumCert(ps->memStore, pPrev);
756     if (ret)
757     {
758         /* same dirty trick: replace the returned context's hCertStore with
759          * store.
760          */
761         ret->cert.hCertStore = store;
762     }
763     return ret;
764 }
765
766 static BOOL WINAPI CRYPT_ProvDeleteCert(HCERTSTORE hCertStore,
767  PCCERT_CONTEXT cert, DWORD dwFlags)
768 {
769     PWINE_PROVIDERSTORE store = (PWINE_PROVIDERSTORE)hCertStore;
770     BOOL ret = TRUE;
771
772     TRACE("(%p, %p, %08lx)\n", hCertStore, cert, dwFlags);
773
774     if (store->provDeleteCert)
775         ret = store->provDeleteCert(store->hStoreProv, cert, dwFlags);
776     if (ret)
777         ret = store->memStore->deleteCert(store->memStore, cert, dwFlags);
778     return ret;
779 }
780
781 static BOOL WINAPI CRYPT_ProvControl(HCERTSTORE hCertStore, DWORD dwFlags,
782  DWORD dwCtrlType, void const *pvCtrlPara)
783 {
784     PWINE_PROVIDERSTORE store = (PWINE_PROVIDERSTORE)hCertStore;
785     BOOL ret = TRUE;
786
787     TRACE("(%p, %08lx, %ld, %p)\n", hCertStore, dwFlags, dwCtrlType,
788      pvCtrlPara);
789
790     if (store->provControl)
791         ret = store->provControl(store->hStoreProv, dwFlags, dwCtrlType,
792          pvCtrlPara);
793     return ret;
794 }
795
796 static PWINECRYPT_CERTSTORE CRYPT_ProvCreateStore(HCRYPTPROV hCryptProv,
797  DWORD dwFlags, PWINECRYPT_CERTSTORE memStore, PCERT_STORE_PROV_INFO pProvInfo)
798 {
799     PWINE_PROVIDERSTORE ret = (PWINE_PROVIDERSTORE)CryptMemAlloc(
800      sizeof(WINE_PROVIDERSTORE));
801
802     if (ret)
803     {
804         CRYPT_InitStore(&ret->hdr, hCryptProv, dwFlags,
805          StoreTypeProvider);
806         ret->dwStoreProvFlags = pProvInfo->dwStoreProvFlags;
807         if (ret->dwStoreProvFlags & CERT_STORE_PROV_EXTERNAL_FLAG)
808         {
809             CertCloseStore(memStore, 0);
810             ret->memStore = NULL;
811         }
812         else
813             ret->memStore = memStore;
814         ret->hStoreProv = pProvInfo->hStoreProv;
815         ret->hdr.closeStore = CRYPT_ProvCloseStore;
816         ret->hdr.addCert = CRYPT_ProvAddCert;
817         ret->hdr.enumCert = CRYPT_ProvEnumCert;
818         ret->hdr.deleteCert = CRYPT_ProvDeleteCert;
819         ret->hdr.control = CRYPT_ProvControl;
820         if (pProvInfo->cStoreProvFunc > CERT_STORE_PROV_CLOSE_FUNC)
821             ret->provCloseStore =
822              pProvInfo->rgpvStoreProvFunc[CERT_STORE_PROV_CLOSE_FUNC];
823         else
824             ret->provCloseStore = NULL;
825         if (pProvInfo->cStoreProvFunc >
826          CERT_STORE_PROV_WRITE_CERT_FUNC)
827             ret->provWriteCert = pProvInfo->rgpvStoreProvFunc[
828              CERT_STORE_PROV_WRITE_CERT_FUNC];
829         else
830             ret->provWriteCert = NULL;
831         if (pProvInfo->cStoreProvFunc >
832          CERT_STORE_PROV_DELETE_CERT_FUNC)
833             ret->provDeleteCert = pProvInfo->rgpvStoreProvFunc[
834              CERT_STORE_PROV_DELETE_CERT_FUNC];
835         else
836             ret->provDeleteCert = NULL;
837         if (pProvInfo->cStoreProvFunc >
838          CERT_STORE_PROV_CONTROL_FUNC)
839             ret->provControl = pProvInfo->rgpvStoreProvFunc[
840              CERT_STORE_PROV_CONTROL_FUNC];
841         else
842             ret->provControl = NULL;
843     }
844     return (PWINECRYPT_CERTSTORE)ret;
845 }
846
847 static PWINECRYPT_CERTSTORE CRYPT_ProvOpenStore(LPCSTR lpszStoreProvider,
848  DWORD dwEncodingType, HCRYPTPROV hCryptProv, DWORD dwFlags, const void *pvPara)
849 {
850     static HCRYPTOIDFUNCSET set = NULL;
851     PFN_CERT_DLL_OPEN_STORE_PROV_FUNC provOpenFunc;
852     HCRYPTOIDFUNCADDR hFunc;
853     PWINECRYPT_CERTSTORE ret = NULL;
854
855     if (!set)
856         set = CryptInitOIDFunctionSet(CRYPT_OID_OPEN_STORE_PROV_FUNC, 0);
857     CryptGetOIDFunctionAddress(set, dwEncodingType, lpszStoreProvider, 0,
858      (void **)&provOpenFunc, &hFunc);
859     if (provOpenFunc)
860     {
861         CERT_STORE_PROV_INFO provInfo = { 0 };
862
863         provInfo.cbSize = sizeof(provInfo);
864         if (dwFlags & CERT_STORE_DELETE_FLAG)
865             provOpenFunc(lpszStoreProvider, dwEncodingType, hCryptProv,
866              dwFlags, pvPara, NULL, &provInfo);
867         else
868         {
869             PWINECRYPT_CERTSTORE memStore;
870
871             memStore = CRYPT_MemOpenStore(hCryptProv, dwFlags, NULL);
872             if (memStore)
873             {
874                 if (provOpenFunc(lpszStoreProvider, dwEncodingType, hCryptProv,
875                  dwFlags, pvPara, memStore, &provInfo))
876                     ret = CRYPT_ProvCreateStore(hCryptProv, dwFlags, memStore,
877                      &provInfo);
878                 else
879                     CertCloseStore(memStore, 0);
880             }
881         }
882         CryptFreeOIDFunctionAddress(hFunc, 0);
883     }
884     else
885         SetLastError(ERROR_FILE_NOT_FOUND);
886     return ret;
887 }
888
889 static void CRYPT_HashToStr(LPBYTE hash, LPWSTR asciiHash)
890 {
891     static const WCHAR fmt[] = { '%','0','2','X',0 };
892     DWORD i;
893
894     assert(hash);
895     assert(asciiHash);
896
897     for (i = 0; i < 20; i++)
898         wsprintfW(asciiHash + i * 2, fmt, hash[i]);
899 }
900
901 static const WCHAR CertsW[] = { 'C','e','r','t','i','f','i','c','a','t','e','s',
902  0 };
903 static const WCHAR CRLsW[] = { 'C','R','L','s',0 };
904 static const WCHAR CTLsW[] = { 'C','T','L','s',0 };
905 static const WCHAR BlobW[] = { 'B','l','o','b',0 };
906
907 static void CRYPT_RegReadSerializedFromReg(PWINE_REGSTOREINFO store, HKEY key,
908  DWORD contextType)
909 {
910     LONG rc;
911     DWORD index = 0;
912     WCHAR subKeyName[MAX_PATH];
913
914     do {
915         DWORD size = sizeof(subKeyName) / sizeof(WCHAR);
916
917         rc = RegEnumKeyExW(key, index++, subKeyName, &size, NULL, NULL, NULL,
918          NULL);
919         if (!rc)
920         {
921             HKEY subKey;
922
923             rc = RegOpenKeyExW(key, subKeyName, 0, KEY_READ, &subKey);
924             if (!rc)
925             {
926                 LPBYTE buf = NULL;
927
928                 size = 0;
929                 rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, NULL, &size);
930                 if (!rc)
931                     buf = CryptMemAlloc(size);
932                 if (buf)
933                 {
934                     rc = RegQueryValueExW(subKey, BlobW, NULL, NULL, buf,
935                      &size);
936                     if (!rc)
937                     {
938                         const void *context;
939                         DWORD addedType;
940
941                         TRACE("Adding cert with hash %s\n",
942                          debugstr_w(subKeyName));
943                         context = CRYPT_ReadSerializedElement(buf, size,
944                          contextType, &addedType);
945                         if (context)
946                         {
947                             const WINE_CONTEXT_INTERFACE *contextInterface;
948                             BYTE hash[20];
949
950                             switch (addedType)
951                             {
952                             case CERT_STORE_CERTIFICATE_CONTEXT:
953                                 contextInterface = &gCertInterface;
954                                 break;
955                             case CERT_STORE_CRL_CONTEXT:
956                                 contextInterface = &gCRLInterface;
957                                 break;
958                             case CERT_STORE_CTL_CONTEXT:
959                                 contextInterface = &gCTLInterface;
960                                 break;
961                             default:
962                                 contextInterface = NULL;
963                             }
964                             if (contextInterface)
965                             {
966                                 size = sizeof(hash);
967                                 if (contextInterface->getProp(context,
968                                  CERT_HASH_PROP_ID, hash, &size))
969                                 {
970                                     WCHAR asciiHash[20 * 2 + 1];
971
972                                     CRYPT_HashToStr(hash, asciiHash);
973                                     TRACE("comparing %s\n",
974                                      debugstr_w(asciiHash));
975                                     TRACE("with %s\n", debugstr_w(subKeyName));
976                                     if (!lstrcmpW(asciiHash, subKeyName))
977                                     {
978                                         TRACE("hash matches, adding\n");
979                                         contextInterface->addContextToStore(
980                                          store->memStore, context,
981                                          CERT_STORE_ADD_REPLACE_EXISTING, NULL);
982                                     }
983                                     else
984                                         TRACE("hash doesn't match, ignoring\n");
985                                 }
986                                 contextInterface->free(context);
987                             }
988                         }
989                     }
990                     CryptMemFree(buf);
991                 }
992                 RegCloseKey(subKey);
993             }
994             /* Ignore intermediate errors, continue enumerating */
995             rc = ERROR_SUCCESS;
996         }
997     } while (!rc);
998 }
999
1000 static void CRYPT_RegReadFromReg(PWINE_REGSTOREINFO store)
1001 {
1002     static const WCHAR *subKeys[] = { CertsW, CRLsW, CTLsW };
1003     static const DWORD contextFlags[] = { CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
1004      CERT_STORE_CRL_CONTEXT_FLAG, CERT_STORE_CTL_CONTEXT_FLAG };
1005     DWORD i;
1006
1007     for (i = 0; i < sizeof(subKeys) / sizeof(subKeys[0]); i++)
1008     {
1009         HKEY key;
1010         LONG rc;
1011
1012         rc = RegCreateKeyExW(store->key, subKeys[i], 0, NULL, 0, KEY_READ, NULL,
1013          &key, NULL);
1014         if (!rc)
1015         {
1016             CRYPT_RegReadSerializedFromReg(store, key, contextFlags[i]);
1017             RegCloseKey(key);
1018         }
1019     }
1020 }
1021
1022 /* Hash is assumed to be 20 bytes in length (a SHA-1 hash) */
1023 static BOOL CRYPT_WriteSerializedToReg(HKEY key, LPBYTE hash, LPBYTE buf,
1024  DWORD len)
1025 {
1026     WCHAR asciiHash[20 * 2 + 1];
1027     LONG rc;
1028     HKEY subKey;
1029     BOOL ret;
1030
1031     CRYPT_HashToStr(hash, asciiHash);
1032     rc = RegCreateKeyExW(key, asciiHash, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
1033      &subKey, NULL);
1034     if (!rc)
1035     {
1036         rc = RegSetValueExW(subKey, BlobW, 0, REG_BINARY, buf, len);
1037         RegCloseKey(subKey);
1038     }
1039     if (!rc)
1040         ret = TRUE;
1041     else
1042     {
1043         SetLastError(rc);
1044         ret = FALSE;
1045     }
1046     return ret;
1047 }
1048
1049 static BOOL CRYPT_SerializeContextsToReg(HKEY key,
1050  const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE memStore)
1051 {
1052     const void *context = NULL;
1053     BOOL ret;
1054
1055     do {
1056         context = contextInterface->enumContextsInStore(memStore, context);
1057         if (context)
1058         {
1059             BYTE hash[20];
1060             DWORD hashSize = sizeof(hash);
1061
1062             ret = contextInterface->getProp(context, CERT_HASH_PROP_ID, hash,
1063              &hashSize);
1064             if (ret)
1065             {
1066                 DWORD size = 0;
1067                 LPBYTE buf = NULL;
1068
1069                 ret = contextInterface->serialize(context, 0, NULL, &size);
1070                 if (size)
1071                     buf = CryptMemAlloc(size);
1072                 if (buf)
1073                 {
1074                     ret = contextInterface->serialize(context, 0, buf, &size);
1075                     if (ret)
1076                         ret = CRYPT_WriteSerializedToReg(key, hash, buf, size);
1077                 }
1078                 CryptMemFree(buf);
1079             }
1080         }
1081         else
1082             ret = TRUE;
1083     } while (ret && context != NULL);
1084     if (context)
1085         contextInterface->free(context);
1086     return ret;
1087 }
1088
1089 static BOOL CRYPT_RegWriteToReg(PWINE_REGSTOREINFO store)
1090 {
1091     static const WCHAR *subKeys[] = { CertsW, CRLsW, CTLsW };
1092     static const WINE_CONTEXT_INTERFACE *interfaces[] = { &gCertInterface,
1093      &gCRLInterface, &gCTLInterface };
1094     struct list *listToDelete[] = { &store->certsToDelete, NULL, NULL };
1095     BOOL ret = TRUE;
1096     DWORD i;
1097
1098     for (i = 0; ret && i < sizeof(subKeys) / sizeof(subKeys[0]); i++)
1099     {
1100         HKEY key;
1101         LONG rc = RegCreateKeyExW(store->key, subKeys[i], 0, NULL, 0,
1102          KEY_ALL_ACCESS, NULL, &key, NULL);
1103
1104         if (!rc)
1105         {
1106             if (listToDelete[i])
1107             {
1108                 PWINE_HASH_TO_DELETE toDelete, next;
1109                 WCHAR asciiHash[20 * 2 + 1];
1110
1111                 EnterCriticalSection(&store->cs);
1112                 LIST_FOR_EACH_ENTRY_SAFE(toDelete, next, listToDelete[i],
1113                  WINE_HASH_TO_DELETE, entry)
1114                 {
1115                     LONG rc;
1116
1117                     CRYPT_HashToStr(toDelete->hash, asciiHash);
1118                     TRACE("Removing %s\n", debugstr_w(asciiHash));
1119                     rc = RegDeleteKeyW(key, asciiHash);
1120                     if (rc != ERROR_SUCCESS && rc != ERROR_FILE_NOT_FOUND)
1121                     {
1122                         SetLastError(rc);
1123                         ret = FALSE;
1124                     }
1125                     list_remove(&toDelete->entry);
1126                     CryptMemFree(toDelete);
1127                 }
1128                 LeaveCriticalSection(&store->cs);
1129             }
1130             ret = CRYPT_SerializeContextsToReg(key, interfaces[i],
1131              store->memStore);
1132             RegCloseKey(key);
1133         }
1134         else
1135         {
1136             SetLastError(rc);
1137             ret = FALSE;
1138         }
1139     }
1140     return ret;
1141 }
1142
1143 /* If force is true or the registry store is dirty, writes the contents of the
1144  * store to the registry.
1145  */
1146 static BOOL CRYPT_RegFlushStore(PWINE_REGSTOREINFO store, BOOL force)
1147 {
1148     BOOL ret;
1149
1150     TRACE("(%p, %d)\n", store, force);
1151
1152     if (store->dirty || force)
1153         ret = CRYPT_RegWriteToReg(store);
1154     else
1155         ret = TRUE;
1156     return ret;
1157 }
1158
1159 static void WINAPI CRYPT_RegCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
1160 {
1161     PWINE_REGSTOREINFO store = (PWINE_REGSTOREINFO)hCertStore;
1162
1163     TRACE("(%p, %08lx)\n", store, dwFlags);
1164     if (dwFlags)
1165         FIXME("Unimplemented flags: %08lx\n", dwFlags);
1166
1167     CRYPT_RegFlushStore(store, FALSE);
1168     RegCloseKey(store->key);
1169     DeleteCriticalSection(&store->cs);
1170     CryptMemFree(store);
1171 }
1172
1173 static BOOL WINAPI CRYPT_RegWriteCert(HCERTSTORE hCertStore,
1174  PCCERT_CONTEXT cert, DWORD dwFlags)
1175 {
1176     PWINE_REGSTOREINFO store = (PWINE_REGSTOREINFO)hCertStore;
1177     BOOL ret;
1178
1179     TRACE("(%p, %p, %ld)\n", hCertStore, cert, dwFlags);
1180
1181     if (dwFlags & CERT_STORE_PROV_WRITE_ADD_FLAG)
1182     {
1183         store->dirty = TRUE;
1184         ret = TRUE;
1185     }
1186     else
1187         ret = FALSE;
1188     return ret;
1189 }
1190
1191 static BOOL WINAPI CRYPT_RegDeleteCert(HCERTSTORE hCertStore,
1192  PCCERT_CONTEXT pCertContext, DWORD dwFlags)
1193 {
1194     PWINE_REGSTOREINFO store = (PWINE_REGSTOREINFO)hCertStore;
1195     BOOL ret;
1196
1197     TRACE("(%p, %p, %08lx)\n", store, pCertContext, dwFlags);
1198
1199     if (store->dwOpenFlags & CERT_STORE_READONLY_FLAG)
1200     {
1201         SetLastError(ERROR_ACCESS_DENIED);
1202         ret = FALSE;
1203     }
1204     else
1205     {
1206         PWINE_HASH_TO_DELETE toDelete =
1207          CryptMemAlloc(sizeof(WINE_HASH_TO_DELETE));
1208
1209         if (toDelete)
1210         {
1211             DWORD size = sizeof(toDelete->hash);
1212
1213             ret = CertGetCertificateContextProperty(pCertContext,
1214              CERT_HASH_PROP_ID, toDelete->hash, &size);
1215             if (ret)
1216             {
1217                 EnterCriticalSection(&store->cs);
1218                 list_add_tail(&store->certsToDelete, &toDelete->entry);
1219                 LeaveCriticalSection(&store->cs);
1220             }
1221             else
1222             {
1223                 CryptMemFree(toDelete);
1224                 ret = FALSE;
1225             }
1226         }
1227         else
1228             ret = FALSE;
1229         if (ret)
1230             store->dirty = TRUE;
1231     }
1232     return ret;
1233 }
1234
1235 static BOOL WINAPI CRYPT_RegControl(HCERTSTORE hCertStore, DWORD dwFlags,
1236  DWORD dwCtrlType, void const *pvCtrlPara)
1237 {
1238     PWINE_REGSTOREINFO store = (PWINE_REGSTOREINFO)hCertStore;
1239     BOOL ret;
1240
1241     TRACE("(%p, %08lx, %ld, %p)\n", hCertStore, dwFlags, dwCtrlType,
1242      pvCtrlPara);
1243
1244     switch (dwCtrlType)
1245     {
1246     case CERT_STORE_CTRL_RESYNC:
1247         CRYPT_RegFlushStore(store, FALSE);
1248         CRYPT_MemEmptyStore((PWINE_MEMSTORE)store->memStore);
1249         CRYPT_RegReadFromReg(store);
1250         ret = TRUE;
1251         break;
1252     case CERT_STORE_CTRL_COMMIT:
1253         ret = CRYPT_RegFlushStore(store,
1254          dwFlags & CERT_STORE_CTRL_COMMIT_FORCE_FLAG);
1255         break;
1256     default:
1257         FIXME("%ld: stub\n", dwCtrlType);
1258         ret = FALSE;
1259     }
1260     return ret;
1261 }
1262
1263 /* Copied from shlwapi's SHDeleteKeyW, and reformatted to match this file. */
1264 static DWORD CRYPT_RecurseDeleteKey(HKEY hKey, LPCWSTR lpszSubKey)
1265 {
1266     DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
1267     WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
1268     HKEY hSubKey = 0;
1269
1270     TRACE("(hkey=%p,%s)\n", hKey, debugstr_w(lpszSubKey));
1271
1272     dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
1273     if (!dwRet)
1274     {
1275         /* Find how many subkeys there are */
1276         dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
1277          &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
1278         if (!dwRet)
1279         {
1280             dwMaxSubkeyLen++;
1281             if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
1282             {
1283                 /* Name too big: alloc a buffer for it */
1284                 lpszName = CryptMemAlloc(dwMaxSubkeyLen*sizeof(WCHAR));
1285             }
1286
1287             if (!lpszName)
1288                 dwRet = ERROR_NOT_ENOUGH_MEMORY;
1289             else
1290             {
1291                 /* Recursively delete all the subkeys */
1292                 for (i = 0; i < dwKeyCount && !dwRet; i++)
1293                 {
1294                     dwSize = dwMaxSubkeyLen;
1295                     dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL,
1296                      NULL, NULL, NULL);
1297                     if (!dwRet)
1298                         dwRet = CRYPT_RecurseDeleteKey(hSubKey, lpszName);
1299                 }
1300
1301                 if (lpszName != szNameBuf)
1302                 {
1303                     /* Free buffer if allocated */
1304                     CryptMemFree(lpszName);
1305                 }
1306             }
1307         }
1308
1309         RegCloseKey(hSubKey);
1310         if (!dwRet)
1311             dwRet = RegDeleteKeyW(hKey, lpszSubKey);
1312     }
1313     return dwRet;
1314 }
1315
1316 static void *regProvFuncs[] = {
1317     CRYPT_RegCloseStore,
1318     NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
1319     CRYPT_RegWriteCert,
1320     CRYPT_RegDeleteCert,
1321     NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
1322     NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
1323     NULL, /* CERT_STORE_PROV_WRITE_CRL_FUNC */
1324     NULL, /* CERT_STORE_PROV_DELETE_CRL_FUNC */
1325     NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
1326     NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
1327     NULL, /* CERT_STORE_PROV_WRITE_CTL_FUNC */
1328     NULL, /* CERT_STORE_PROV_DELETE_CTL_FUNC */
1329     NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
1330     CRYPT_RegControl,
1331 };
1332
1333 static WINECRYPT_CERTSTORE *CRYPT_RegOpenStore(HCRYPTPROV hCryptProv,
1334  DWORD dwFlags, const void *pvPara)
1335 {
1336     PWINECRYPT_CERTSTORE store = NULL;
1337
1338     TRACE("(%ld, %08lx, %p)\n", hCryptProv, dwFlags, pvPara);
1339
1340     if (dwFlags & CERT_STORE_DELETE_FLAG)
1341     {
1342         DWORD rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CertsW);
1343
1344         if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS)
1345             rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CRLsW);
1346         if (rc == ERROR_SUCCESS || rc == ERROR_NO_MORE_ITEMS)
1347             rc = CRYPT_RecurseDeleteKey((HKEY)pvPara, CTLsW);
1348         if (rc == ERROR_NO_MORE_ITEMS)
1349             rc = ERROR_SUCCESS;
1350         SetLastError(rc);
1351     }
1352     else
1353     {
1354         HKEY key;
1355
1356         if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara,
1357          GetCurrentProcess(), (LPHANDLE)&key,
1358          dwFlags & CERT_STORE_READONLY_FLAG ? KEY_READ : KEY_ALL_ACCESS,
1359          TRUE, 0))
1360         {
1361             PWINECRYPT_CERTSTORE memStore;
1362
1363             memStore = CRYPT_MemOpenStore(hCryptProv, dwFlags, NULL);
1364             if (memStore)
1365             {
1366                 PWINE_REGSTOREINFO regInfo = CryptMemAlloc(
1367                  sizeof(WINE_REGSTOREINFO));
1368
1369                 if (regInfo)
1370                 {
1371                     CERT_STORE_PROV_INFO provInfo = { 0 };
1372
1373                     regInfo->dwOpenFlags = dwFlags;
1374                     regInfo->cryptProv = hCryptProv;
1375                     regInfo->memStore = memStore;
1376                     regInfo->key = key;
1377                     InitializeCriticalSection(&regInfo->cs);
1378                     list_init(&regInfo->certsToDelete);
1379                     CRYPT_RegReadFromReg(regInfo);
1380                     regInfo->dirty = FALSE;
1381                     provInfo.cbSize = sizeof(provInfo);
1382                     provInfo.cStoreProvFunc = sizeof(regProvFuncs) /
1383                      sizeof(regProvFuncs[0]);
1384                     provInfo.rgpvStoreProvFunc = regProvFuncs;
1385                     provInfo.hStoreProv = regInfo;
1386                     store = CRYPT_ProvCreateStore(hCryptProv, dwFlags, memStore,
1387                      &provInfo);
1388                 }
1389             }
1390         }
1391     }
1392     TRACE("returning %p\n", store);
1393     return store;
1394 }
1395
1396 /* FIXME: this isn't complete for the Root store, in which the top-level
1397  * self-signed CA certs reside.  Adding a cert to the Root store should present
1398  * the user with a dialog indicating the consequences of doing so, and asking
1399  * the user to confirm whether the cert should be added.
1400  */
1401 static PWINECRYPT_CERTSTORE CRYPT_SysRegOpenStoreW(HCRYPTPROV hCryptProv,
1402  DWORD dwFlags, const void *pvPara)
1403 {
1404     static const WCHAR fmt[] = { '%','s','\\','%','s',0 };
1405     LPCWSTR storeName = (LPCWSTR)pvPara;
1406     LPWSTR storePath;
1407     PWINECRYPT_CERTSTORE store = NULL;
1408     HKEY root;
1409     LPCWSTR base;
1410     BOOL ret;
1411
1412     TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags,
1413      debugstr_w((LPCWSTR)pvPara));
1414
1415     if (!pvPara)
1416     {
1417         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
1418         return NULL;
1419     }
1420
1421     ret = TRUE;
1422     switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK)
1423     {
1424     case CERT_SYSTEM_STORE_LOCAL_MACHINE:
1425         root = HKEY_LOCAL_MACHINE;
1426         base = CERT_LOCAL_MACHINE_SYSTEM_STORE_REGPATH;
1427         break;
1428     case CERT_SYSTEM_STORE_CURRENT_USER:
1429         root = HKEY_CURRENT_USER;
1430         base = CERT_LOCAL_MACHINE_SYSTEM_STORE_REGPATH;
1431         break;
1432     case CERT_SYSTEM_STORE_CURRENT_SERVICE:
1433         /* hklm\Software\Microsoft\Cryptography\Services\servicename\
1434          * SystemCertificates
1435          */
1436         FIXME("CERT_SYSTEM_STORE_CURRENT_SERVICE, %s: stub\n",
1437          debugstr_w(storeName));
1438         return NULL;
1439     case CERT_SYSTEM_STORE_SERVICES:
1440         /* hklm\Software\Microsoft\Cryptography\Services\servicename\
1441          * SystemCertificates
1442          */
1443         FIXME("CERT_SYSTEM_STORE_SERVICES, %s: stub\n",
1444          debugstr_w(storeName));
1445         return NULL;
1446     case CERT_SYSTEM_STORE_USERS:
1447         /* hku\user sid\Software\Microsoft\SystemCertificates */
1448         FIXME("CERT_SYSTEM_STORE_USERS, %s: stub\n",
1449          debugstr_w(storeName));
1450         return NULL;
1451     case CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY:
1452         root = HKEY_CURRENT_USER;
1453         base = CERT_GROUP_POLICY_SYSTEM_STORE_REGPATH;
1454         break;
1455     case CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY:
1456         root = HKEY_LOCAL_MACHINE;
1457         base = CERT_GROUP_POLICY_SYSTEM_STORE_REGPATH;
1458         break;
1459     case CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE:
1460         /* hklm\Software\Microsoft\EnterpriseCertificates */
1461         FIXME("CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE, %s: stub\n",
1462          debugstr_w(storeName));
1463         return NULL;
1464     default:
1465         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
1466         return NULL;
1467     }
1468
1469     storePath = CryptMemAlloc((lstrlenW(base) + lstrlenW(storeName) + 2) *
1470      sizeof(WCHAR));
1471     if (storePath)
1472     {
1473         LONG rc;
1474         HKEY key;
1475         REGSAM sam = dwFlags & CERT_STORE_READONLY_FLAG ? KEY_READ :
1476             KEY_ALL_ACCESS;
1477
1478         wsprintfW(storePath, fmt, base, storeName);
1479         if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG)
1480             rc = RegOpenKeyExW(root, storePath, 0, sam, &key);
1481         else
1482         {
1483             DWORD disp;
1484
1485             rc = RegCreateKeyExW(root, storePath, 0, NULL, 0, sam, NULL,
1486                                  &key, &disp);
1487             if (!rc && dwFlags & CERT_STORE_CREATE_NEW_FLAG &&
1488                 disp == REG_OPENED_EXISTING_KEY)
1489             {
1490                 RegCloseKey(key);
1491                 rc = ERROR_FILE_EXISTS;
1492             }
1493         }
1494         if (!rc)
1495         {
1496             store = CRYPT_RegOpenStore(hCryptProv, dwFlags, key);
1497             RegCloseKey(key);
1498         }
1499         else
1500             SetLastError(rc);
1501         CryptMemFree(storePath);
1502     }
1503     return store;
1504 }
1505
1506 static PWINECRYPT_CERTSTORE CRYPT_SysRegOpenStoreA(HCRYPTPROV hCryptProv,
1507  DWORD dwFlags, const void *pvPara)
1508 {
1509     int len;
1510     PWINECRYPT_CERTSTORE ret = NULL;
1511
1512     TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags,
1513      debugstr_a((LPCSTR)pvPara));
1514
1515     if (!pvPara)
1516     {
1517         SetLastError(ERROR_FILE_NOT_FOUND);
1518         return NULL;
1519     }
1520     len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, NULL, 0);
1521     if (len)
1522     {
1523         LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR));
1524
1525         if (storeName)
1526         {
1527             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, storeName, len);
1528             ret = CRYPT_SysRegOpenStoreW(hCryptProv, dwFlags, storeName);
1529             CryptMemFree(storeName);
1530         }
1531     }
1532     return ret;
1533 }
1534
1535 static PWINECRYPT_CERTSTORE CRYPT_SysOpenStoreW(HCRYPTPROV hCryptProv,
1536  DWORD dwFlags, const void *pvPara)
1537 {
1538     HCERTSTORE store = 0;
1539     BOOL ret;
1540
1541     TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags,
1542      debugstr_w((LPCWSTR)pvPara));
1543
1544     if (!pvPara)
1545     {
1546         SetLastError(ERROR_FILE_NOT_FOUND);
1547         return NULL;
1548     }
1549     /* This returns a different error than system registry stores if the
1550      * location is invalid.
1551      */
1552     switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK)
1553     {
1554     case CERT_SYSTEM_STORE_LOCAL_MACHINE:
1555     case CERT_SYSTEM_STORE_CURRENT_USER:
1556     case CERT_SYSTEM_STORE_CURRENT_SERVICE:
1557     case CERT_SYSTEM_STORE_SERVICES:
1558     case CERT_SYSTEM_STORE_USERS:
1559     case CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY:
1560     case CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY:
1561     case CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE:
1562         ret = TRUE;
1563         break;
1564     default:
1565         SetLastError(ERROR_FILE_NOT_FOUND);
1566         ret = FALSE;
1567     }
1568     if (ret)
1569     {
1570         HCERTSTORE regStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W,
1571          0, hCryptProv, dwFlags, pvPara);
1572
1573         if (regStore)
1574         {
1575             store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
1576              CERT_STORE_CREATE_NEW_FLAG, NULL);
1577             CertAddStoreToCollection(store, regStore,
1578              dwFlags & CERT_STORE_READONLY_FLAG ? 0 :
1579              CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1580             CertCloseStore(regStore, 0);
1581             /* CERT_SYSTEM_STORE_CURRENT_USER returns both the HKCU and HKLM
1582              * stores.
1583              */
1584             if ((dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK) ==
1585              CERT_SYSTEM_STORE_CURRENT_USER)
1586             {
1587                 dwFlags &= ~CERT_SYSTEM_STORE_CURRENT_USER;
1588                 dwFlags |= CERT_SYSTEM_STORE_LOCAL_MACHINE;
1589                 regStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0,
1590                  hCryptProv, dwFlags, pvPara);
1591                 if (regStore)
1592                 {
1593                     CertAddStoreToCollection(store, regStore,
1594                      dwFlags & CERT_STORE_READONLY_FLAG ? 0 :
1595                      CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1596                     CertCloseStore(regStore, 0);
1597                 }
1598             }
1599         }
1600     }
1601     return (PWINECRYPT_CERTSTORE)store;
1602 }
1603
1604 static PWINECRYPT_CERTSTORE CRYPT_SysOpenStoreA(HCRYPTPROV hCryptProv,
1605  DWORD dwFlags, const void *pvPara)
1606 {
1607     int len;
1608     PWINECRYPT_CERTSTORE ret = NULL;
1609
1610     TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags,
1611      debugstr_a((LPCSTR)pvPara));
1612
1613     if (!pvPara)
1614     {
1615         SetLastError(ERROR_FILE_NOT_FOUND);
1616         return NULL;
1617     }
1618     len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, NULL, 0);
1619     if (len)
1620     {
1621         LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR));
1622
1623         if (storeName)
1624         {
1625             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, storeName, len);
1626             ret = CRYPT_SysOpenStoreW(hCryptProv, dwFlags, storeName);
1627             CryptMemFree(storeName);
1628         }
1629     }
1630     return ret;
1631 }
1632
1633 HCERTSTORE WINAPI CertOpenStore(LPCSTR lpszStoreProvider,
1634  DWORD dwMsgAndCertEncodingType, HCRYPTPROV hCryptProv, DWORD dwFlags,
1635  const void* pvPara)
1636 {
1637     WINECRYPT_CERTSTORE *hcs;
1638     StoreOpenFunc openFunc = NULL;
1639
1640     TRACE("(%s, %08lx, %08lx, %08lx, %p)\n", debugstr_a(lpszStoreProvider),
1641           dwMsgAndCertEncodingType, hCryptProv, dwFlags, pvPara);
1642
1643     if (!HIWORD(lpszStoreProvider))
1644     {
1645         switch (LOWORD(lpszStoreProvider))
1646         {
1647         case (int)CERT_STORE_PROV_MEMORY:
1648             openFunc = CRYPT_MemOpenStore;
1649             break;
1650         case (int)CERT_STORE_PROV_REG:
1651             openFunc = CRYPT_RegOpenStore;
1652             break;
1653         case (int)CERT_STORE_PROV_COLLECTION:
1654             openFunc = CRYPT_CollectionOpenStore;
1655             break;
1656         case (int)CERT_STORE_PROV_SYSTEM_A:
1657             openFunc = CRYPT_SysOpenStoreA;
1658             break;
1659         case (int)CERT_STORE_PROV_SYSTEM_W:
1660             openFunc = CRYPT_SysOpenStoreW;
1661             break;
1662         case (int)CERT_STORE_PROV_SYSTEM_REGISTRY_A:
1663             openFunc = CRYPT_SysRegOpenStoreA;
1664             break;
1665         case (int)CERT_STORE_PROV_SYSTEM_REGISTRY_W:
1666             openFunc = CRYPT_SysRegOpenStoreW;
1667             break;
1668         default:
1669             if (LOWORD(lpszStoreProvider))
1670                 FIXME("unimplemented type %d\n", LOWORD(lpszStoreProvider));
1671         }
1672     }
1673     else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_MEMORY))
1674         openFunc = CRYPT_MemOpenStore;
1675     else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_SYSTEM))
1676         openFunc = CRYPT_SysOpenStoreW;
1677     else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_COLLECTION))
1678         openFunc = CRYPT_CollectionOpenStore;
1679     else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_SYSTEM_REGISTRY))
1680         openFunc = CRYPT_SysRegOpenStoreW;
1681     else
1682     {
1683         FIXME("unimplemented type %s\n", lpszStoreProvider);
1684         openFunc = NULL;
1685     }
1686
1687     if (!openFunc)
1688         hcs = CRYPT_ProvOpenStore(lpszStoreProvider, dwMsgAndCertEncodingType,
1689          hCryptProv, dwFlags, pvPara);
1690     else
1691         hcs = openFunc(hCryptProv, dwFlags, pvPara);
1692     return (HCERTSTORE)hcs;
1693 }
1694
1695 HCERTSTORE WINAPI CertOpenSystemStoreA(HCRYPTPROV hProv,
1696  LPCSTR szSubSystemProtocol)
1697 {
1698     if (!szSubSystemProtocol)
1699     {
1700         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
1701         return 0;
1702     }
1703     return CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, hProv,
1704      CERT_SYSTEM_STORE_CURRENT_USER, szSubSystemProtocol);
1705 }
1706
1707 HCERTSTORE WINAPI CertOpenSystemStoreW(HCRYPTPROV hProv,
1708  LPCWSTR szSubSystemProtocol)
1709 {
1710     if (!szSubSystemProtocol)
1711     {
1712         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
1713         return 0;
1714     }
1715     return CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, hProv,
1716      CERT_SYSTEM_STORE_CURRENT_USER, szSubSystemProtocol);
1717 }
1718
1719 BOOL WINAPI CertSaveStore(HCERTSTORE hCertStore, DWORD dwMsgAndCertEncodingType,
1720              DWORD dwSaveAs, DWORD dwSaveTo, void* pvSaveToPara, DWORD dwFlags)
1721 {
1722     FIXME("(%p,%ld,%ld,%ld,%p,%08lx) stub!\n", hCertStore, 
1723           dwMsgAndCertEncodingType, dwSaveAs, dwSaveTo, pvSaveToPara, dwFlags);
1724     return TRUE;
1725 }
1726
1727 PCCRL_CONTEXT WINAPI CertCreateCRLContext( DWORD dwCertEncodingType,
1728   const BYTE* pbCrlEncoded, DWORD cbCrlEncoded)
1729 {
1730     PCRL_CONTEXT pcrl;
1731     BYTE* data;
1732
1733     TRACE("%08lx %p %08lx\n", dwCertEncodingType, pbCrlEncoded, cbCrlEncoded);
1734
1735     /* FIXME: semi-stub, need to use CryptDecodeObjectEx to decode the CRL. */
1736     pcrl = CryptMemAlloc( sizeof (CRL_CONTEXT) );
1737     if( !pcrl )
1738         return NULL;
1739
1740     data = CryptMemAlloc( cbCrlEncoded );
1741     if( !data )
1742     {
1743         CryptMemFree( pcrl );
1744         return NULL;
1745     }
1746
1747     pcrl->dwCertEncodingType = dwCertEncodingType;
1748     pcrl->pbCrlEncoded       = data;
1749     pcrl->cbCrlEncoded       = cbCrlEncoded;
1750     pcrl->pCrlInfo           = NULL;
1751     pcrl->hCertStore         = 0;
1752
1753     return pcrl;
1754 }
1755
1756 /* Decodes the encoded certificate and creates the certificate context for it.
1757  */
1758 static PWINE_CERT_CONTEXT CRYPT_CreateCertificateContext(
1759  DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded)
1760 {
1761     PWINE_CERT_CONTEXT_DATA cert = NULL;
1762     BOOL ret;
1763     PCERT_SIGNED_CONTENT_INFO signedCert = NULL;
1764     PCERT_INFO certInfo = NULL;
1765     DWORD size = 0;
1766
1767     TRACE("(%08lx, %p, %ld)\n", dwCertEncodingType, pbCertEncoded,
1768      cbCertEncoded);
1769
1770     /* First try to decode it as a signed cert. */
1771     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT, pbCertEncoded,
1772      cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&signedCert, &size);
1773     if (ret)
1774     {
1775         size = 0;
1776         ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED,
1777          signedCert->ToBeSigned.pbData, signedCert->ToBeSigned.cbData,
1778          CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&certInfo, &size);
1779         LocalFree(signedCert);
1780     }
1781     /* Failing that, try it as an unsigned cert */
1782     if (!ret)
1783     {
1784         size = 0;
1785         ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED,
1786          pbCertEncoded, cbCertEncoded,
1787          CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
1788          (BYTE *)&certInfo, &size);
1789     }
1790     if (ret)
1791     {
1792         BYTE *data = NULL;
1793
1794         cert = CryptMemAlloc(sizeof(WINE_CERT_CONTEXT_DATA));
1795         if (!cert)
1796             goto end;
1797         data = CryptMemAlloc(cbCertEncoded);
1798         if (!data)
1799         {
1800             CryptMemFree(cert);
1801             cert = NULL;
1802             goto end;
1803         }
1804         memcpy(data, pbCertEncoded, cbCertEncoded);
1805         cert->cert.dwCertEncodingType = dwCertEncodingType;
1806         cert->cert.pbCertEncoded      = data;
1807         cert->cert.cbCertEncoded      = cbCertEncoded;
1808         cert->cert.pCertInfo          = certInfo;
1809         cert->cert.hCertStore         = 0;
1810         cert->ref = 1;
1811         cert->type = ContextTypeData;
1812         cert->properties = ContextPropertyList_Create();
1813     }
1814
1815 end:
1816     return (PWINE_CERT_CONTEXT)cert;
1817 }
1818
1819 PCCERT_CONTEXT WINAPI CertCreateCertificateContext(DWORD dwCertEncodingType,
1820  const BYTE *pbCertEncoded, DWORD cbCertEncoded)
1821 {
1822     PWINE_CERT_CONTEXT cert;
1823
1824     TRACE("(%08lx, %p, %ld)\n", dwCertEncodingType, pbCertEncoded,
1825      cbCertEncoded);
1826
1827     cert = CRYPT_CreateCertificateContext(dwCertEncodingType, pbCertEncoded,
1828      cbCertEncoded);
1829     return (PCCERT_CONTEXT)cert;
1830 }
1831
1832 /* If context is a link, follows it to its linked context (recursively, if
1833  * necessary) and returns the data context associated with the link.
1834  * Otherwise just returns context.
1835  */
1836 static inline PWINE_CERT_CONTEXT_DATA CertContext_GetDataContext(
1837  PWINE_CERT_CONTEXT context)
1838 {
1839     PWINE_CERT_CONTEXT ptr = context;
1840
1841     while (ptr && ptr->type == ContextTypeLink)
1842         ptr = ((PWINE_CERT_CONTEXT_LINK)ptr)->linked;
1843     return (ptr && ptr->type == ContextTypeData) ?
1844      (PWINE_CERT_CONTEXT_DATA)ptr : NULL;
1845 }
1846
1847 DWORD WINAPI CertEnumCertificateContextProperties(PCCERT_CONTEXT pCertContext,
1848  DWORD dwPropId)
1849 {
1850     PWINE_CERT_CONTEXT_DATA linked = CertContext_GetDataContext(
1851      (PWINE_CERT_CONTEXT)pCertContext);
1852     DWORD ret;
1853
1854     TRACE("(%p, %ld)\n", pCertContext, dwPropId);
1855
1856     if (linked)
1857         ret = ContextPropertyList_EnumPropIDs(linked->properties, dwPropId);
1858     else
1859         ret = 0;
1860     return ret;
1861 }
1862
1863 static BOOL CRYPT_GetCertHashProp(PWINE_CERT_CONTEXT context, DWORD dwPropId,
1864  ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData,
1865  DWORD *pcbData)
1866 {
1867     BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData,
1868      pcbData);
1869     if (ret)
1870     {
1871         CRYPT_DATA_BLOB blob = { *pcbData, pvData };
1872
1873         ret = CRYPT_SetCertificateContextProperty(context, dwPropId,
1874          0, &blob);
1875     }
1876     return ret;
1877 }
1878
1879 static BOOL WINAPI CRYPT_GetCertificateContextProperty(
1880  PWINE_CERT_CONTEXT context, DWORD dwPropId, void *pvData, DWORD *pcbData)
1881 {
1882     PWINE_CERT_CONTEXT_DATA linked = CertContext_GetDataContext(context);
1883     BOOL ret;
1884     CRYPT_DATA_BLOB blob;
1885
1886     TRACE("(%p, %ld, %p, %p)\n", context, dwPropId, pvData, pcbData);
1887
1888     if (linked)
1889         ret = ContextPropertyList_FindProperty(linked->properties, dwPropId,
1890          &blob);
1891     else
1892         ret = FALSE;
1893     if (ret)
1894     {
1895         if (!pvData)
1896         {
1897             *pcbData = blob.cbData;
1898             ret = TRUE;
1899         }
1900         else if (*pcbData < blob.cbData)
1901         {
1902             SetLastError(ERROR_MORE_DATA);
1903             *pcbData = blob.cbData;
1904         }
1905         else
1906         {
1907             memcpy(pvData, blob.pbData, blob.cbData);
1908             *pcbData = blob.cbData;
1909             ret = TRUE;
1910         }
1911     }
1912     else
1913     {
1914         /* Implicit properties */
1915         switch (dwPropId)
1916         {
1917         case CERT_SHA1_HASH_PROP_ID:
1918             ret = CRYPT_GetCertHashProp(context, dwPropId, CALG_SHA1,
1919              context->cert.pbCertEncoded, context->cert.cbCertEncoded, pvData,
1920              pcbData);
1921             break;
1922         case CERT_MD5_HASH_PROP_ID:
1923             ret = CRYPT_GetCertHashProp(context, dwPropId, CALG_MD5,
1924              context->cert.pbCertEncoded, context->cert.cbCertEncoded, pvData,
1925              pcbData);
1926             break;
1927         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
1928             ret = CRYPT_GetCertHashProp(context, dwPropId, CALG_MD5,
1929              context->cert.pCertInfo->Subject.pbData,
1930              context->cert.pCertInfo->Subject.cbData,
1931              pvData, pcbData);
1932             break;
1933         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
1934             ret = CRYPT_GetCertHashProp(context, dwPropId, CALG_MD5,
1935              context->cert.pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
1936              context->cert.pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData,
1937              pvData, pcbData);
1938             break;
1939         case CERT_SIGNATURE_HASH_PROP_ID:
1940         case CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID:
1941             FIXME("implicit property %ld\n", dwPropId);
1942             SetLastError(CRYPT_E_NOT_FOUND);
1943             break;
1944         default:
1945             SetLastError(CRYPT_E_NOT_FOUND);
1946         }
1947     }
1948     TRACE("returning %d\n", ret);
1949     return ret;
1950 }
1951
1952 BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
1953  DWORD dwPropId, void *pvData, DWORD *pcbData)
1954 {
1955     BOOL ret;
1956
1957     TRACE("(%p, %ld, %p, %p)\n", pCertContext, dwPropId, pvData, pcbData);
1958
1959     /* Special cases for invalid/special prop IDs.
1960      */
1961     switch (dwPropId)
1962     {
1963     case 0:
1964     case CERT_CERT_PROP_ID:
1965     case CERT_CRL_PROP_ID:
1966     case CERT_CTL_PROP_ID:
1967         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
1968         return FALSE;
1969     case CERT_ACCESS_STATE_PROP_ID:
1970         if (!pvData)
1971         {
1972             *pcbData = sizeof(DWORD);
1973             return TRUE;
1974         }
1975         else if (*pcbData < sizeof(DWORD))
1976         {
1977             SetLastError(ERROR_MORE_DATA);
1978             *pcbData = sizeof(DWORD);
1979             return FALSE;
1980         }
1981         else
1982         {
1983             DWORD state = 0;
1984
1985             if (pCertContext->hCertStore)
1986             {
1987                 PWINECRYPT_CERTSTORE store =
1988                  (PWINECRYPT_CERTSTORE)pCertContext->hCertStore;
1989
1990                 if (!(store->dwOpenFlags & CERT_STORE_READONLY_FLAG))
1991                     state |= CERT_ACCESS_STATE_WRITE_PERSIST_FLAG;
1992             }
1993             *(DWORD *)pvData = state;
1994             return TRUE;
1995         }
1996     }
1997
1998     ret = CRYPT_GetCertificateContextProperty((PWINE_CERT_CONTEXT)pCertContext,
1999      dwPropId, pvData, pcbData);
2000     TRACE("returning %d\n", ret);
2001     return ret;
2002 }
2003
2004 static BOOL WINAPI CRYPT_SetCertificateContextProperty(
2005  PWINE_CERT_CONTEXT context, DWORD dwPropId, DWORD dwFlags, const void *pvData)
2006 {
2007     PWINE_CERT_CONTEXT_DATA linked = CertContext_GetDataContext(context);
2008     BOOL ret;
2009
2010     TRACE("(%p, %ld, %08lx, %p)\n", context, dwPropId, dwFlags, pvData);
2011
2012     if (!linked)
2013         ret = FALSE;
2014     else if (!pvData)
2015     {
2016         ContextPropertyList_RemoveProperty(linked->properties, dwPropId);
2017         ret = TRUE;
2018     }
2019     else
2020     {
2021         switch (dwPropId)
2022         {
2023         case CERT_AUTO_ENROLL_PROP_ID:
2024         case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */
2025         case CERT_DESCRIPTION_PROP_ID:
2026         case CERT_FRIENDLY_NAME_PROP_ID:
2027         case CERT_HASH_PROP_ID:
2028         case CERT_KEY_IDENTIFIER_PROP_ID:
2029         case CERT_MD5_HASH_PROP_ID:
2030         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
2031         case CERT_PUBKEY_ALG_PARA_PROP_ID:
2032         case CERT_PVK_FILE_PROP_ID:
2033         case CERT_SIGNATURE_HASH_PROP_ID:
2034         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
2035         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
2036         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
2037         case CERT_ENROLLMENT_PROP_ID:
2038         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
2039         case CERT_RENEWAL_PROP_ID:
2040         {
2041             PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvData;
2042
2043             ret = ContextPropertyList_SetProperty(linked->properties, dwPropId,
2044              blob->pbData, blob->cbData);
2045             break;
2046         }
2047         case CERT_DATE_STAMP_PROP_ID:
2048             ret = ContextPropertyList_SetProperty(linked->properties, dwPropId,
2049              pvData, sizeof(FILETIME));
2050             break;
2051         default:
2052             FIXME("%ld: stub\n", dwPropId);
2053             ret = FALSE;
2054         }
2055     }
2056     TRACE("returning %d\n", ret);
2057     return ret;
2058 }
2059
2060 BOOL WINAPI CertSetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
2061  DWORD dwPropId, DWORD dwFlags, const void *pvData)
2062 {
2063     BOOL ret;
2064
2065     TRACE("(%p, %ld, %08lx, %p)\n", pCertContext, dwPropId, dwFlags, pvData);
2066
2067     /* Handle special cases for "read-only"/invalid prop IDs.  Windows just
2068      * crashes on most of these, I'll be safer.
2069      */
2070     switch (dwPropId)
2071     {
2072     case 0:
2073     case CERT_ACCESS_STATE_PROP_ID:
2074     case CERT_CERT_PROP_ID:
2075     case CERT_CRL_PROP_ID:
2076     case CERT_CTL_PROP_ID:
2077         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
2078         return FALSE;
2079     }
2080     ret = CRYPT_SetCertificateContextProperty((PWINE_CERT_CONTEXT)pCertContext,
2081      dwPropId, dwFlags, pvData);
2082     TRACE("returning %d\n", ret);
2083     return ret;
2084 }
2085
2086 PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext(
2087  PCCERT_CONTEXT pCertContext)
2088 {
2089     PWINE_CERT_CONTEXT context = (PWINE_CERT_CONTEXT)pCertContext;
2090
2091     TRACE("(%p)\n", pCertContext);
2092     InterlockedIncrement(&context->ref);
2093     return pCertContext;
2094 }
2095
2096 BOOL WINAPI CertAddCertificateContextToStore(HCERTSTORE hCertStore,
2097  PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition,
2098  PCCERT_CONTEXT *ppStoreContext)
2099 {
2100     PWINECRYPT_CERTSTORE store = (PWINECRYPT_CERTSTORE)hCertStore;
2101     PWINE_CERT_CONTEXT_DATA linked = CertContext_GetDataContext(
2102      (PWINE_CERT_CONTEXT)pCertContext);
2103     PWINE_CERT_CONTEXT cert;
2104     BOOL ret;
2105
2106     TRACE("(%p, %p, %08lx, %p)\n", hCertStore, pCertContext,
2107      dwAddDisposition, ppStoreContext);
2108
2109     /* FIXME: some tests needed to verify return codes */
2110     if (!store)
2111     {
2112         SetLastError(ERROR_INVALID_PARAMETER);
2113         return FALSE;
2114     }
2115     if (store->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2116     {
2117         SetLastError(ERROR_INVALID_PARAMETER);
2118         return FALSE;
2119     }
2120
2121     cert = CRYPT_CreateCertificateContext(pCertContext->dwCertEncodingType,
2122      pCertContext->pbCertEncoded, pCertContext->cbCertEncoded);
2123     if (cert)
2124     {
2125         PWINE_CERT_CONTEXT_DATA certData = CertContext_GetDataContext(cert);
2126
2127         ContextPropertyList_Copy(certData->properties, linked->properties);
2128         ret = store->addCert(store, cert, dwAddDisposition, ppStoreContext);
2129         CertFreeCertificateContext((PCCERT_CONTEXT)cert);
2130     }
2131     else
2132         ret = FALSE;
2133     return ret;
2134 }
2135
2136 BOOL WINAPI CertAddEncodedCertificateToStore(HCERTSTORE hCertStore,
2137  DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded,
2138  DWORD dwAddDisposition, PCCERT_CONTEXT *ppCertContext)
2139 {
2140     WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore;
2141     BOOL ret;
2142
2143     TRACE("(%p, %08lx, %p, %ld, %08lx, %p)\n", hCertStore, dwCertEncodingType,
2144      pbCertEncoded, cbCertEncoded, dwAddDisposition, ppCertContext);
2145
2146     if (!hcs)
2147         ret = FALSE;
2148     else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2149         ret = FALSE;
2150     else
2151     {
2152         PWINE_CERT_CONTEXT cert = CRYPT_CreateCertificateContext(
2153          dwCertEncodingType, pbCertEncoded, cbCertEncoded);
2154
2155         if (cert)
2156         {
2157             ret = hcs->addCert(hcs, cert, dwAddDisposition, ppCertContext);
2158             CertFreeCertificateContext((PCCERT_CONTEXT)cert);
2159         }
2160         else
2161             ret = FALSE;
2162     }
2163     return ret;
2164 }
2165
2166 PCCERT_CONTEXT WINAPI CertEnumCertificatesInStore(HCERTSTORE hCertStore,
2167  PCCERT_CONTEXT pPrev)
2168 {
2169     WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore;
2170     PCCERT_CONTEXT ret;
2171
2172     TRACE("(%p, %p)\n", hCertStore, pPrev);
2173     if (!hCertStore)
2174         ret = NULL;
2175     else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2176         ret = NULL;
2177     else
2178         ret = (PCCERT_CONTEXT)hcs->enumCert(hcs, (PWINE_CERT_CONTEXT)pPrev);
2179     return ret;
2180 }
2181
2182 BOOL WINAPI CertDeleteCertificateFromStore(PCCERT_CONTEXT pCertContext)
2183 {
2184     BOOL ret;
2185
2186     TRACE("(%p)\n", pCertContext);
2187
2188     if (!pCertContext)
2189         ret = TRUE;
2190     else if (!pCertContext->hCertStore)
2191     {
2192         ret = TRUE;
2193         CertFreeCertificateContext(pCertContext);
2194     }
2195     else
2196     {
2197         PWINECRYPT_CERTSTORE hcs =
2198          (PWINECRYPT_CERTSTORE)pCertContext->hCertStore;
2199
2200         if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2201             ret = FALSE;
2202         else
2203             ret = hcs->deleteCert(hcs, pCertContext, 0);
2204         CertFreeCertificateContext(pCertContext);
2205     }
2206     return ret;
2207 }
2208
2209 BOOL WINAPI CertAddEncodedCRLToStore(HCERTSTORE hCertStore,
2210  DWORD dwCertEncodingType, const BYTE *pbCrlEncoded, DWORD cbCrlEncoded,
2211  DWORD dwAddDisposition, PCCRL_CONTEXT *ppCrlContext)
2212 {
2213     FIXME("(%p, %08lx, %p, %ld, %08lx, %p): stub\n", hCertStore,
2214      dwCertEncodingType, pbCrlEncoded, cbCrlEncoded, dwAddDisposition,
2215      ppCrlContext);
2216     return FALSE;
2217 }
2218
2219 BOOL WINAPI CertAddCRLContextToStore( HCERTSTORE hCertStore,
2220              PCCRL_CONTEXT pCrlContext, DWORD dwAddDisposition,
2221              PCCRL_CONTEXT* ppStoreContext )
2222 {
2223     FIXME("%p %p %08lx %p\n", hCertStore, pCrlContext,
2224           dwAddDisposition, ppStoreContext);
2225     return TRUE;
2226 }
2227
2228 BOOL WINAPI CertFreeCRLContext( PCCRL_CONTEXT pCrlContext)
2229 {
2230     FIXME("%p\n", pCrlContext );
2231
2232     return TRUE;
2233 }
2234
2235 BOOL WINAPI CertDeleteCRLFromStore(PCCRL_CONTEXT pCrlContext)
2236 {
2237     FIXME("(%p): stub\n", pCrlContext);
2238     return TRUE;
2239 }
2240
2241 PCCRL_CONTEXT WINAPI CertEnumCRLsInStore(HCERTSTORE hCertStore,
2242  PCCRL_CONTEXT pPrev)
2243 {
2244     FIXME("(%p, %p): stub\n", hCertStore, pPrev);
2245     return NULL;
2246 }
2247
2248 PCCTL_CONTEXT WINAPI CertCreateCTLContext(DWORD dwCertEncodingType,
2249   const BYTE* pbCtlEncoded, DWORD cbCtlEncoded)
2250 {
2251     FIXME("(%08lx, %p, %08lx): stub\n", dwCertEncodingType, pbCtlEncoded,
2252      cbCtlEncoded);
2253     return NULL;
2254 }
2255
2256 BOOL WINAPI CertAddEncodedCTLToStore(HCERTSTORE hCertStore,
2257  DWORD dwMsgAndCertEncodingType, const BYTE *pbCtlEncoded, DWORD cbCtlEncoded,
2258  DWORD dwAddDisposition, PCCTL_CONTEXT *ppCtlContext)
2259 {
2260     FIXME("(%p, %08lx, %p, %ld, %08lx, %p): stub\n", hCertStore,
2261      dwMsgAndCertEncodingType, pbCtlEncoded, cbCtlEncoded, dwAddDisposition,
2262      ppCtlContext);
2263     return FALSE;
2264 }
2265
2266 BOOL WINAPI CertAddCTLContextToStore(HCERTSTORE hCertStore,
2267  PCCTL_CONTEXT pCtlContext, DWORD dwAddDisposition,
2268  PCCTL_CONTEXT* ppStoreContext)
2269 {
2270     FIXME("(%p, %p, %08lx, %p): stub\n", hCertStore, pCtlContext,
2271      dwAddDisposition, ppStoreContext);
2272     return TRUE;
2273 }
2274
2275 BOOL WINAPI CertFreeCTLContext(PCCTL_CONTEXT pCtlContext)
2276 {
2277     FIXME("(%p): stub\n", pCtlContext );
2278     return TRUE;
2279 }
2280
2281 BOOL WINAPI CertDeleteCTLFromStore(PCCTL_CONTEXT pCtlContext)
2282 {
2283     FIXME("(%p): stub\n", pCtlContext);
2284     return TRUE;
2285 }
2286
2287 PCCTL_CONTEXT WINAPI CertEnumCTLsInStore(HCERTSTORE hCertStore,
2288  PCCTL_CONTEXT pPrev)
2289 {
2290     FIXME("(%p, %p): stub\n", hCertStore, pPrev);
2291     return NULL;
2292 }
2293
2294 HCERTSTORE WINAPI CertDuplicateStore(HCERTSTORE hCertStore)
2295 {
2296     WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore;
2297
2298     TRACE("(%p)\n", hCertStore);
2299
2300     if (hcs && hcs->dwMagic == WINE_CRYPTCERTSTORE_MAGIC)
2301         InterlockedIncrement(&hcs->ref);
2302     return hCertStore;
2303 }
2304
2305 BOOL WINAPI CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
2306 {
2307     WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *) hCertStore;
2308
2309     TRACE("(%p, %08lx)\n", hCertStore, dwFlags);
2310
2311     if( ! hCertStore )
2312         return TRUE;
2313
2314     if ( hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC )
2315         return FALSE;
2316
2317     if (InterlockedDecrement(&hcs->ref) == 0)
2318     {
2319         TRACE("%p's ref count is 0, freeing\n", hcs);
2320         hcs->dwMagic = 0;
2321         if (!(hcs->dwOpenFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
2322             CryptReleaseContext(hcs->cryptProv, 0);
2323         hcs->closeStore(hcs, dwFlags);
2324     }
2325     else
2326         TRACE("%p's ref count is %ld\n", hcs, hcs->ref);
2327     return TRUE;
2328 }
2329
2330 BOOL WINAPI CertControlStore(HCERTSTORE hCertStore, DWORD dwFlags,
2331  DWORD dwCtrlType, void const *pvCtrlPara)
2332 {
2333     WINECRYPT_CERTSTORE *hcs = (WINECRYPT_CERTSTORE *)hCertStore;
2334     BOOL ret;
2335
2336     TRACE("(%p, %08lx, %ld, %p)\n", hCertStore, dwFlags, dwCtrlType,
2337      pvCtrlPara);
2338
2339     if (!hcs)
2340         ret = FALSE;
2341     else if (hcs->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2342         ret = FALSE;
2343     else
2344     {
2345         if (hcs->control)
2346             ret = hcs->control(hCertStore, dwFlags, dwCtrlType, pvCtrlPara);
2347         else
2348             ret = TRUE;
2349     }
2350     return ret;
2351 }
2352
2353 BOOL WINAPI CertGetCRLContextProperty(PCCRL_CONTEXT pCRLContext,
2354  DWORD dwPropId, void *pvData, DWORD *pcbData)
2355 {
2356     FIXME("(%p, %ld, %p, %p): stub\n", pCRLContext, dwPropId, pvData, pcbData);
2357     return FALSE;
2358 }
2359
2360 BOOL WINAPI CertSetCRLContextProperty(PCCRL_CONTEXT pCRLContext,
2361  DWORD dwPropId, DWORD dwFlags, const void *pvData)
2362 {
2363     FIXME("(%p, %ld, %08lx, %p): stub\n", pCRLContext, dwPropId, dwFlags,
2364      pvData);
2365     return FALSE;
2366 }
2367
2368 BOOL WINAPI CertGetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
2369  DWORD dwPropId, void *pvData, DWORD *pcbData)
2370 {
2371     FIXME("(%p, %ld, %p, %p): stub\n", pCTLContext, dwPropId, pvData, pcbData);
2372     return FALSE;
2373 }
2374
2375 BOOL WINAPI CertSetCTLContextProperty(PCCTL_CONTEXT pCTLContext,
2376  DWORD dwPropId, DWORD dwFlags, const void *pvData)
2377 {
2378     FIXME("(%p, %ld, %08lx, %p): stub\n", pCTLContext, dwPropId, dwFlags,
2379      pvData);
2380     return FALSE;
2381 }
2382
2383 static void CertDataContext_Free(PWINE_CERT_CONTEXT_DATA context)
2384 {
2385     CryptMemFree(context->cert.pbCertEncoded);
2386     LocalFree(context->cert.pCertInfo);
2387     ContextPropertyList_Free(context->properties);
2388     CryptMemFree(context);
2389 }
2390
2391 static void CertLinkContext_Free(PWINE_CERT_CONTEXT_LINK context)
2392 {
2393     CertFreeCertificateContext((PCCERT_CONTEXT)context->linked);
2394     CryptMemFree(context);
2395 }
2396
2397 static void CertContext_Release(PWINE_CERT_CONTEXT context)
2398 {
2399     if (InterlockedDecrement(&context->ref) == 0)
2400     {
2401         TRACE("freeing %p\n", context);
2402         switch (context->type)
2403         {
2404         case ContextTypeData:
2405             CertDataContext_Free((PWINE_CERT_CONTEXT_DATA)context);
2406             break;
2407         case ContextTypeLink:
2408             CertLinkContext_Free((PWINE_CERT_CONTEXT_LINK)context);
2409             break;
2410         default:
2411             assert(0);
2412         }
2413     }
2414     else
2415         TRACE("%p's ref count is %ld\n", context, context->ref);
2416 }
2417
2418 BOOL WINAPI CertFreeCertificateContext(PCCERT_CONTEXT pCertContext)
2419 {
2420     TRACE("(%p)\n", pCertContext);
2421
2422     if (pCertContext)
2423         CertContext_Release((PWINE_CERT_CONTEXT)pCertContext);
2424     return TRUE;
2425 }
2426
2427 typedef BOOL (*CertCompareFunc)(PCCERT_CONTEXT pCertContext, DWORD dwType,
2428  DWORD dwFlags, const void *pvPara);
2429
2430 static BOOL compare_cert_any(PCCERT_CONTEXT pCertContext, DWORD dwType,
2431  DWORD dwFlags, const void *pvPara)
2432 {
2433     return TRUE;
2434 }
2435
2436 static BOOL compare_cert_by_md5_hash(PCCERT_CONTEXT pCertContext, DWORD dwType,
2437  DWORD dwFlags, const void *pvPara)
2438 {
2439     BOOL ret;
2440     BYTE hash[16];
2441     DWORD size = sizeof(hash);
2442
2443     ret = CertGetCertificateContextProperty(pCertContext,
2444      CERT_MD5_HASH_PROP_ID, hash, &size);
2445     if (ret)
2446     {
2447         const CRYPT_HASH_BLOB *pHash = (const CRYPT_HASH_BLOB *)pvPara;
2448
2449         if (size == pHash->cbData)
2450             ret = !memcmp(pHash->pbData, hash, size);
2451         else
2452             ret = FALSE;
2453     }
2454     return ret;
2455 }
2456
2457 static BOOL compare_cert_by_sha1_hash(PCCERT_CONTEXT pCertContext, DWORD dwType,
2458  DWORD dwFlags, const void *pvPara)
2459 {
2460     BOOL ret;
2461     BYTE hash[20];
2462     DWORD size = sizeof(hash);
2463
2464     ret = CertGetCertificateContextProperty(pCertContext,
2465      CERT_SHA1_HASH_PROP_ID, hash, &size);
2466     if (ret)
2467     {
2468         const CRYPT_HASH_BLOB *pHash = (const CRYPT_HASH_BLOB *)pvPara;
2469
2470         if (size == pHash->cbData)
2471             ret = !memcmp(pHash->pbData, hash, size);
2472         else
2473             ret = FALSE;
2474     }
2475     return ret;
2476 }
2477
2478 static BOOL compare_cert_by_name(PCCERT_CONTEXT pCertContext, DWORD dwType,
2479  DWORD dwFlags, const void *pvPara)
2480 {
2481     const CERT_NAME_BLOB *blob = (const CERT_NAME_BLOB *)pvPara, *toCompare;
2482     BOOL ret;
2483
2484     if (dwType & CERT_INFO_SUBJECT_FLAG)
2485         toCompare = &pCertContext->pCertInfo->Subject;
2486     else
2487         toCompare = &pCertContext->pCertInfo->Issuer;
2488     if (toCompare->cbData == blob->cbData)
2489         ret = !memcmp(toCompare->pbData, blob->pbData, blob->cbData);
2490     else
2491         ret = FALSE;
2492     return ret;
2493 }
2494
2495 static BOOL compare_cert_by_subject_cert(PCCERT_CONTEXT pCertContext,
2496  DWORD dwType, DWORD dwFlags, const void *pvPara)
2497 {
2498     const CERT_INFO *pCertInfo = (const CERT_INFO *)pvPara;
2499     BOOL ret;
2500
2501     if (pCertInfo->Issuer.cbData == pCertContext->pCertInfo->Subject.cbData)
2502         ret = !memcmp(pCertInfo->Issuer.pbData,
2503          pCertContext->pCertInfo->Subject.pbData, pCertInfo->Issuer.cbData);
2504     else
2505         ret = FALSE;
2506     return ret;
2507 }
2508
2509 PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore,
2510                 DWORD dwCertEncodingType, DWORD dwFlags, DWORD dwType,
2511                 const void *pvPara, PCCERT_CONTEXT pPrevCertContext)
2512 {
2513     PCCERT_CONTEXT ret;
2514     CertCompareFunc compare;
2515
2516     TRACE("(%p, %ld, %ld, %ld, %p, %p)\n", hCertStore, dwCertEncodingType,
2517          dwFlags, dwType, pvPara, pPrevCertContext);
2518
2519     switch (dwType >> CERT_COMPARE_SHIFT)
2520     {
2521     case CERT_COMPARE_ANY:
2522         compare = compare_cert_any;
2523         break;
2524     case CERT_COMPARE_MD5_HASH:
2525         compare = compare_cert_by_md5_hash;
2526         break;
2527     case CERT_COMPARE_SHA1_HASH:
2528         compare = compare_cert_by_sha1_hash;
2529         break;
2530     case CERT_COMPARE_NAME:
2531         compare = compare_cert_by_name;
2532         break;
2533     case CERT_COMPARE_SUBJECT_CERT:
2534         compare = compare_cert_by_subject_cert;
2535         break;
2536     default:
2537         FIXME("find type %08lx unimplemented\n", dwType);
2538         compare = NULL;
2539     }
2540
2541     if (compare)
2542     {
2543         BOOL matches = FALSE;
2544
2545         ret = pPrevCertContext;
2546         do {
2547             ret = CertEnumCertificatesInStore(hCertStore, ret);
2548             if (ret)
2549                 matches = compare(ret, dwType, dwFlags, pvPara);
2550         } while (ret != NULL && !matches);
2551         if (!ret)
2552             SetLastError(CRYPT_E_NOT_FOUND);
2553     }
2554     else
2555     {
2556         SetLastError(CRYPT_E_NOT_FOUND);
2557         ret = NULL;
2558     }
2559     return ret;
2560 }
2561
2562 BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore,
2563  HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority)
2564 {
2565     PWINE_COLLECTIONSTORE collection = (PWINE_COLLECTIONSTORE)hCollectionStore;
2566     WINECRYPT_CERTSTORE *sibling = (WINECRYPT_CERTSTORE *)hSiblingStore;
2567     PWINE_STORE_LIST_ENTRY entry;
2568     BOOL ret;
2569
2570     TRACE("(%p, %p, %08lx, %ld)\n", hCollectionStore, hSiblingStore,
2571      dwUpdateFlags, dwPriority);
2572
2573     if (!collection || !sibling)
2574         return TRUE;
2575     if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2576     {
2577         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
2578         return FALSE;
2579     }
2580     if (collection->hdr.type != StoreTypeCollection)
2581     {
2582         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
2583         return FALSE;
2584     }
2585     if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2586     {
2587         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
2588         return FALSE;
2589     }
2590
2591     entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY));
2592     if (entry)
2593     {
2594         InterlockedIncrement(&sibling->ref);
2595         TRACE("sibling %p's ref count is %ld\n", sibling, sibling->ref);
2596         entry->store = sibling;
2597         entry->dwUpdateFlags = dwUpdateFlags;
2598         entry->dwPriority = dwPriority;
2599         list_init(&entry->entry);
2600         TRACE("%p: adding %p, priority %ld\n", collection, entry, dwPriority);
2601         EnterCriticalSection(&collection->cs);
2602         if (dwPriority)
2603         {
2604             PWINE_STORE_LIST_ENTRY cursor;
2605             BOOL added = FALSE;
2606
2607             LIST_FOR_EACH_ENTRY(cursor, &collection->stores,
2608              WINE_STORE_LIST_ENTRY, entry)
2609             {
2610                 if (cursor->dwPriority < dwPriority)
2611                 {
2612                     list_add_before(&cursor->entry, &entry->entry);
2613                     added = TRUE;
2614                     break;
2615                 }
2616             }
2617             if (!added)
2618                 list_add_tail(&collection->stores, &entry->entry);
2619         }
2620         else
2621             list_add_tail(&collection->stores, &entry->entry);
2622         LeaveCriticalSection(&collection->cs);
2623         ret = TRUE;
2624     }
2625     else
2626         ret = FALSE;
2627     return ret;
2628 }
2629
2630 void WINAPI CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore,
2631  HCERTSTORE hSiblingStore)
2632 {
2633     PWINE_COLLECTIONSTORE collection = (PWINE_COLLECTIONSTORE)hCollectionStore;
2634     WINECRYPT_CERTSTORE *sibling = (WINECRYPT_CERTSTORE *)hSiblingStore;
2635     PWINE_STORE_LIST_ENTRY store, next;
2636
2637     TRACE("(%p, %p)\n", hCollectionStore, hSiblingStore);
2638
2639     if (!collection || !sibling)
2640         return;
2641     if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2642     {
2643         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
2644         return;
2645     }
2646     if (collection->hdr.type != StoreTypeCollection)
2647         return;
2648     if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
2649     {
2650         SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
2651         return;
2652     }
2653     EnterCriticalSection(&collection->cs);
2654     LIST_FOR_EACH_ENTRY_SAFE(store, next, &collection->stores,
2655      WINE_STORE_LIST_ENTRY, entry)
2656     {
2657         if (store->store == sibling)
2658         {
2659             list_remove(&store->entry);
2660             CertCloseStore(store->store, 0);
2661             CryptMemFree(store);
2662             break;
2663         }
2664     }
2665     LeaveCriticalSection(&collection->cs);
2666 }