crypt32: Check registered CryptFormatObject functions before using default hex format.
[wine] / dlls / crypt32 / filestore.c
1 /*
2  * Copyright 2004-2007 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #include <stdarg.h>
19 #include "windef.h"
20 #include "winbase.h"
21 #include "wincrypt.h"
22 #include "winnls.h"
23 #include "wine/debug.h"
24 #include "wine/unicode.h"
25 #include "crypt32_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
28
29 typedef struct _WINE_FILESTOREINFO
30 {
31     DWORD      dwOpenFlags;
32     HCERTSTORE memStore;
33     HANDLE     file;
34     DWORD      type;
35     BOOL       dirty;
36 } WINE_FILESTOREINFO, *PWINE_FILESTOREINFO;
37
38 static void WINAPI CRYPT_FileCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
39 {
40     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
41
42     TRACE("(%p, %08x)\n", store, dwFlags);
43     if (store->dirty)
44         CertSaveStore(store->memStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
45          store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0);
46     CertCloseStore(store->memStore, dwFlags);
47     CloseHandle(store->file);
48     CryptMemFree(store);
49 }
50
51 static BOOL WINAPI CRYPT_FileWriteCert(HCERTSTORE hCertStore,
52  PCCERT_CONTEXT cert, DWORD dwFlags)
53 {
54     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
55
56     TRACE("(%p, %p, %d)\n", hCertStore, cert, dwFlags);
57     store->dirty = TRUE;
58     return TRUE;
59 }
60
61 static BOOL WINAPI CRYPT_FileDeleteCert(HCERTSTORE hCertStore,
62  PCCERT_CONTEXT pCertContext, DWORD dwFlags)
63 {
64     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
65
66     TRACE("(%p, %p, %08x)\n", hCertStore, pCertContext, dwFlags);
67     store->dirty = TRUE;
68     return TRUE;
69 }
70
71 static BOOL WINAPI CRYPT_FileWriteCRL(HCERTSTORE hCertStore,
72  PCCRL_CONTEXT crl, DWORD dwFlags)
73 {
74     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
75
76     TRACE("(%p, %p, %d)\n", hCertStore, crl, dwFlags);
77     store->dirty = TRUE;
78     return TRUE;
79 }
80
81 static BOOL WINAPI CRYPT_FileDeleteCRL(HCERTSTORE hCertStore,
82  PCCRL_CONTEXT pCrlContext, DWORD dwFlags)
83 {
84     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
85
86     TRACE("(%p, %p, %08x)\n", hCertStore, pCrlContext, dwFlags);
87     store->dirty = TRUE;
88     return TRUE;
89 }
90
91 static BOOL WINAPI CRYPT_FileWriteCTL(HCERTSTORE hCertStore,
92  PCCTL_CONTEXT ctl, DWORD dwFlags)
93 {
94     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
95
96     TRACE("(%p, %p, %d)\n", hCertStore, ctl, dwFlags);
97     store->dirty = TRUE;
98     return TRUE;
99 }
100
101 static BOOL WINAPI CRYPT_FileDeleteCTL(HCERTSTORE hCertStore,
102  PCCTL_CONTEXT pCtlContext, DWORD dwFlags)
103 {
104     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
105
106     TRACE("(%p, %p, %08x)\n", hCertStore, pCtlContext, dwFlags);
107     store->dirty = TRUE;
108     return TRUE;
109 }
110
111 static BOOL CRYPT_ReadBlobFromFile(HANDLE file, PCERT_BLOB blob)
112 {
113     BOOL ret = TRUE;
114
115     blob->cbData = GetFileSize(file, NULL);
116     if (blob->cbData)
117     {
118         blob->pbData = CryptMemAlloc(blob->cbData);
119         if (blob->pbData)
120         {
121             DWORD read;
122
123             ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL);
124         }
125     }
126     return ret;
127 }
128
129 static BOOL WINAPI CRYPT_FileControl(HCERTSTORE hCertStore, DWORD dwFlags,
130  DWORD dwCtrlType, void const *pvCtrlPara)
131 {
132     PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
133     BOOL ret;
134
135     TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType,
136      pvCtrlPara);
137
138     switch (dwCtrlType)
139     {
140     case CERT_STORE_CTRL_RESYNC:
141         store->dirty = FALSE;
142         if (store->type == CERT_STORE_SAVE_AS_STORE)
143         {
144             HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
145              CERT_STORE_CREATE_NEW_FLAG, NULL);
146
147             /* FIXME: if I could translate a handle to a path, I could use
148              * CryptQueryObject instead, but there's no API to do so yet.
149              */
150             ret = CRYPT_ReadSerializedStoreFromFile(store->file, memStore);
151             if (ret)
152                 I_CertUpdateStore(store->memStore, memStore, 0, 0);
153             CertCloseStore(memStore, 0);
154         }
155         else if (store->type == CERT_STORE_SAVE_AS_PKCS7)
156         {
157             CERT_BLOB blob = { 0, NULL };
158
159             ret = CRYPT_ReadBlobFromFile(store->file, &blob);
160             if (ret)
161             {
162                 HCERTSTORE messageStore;
163
164                 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
165                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
166                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
167                  &messageStore, NULL, NULL);
168                 I_CertUpdateStore(store->memStore, messageStore, 0, 0);
169                 CertCloseStore(messageStore, 0);
170                 CryptMemFree(blob.pbData);
171             }
172         }
173         else
174         {
175             WARN("unknown type %d\n", store->type);
176             ret = FALSE;
177         }
178         break;
179     case CERT_STORE_CTRL_COMMIT:
180         if (!(store->dwOpenFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
181         {
182             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
183             ret = FALSE;
184         }
185         else if (store->dirty)
186             ret = CertSaveStore(store->memStore,
187              X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
188              store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0);
189         else
190             ret = TRUE;
191         break;
192     default:
193         FIXME("%d: stub\n", dwCtrlType);
194         ret = FALSE;
195     }
196     return ret;
197 }
198
199 static void *fileProvFuncs[] = {
200     CRYPT_FileCloseStore,
201     NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
202     CRYPT_FileWriteCert,
203     CRYPT_FileDeleteCert,
204     NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
205     NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
206     CRYPT_FileWriteCRL,
207     CRYPT_FileDeleteCRL,
208     NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
209     NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
210     CRYPT_FileWriteCTL,
211     CRYPT_FileDeleteCTL,
212     NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
213     CRYPT_FileControl,
214 };
215
216 static PWINECRYPT_CERTSTORE CRYPT_CreateFileStore(DWORD dwFlags,
217  HCERTSTORE memStore, HANDLE file, DWORD type)
218 {
219     PWINECRYPT_CERTSTORE store = NULL;
220     PWINE_FILESTOREINFO info = CryptMemAlloc(sizeof(WINE_FILESTOREINFO));
221
222     if (info)
223     {
224         CERT_STORE_PROV_INFO provInfo = { 0 };
225
226         info->dwOpenFlags = dwFlags;
227         info->memStore = memStore;
228         info->file = file;
229         info->type = type;
230         info->dirty = FALSE;
231         provInfo.cbSize = sizeof(provInfo);
232         provInfo.cStoreProvFunc = sizeof(fileProvFuncs) /
233          sizeof(fileProvFuncs[0]);
234         provInfo.rgpvStoreProvFunc = fileProvFuncs;
235         provInfo.hStoreProv = info;
236         store = CRYPT_ProvCreateStore(dwFlags, memStore, &provInfo);
237     }
238     return store;
239 }
240
241 PWINECRYPT_CERTSTORE CRYPT_FileOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags,
242  const void *pvPara)
243 {
244     PWINECRYPT_CERTSTORE store = NULL;
245     HANDLE file = (HANDLE)pvPara;
246
247     TRACE("(%ld, %08x, %p)\n", hCryptProv, dwFlags, pvPara);
248
249     if (!pvPara)
250     {
251         SetLastError(ERROR_INVALID_HANDLE);
252         return NULL;
253     }
254     if (dwFlags & CERT_STORE_DELETE_FLAG)
255     {
256         SetLastError(E_INVALIDARG);
257         return NULL;
258     }
259     if ((dwFlags & CERT_STORE_READONLY_FLAG) &&
260      (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
261     {
262         SetLastError(E_INVALIDARG);
263         return NULL;
264     }
265
266     if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara,
267      GetCurrentProcess(), &file, dwFlags & CERT_STORE_READONLY_FLAG ?
268      GENERIC_READ : GENERIC_READ | GENERIC_WRITE, TRUE, 0))
269     {
270         HCERTSTORE memStore;
271
272         memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
273          CERT_STORE_CREATE_NEW_FLAG, NULL);
274         if (memStore)
275         {
276             if (CRYPT_ReadSerializedStoreFromFile(file, memStore))
277             {
278                 store = CRYPT_CreateFileStore(dwFlags, memStore, file,
279                  CERT_STORE_SAVE_AS_STORE);
280                 /* File store doesn't need crypto provider, so close it */
281                 if (hCryptProv &&
282                  !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
283                     CryptReleaseContext(hCryptProv, 0);
284             }
285         }
286     }
287     TRACE("returning %p\n", store);
288     return store;
289 }
290
291 PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv,
292  DWORD dwFlags, const void *pvPara)
293 {
294     HCERTSTORE store = 0;
295     LPCWSTR fileName = (LPCWSTR)pvPara;
296     DWORD access, create;
297     HANDLE file;
298
299     TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, debugstr_w(fileName));
300
301     if (!fileName)
302     {
303         SetLastError(ERROR_PATH_NOT_FOUND);
304         return NULL;
305     }
306     if ((dwFlags & CERT_STORE_READONLY_FLAG) &&
307      (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
308     {
309         SetLastError(E_INVALIDARG);
310         return NULL;
311     }
312
313     access = GENERIC_READ;
314     if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)
315         access |= GENERIC_WRITE;
316     if (dwFlags & CERT_STORE_CREATE_NEW_FLAG)
317         create = CREATE_NEW;
318     else if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG)
319         create = OPEN_EXISTING;
320     else
321         create = OPEN_ALWAYS;
322     file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, create,
323      FILE_ATTRIBUTE_NORMAL, NULL);
324     if (file != INVALID_HANDLE_VALUE)
325     {
326         HCERTSTORE memStore = NULL;
327         DWORD size = GetFileSize(file, NULL), type = 0;
328
329         /* If the file isn't empty, try to get the type from the file itself */
330         if (size)
331         {
332             DWORD contentType;
333             BOOL ret;
334
335             /* Close the file so CryptQueryObject can succeed.. */
336             CloseHandle(file);
337             ret = CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName,
338              CERT_QUERY_CONTENT_FLAG_CERT |
339              CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
340              CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
341              CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL,
342              &memStore, NULL, NULL);
343             if (ret)
344             {
345                 if (contentType == CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)
346                     type = CERT_STORE_SAVE_AS_PKCS7;
347                 else
348                     type = CERT_STORE_SAVE_AS_STORE;
349                 /* and reopen the file. */
350                 file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL,
351                  create, FILE_ATTRIBUTE_NORMAL, NULL);
352             }
353         }
354         else
355         {
356             static const WCHAR spc[] = { 's','p','c',0 };
357             static const WCHAR p7c[] = { 'p','7','c',0 };
358             LPCWSTR ext = strrchrW(fileName, '.');
359
360             if (ext)
361             {
362                 ext++;
363                 if (!lstrcmpiW(ext, spc) || !lstrcmpiW(ext, p7c))
364                     type = CERT_STORE_SAVE_AS_PKCS7;
365             }
366             if (!type)
367                 type = CERT_STORE_SAVE_AS_STORE;
368             memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
369              CERT_STORE_CREATE_NEW_FLAG, NULL);
370         }
371         if (memStore)
372         {
373             store = CRYPT_CreateFileStore(dwFlags, memStore, file, type);
374             /* File store doesn't need crypto provider, so close it */
375             if (hCryptProv && !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
376                 CryptReleaseContext(hCryptProv, 0);
377         }
378     }
379     return (PWINECRYPT_CERTSTORE)store;
380 }
381
382 PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreA(HCRYPTPROV hCryptProv,
383  DWORD dwFlags, const void *pvPara)
384 {
385     int len;
386     PWINECRYPT_CERTSTORE ret = NULL;
387
388     TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags,
389      debugstr_a((LPCSTR)pvPara));
390
391     if (!pvPara)
392     {
393         SetLastError(ERROR_FILE_NOT_FOUND);
394         return NULL;
395     }
396     len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, NULL, 0);
397     if (len)
398     {
399         LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR));
400
401         if (storeName)
402         {
403             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, storeName, len);
404             ret = CRYPT_FileNameOpenStoreW(hCryptProv, dwFlags, storeName);
405             CryptMemFree(storeName);
406         }
407     }
408     return ret;
409 }