xref: /reactos/dll/win32/crypt32/filestore.c (revision 19b18ce2)
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;
37 
38 static void WINAPI CRYPT_FileCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
39 {
40     WINE_FILESTOREINFO *store = 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     CloseHandle(store->file);
47     CryptMemFree(store);
48 }
49 
50 static BOOL WINAPI CRYPT_FileWriteCert(HCERTSTORE hCertStore,
51  PCCERT_CONTEXT cert, DWORD dwFlags)
52 {
53     WINE_FILESTOREINFO *store = hCertStore;
54 
55     TRACE("(%p, %p, %d)\n", hCertStore, cert, dwFlags);
56     store->dirty = TRUE;
57     return TRUE;
58 }
59 
60 static BOOL WINAPI CRYPT_FileDeleteCert(HCERTSTORE hCertStore,
61  PCCERT_CONTEXT pCertContext, DWORD dwFlags)
62 {
63     WINE_FILESTOREINFO *store = hCertStore;
64 
65     TRACE("(%p, %p, %08x)\n", hCertStore, pCertContext, dwFlags);
66     store->dirty = TRUE;
67     return TRUE;
68 }
69 
70 static BOOL WINAPI CRYPT_FileWriteCRL(HCERTSTORE hCertStore,
71  PCCRL_CONTEXT crl, DWORD dwFlags)
72 {
73     WINE_FILESTOREINFO *store = hCertStore;
74 
75     TRACE("(%p, %p, %d)\n", hCertStore, crl, dwFlags);
76     store->dirty = TRUE;
77     return TRUE;
78 }
79 
80 static BOOL WINAPI CRYPT_FileDeleteCRL(HCERTSTORE hCertStore,
81  PCCRL_CONTEXT pCrlContext, DWORD dwFlags)
82 {
83     WINE_FILESTOREINFO *store = hCertStore;
84 
85     TRACE("(%p, %p, %08x)\n", hCertStore, pCrlContext, dwFlags);
86     store->dirty = TRUE;
87     return TRUE;
88 }
89 
90 static BOOL WINAPI CRYPT_FileWriteCTL(HCERTSTORE hCertStore,
91  PCCTL_CONTEXT ctl, DWORD dwFlags)
92 {
93     WINE_FILESTOREINFO *store = hCertStore;
94 
95     TRACE("(%p, %p, %d)\n", hCertStore, ctl, dwFlags);
96     store->dirty = TRUE;
97     return TRUE;
98 }
99 
100 static BOOL WINAPI CRYPT_FileDeleteCTL(HCERTSTORE hCertStore,
101  PCCTL_CONTEXT pCtlContext, DWORD dwFlags)
102 {
103     WINE_FILESTOREINFO *store = hCertStore;
104 
105     TRACE("(%p, %p, %08x)\n", hCertStore, pCtlContext, dwFlags);
106     store->dirty = TRUE;
107     return TRUE;
108 }
109 
110 static BOOL CRYPT_ReadBlobFromFile(HANDLE file, PCERT_BLOB blob)
111 {
112     BOOL ret = TRUE;
113 
114     blob->cbData = GetFileSize(file, NULL);
115     if (blob->cbData)
116     {
117         blob->pbData = CryptMemAlloc(blob->cbData);
118         if (blob->pbData)
119         {
120             DWORD read;
121 
122             ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL) && read == blob->cbData;
123             if (!ret) CryptMemFree(blob->pbData);
124         }
125         else
126             ret = FALSE;
127     }
128     return ret;
129 }
130 
131 static BOOL WINAPI CRYPT_FileControl(HCERTSTORE hCertStore, DWORD dwFlags,
132  DWORD dwCtrlType, void const *pvCtrlPara)
133 {
134     WINE_FILESTOREINFO *store = hCertStore;
135     BOOL ret;
136 
137     TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType,
138      pvCtrlPara);
139 
140     switch (dwCtrlType)
141     {
142     case CERT_STORE_CTRL_RESYNC:
143         store->dirty = FALSE;
144         if (store->type == CERT_STORE_SAVE_AS_STORE)
145         {
146             HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
147              CERT_STORE_CREATE_NEW_FLAG, NULL);
148 
149             /* FIXME: if I could translate a handle to a path, I could use
150              * CryptQueryObject instead, but there's no API to do so yet.
151              */
152             ret = CRYPT_ReadSerializedStoreFromFile(store->file, memStore);
153             if (ret)
154                 I_CertUpdateStore(store->memStore, memStore, 0, 0);
155             CertCloseStore(memStore, 0);
156         }
157         else if (store->type == CERT_STORE_SAVE_AS_PKCS7)
158         {
159             CERT_BLOB blob = { 0, NULL };
160 
161             ret = CRYPT_ReadBlobFromFile(store->file, &blob);
162             if (ret)
163             {
164                 HCERTSTORE messageStore;
165 
166                 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
167                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
168                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
169                  &messageStore, NULL, NULL);
170                 I_CertUpdateStore(store->memStore, messageStore, 0, 0);
171                 CertCloseStore(messageStore, 0);
172                 CryptMemFree(blob.pbData);
173             }
174         }
175         else
176         {
177             WARN("unknown type %d\n", store->type);
178             ret = FALSE;
179         }
180         break;
181     case CERT_STORE_CTRL_COMMIT:
182         if (!(store->dwOpenFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
183         {
184             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
185             ret = FALSE;
186         }
187         else if (store->dirty)
188             ret = CertSaveStore(store->memStore,
189              X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
190              store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0);
191         else
192             ret = TRUE;
193         break;
194     default:
195         FIXME("%d: stub\n", dwCtrlType);
196         ret = FALSE;
197     }
198     return ret;
199 }
200 
201 static void *fileProvFuncs[] = {
202     CRYPT_FileCloseStore,
203     NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
204     CRYPT_FileWriteCert,
205     CRYPT_FileDeleteCert,
206     NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
207     NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
208     CRYPT_FileWriteCRL,
209     CRYPT_FileDeleteCRL,
210     NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
211     NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
212     CRYPT_FileWriteCTL,
213     CRYPT_FileDeleteCTL,
214     NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
215     CRYPT_FileControl,
216 };
217 
218 static WINECRYPT_CERTSTORE *CRYPT_CreateFileStore(DWORD dwFlags,
219  HCERTSTORE memStore, HANDLE file, DWORD type)
220 {
221     WINECRYPT_CERTSTORE *store = NULL;
222     WINE_FILESTOREINFO *info = CryptMemAlloc(sizeof(WINE_FILESTOREINFO));
223 
224     if (info)
225     {
226         CERT_STORE_PROV_INFO provInfo = { 0 };
227 
228         info->dwOpenFlags = dwFlags;
229         info->memStore = memStore;
230         info->file = file;
231         info->type = type;
232         info->dirty = FALSE;
233         provInfo.cbSize = sizeof(provInfo);
234         provInfo.cStoreProvFunc = sizeof(fileProvFuncs) /
235          sizeof(fileProvFuncs[0]);
236         provInfo.rgpvStoreProvFunc = fileProvFuncs;
237         provInfo.hStoreProv = info;
238         store = CRYPT_ProvCreateStore(dwFlags, memStore, &provInfo);
239     }
240     return store;
241 }
242 
243 WINECRYPT_CERTSTORE *CRYPT_FileOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags,
244  const void *pvPara)
245 {
246     WINECRYPT_CERTSTORE *store = NULL;
247     HANDLE file = (HANDLE)pvPara;
248 
249     TRACE("(%ld, %08x, %p)\n", hCryptProv, dwFlags, pvPara);
250 
251     if (!pvPara)
252     {
253         SetLastError(ERROR_INVALID_HANDLE);
254         return NULL;
255     }
256     if (dwFlags & CERT_STORE_DELETE_FLAG)
257     {
258         SetLastError(E_INVALIDARG);
259         return NULL;
260     }
261     if ((dwFlags & CERT_STORE_READONLY_FLAG) &&
262      (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
263     {
264         SetLastError(E_INVALIDARG);
265         return NULL;
266     }
267 
268     if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara,
269      GetCurrentProcess(), &file, dwFlags & CERT_STORE_READONLY_FLAG ?
270      GENERIC_READ : GENERIC_READ | GENERIC_WRITE, TRUE, 0))
271     {
272         HCERTSTORE memStore;
273 
274         memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
275          CERT_STORE_CREATE_NEW_FLAG, NULL);
276         if (memStore)
277         {
278             if (CRYPT_ReadSerializedStoreFromFile(file, memStore))
279             {
280                 store = CRYPT_CreateFileStore(dwFlags, memStore, file,
281                  CERT_STORE_SAVE_AS_STORE);
282                 /* File store doesn't need crypto provider, so close it */
283                 if (hCryptProv &&
284                  !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
285                     CryptReleaseContext(hCryptProv, 0);
286             }
287         }
288     }
289     TRACE("returning %p\n", store);
290     return store;
291 }
292 
293 WINECRYPT_CERTSTORE *CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv,
294  DWORD dwFlags, const void *pvPara)
295 {
296     HCERTSTORE store = 0;
297     LPCWSTR fileName = pvPara;
298     DWORD access, create;
299     HANDLE file;
300 
301     TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, debugstr_w(fileName));
302 
303     if (!fileName)
304     {
305         SetLastError(ERROR_PATH_NOT_FOUND);
306         return NULL;
307     }
308     if ((dwFlags & CERT_STORE_READONLY_FLAG) &&
309      (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
310     {
311         SetLastError(E_INVALIDARG);
312         return NULL;
313     }
314 
315     access = GENERIC_READ;
316     if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)
317         access |= GENERIC_WRITE;
318     if (dwFlags & CERT_STORE_CREATE_NEW_FLAG)
319         create = CREATE_NEW;
320     else if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG)
321         create = OPEN_EXISTING;
322     else
323         create = OPEN_ALWAYS;
324     file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, create,
325      FILE_ATTRIBUTE_NORMAL, NULL);
326     if (file != INVALID_HANDLE_VALUE)
327     {
328         HCERTSTORE memStore = NULL;
329         DWORD size = GetFileSize(file, NULL), type = 0;
330 
331         /* If the file isn't empty, try to get the type from the file itself */
332         if (size)
333         {
334             DWORD contentType;
335             BOOL ret;
336 
337             /* Close the file so CryptQueryObject can succeed.. */
338             CloseHandle(file);
339             ret = CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName,
340              CERT_QUERY_CONTENT_FLAG_CERT |
341              CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
342              CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
343              CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &contentType, NULL,
344              &memStore, NULL, NULL);
345             if (ret)
346             {
347                 if (contentType == CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)
348                     type = CERT_STORE_SAVE_AS_PKCS7;
349                 else
350                     type = CERT_STORE_SAVE_AS_STORE;
351                 /* and reopen the file. */
352                 file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL,
353                  create, FILE_ATTRIBUTE_NORMAL, NULL);
354             }
355         }
356         else
357         {
358             static const WCHAR spc[] = { 's','p','c',0 };
359             static const WCHAR p7c[] = { 'p','7','c',0 };
360             LPCWSTR ext = strrchrW(fileName, '.');
361 
362             if (ext)
363             {
364                 ext++;
365                 if (!lstrcmpiW(ext, spc) || !lstrcmpiW(ext, p7c))
366                     type = CERT_STORE_SAVE_AS_PKCS7;
367             }
368             if (!type)
369                 type = CERT_STORE_SAVE_AS_STORE;
370             memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
371              CERT_STORE_CREATE_NEW_FLAG, NULL);
372         }
373         if (memStore)
374         {
375             store = CRYPT_CreateFileStore(dwFlags, memStore, file, type);
376             /* File store doesn't need crypto provider, so close it */
377             if (hCryptProv && !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
378                 CryptReleaseContext(hCryptProv, 0);
379         }
380     }
381     return store;
382 }
383 
384 WINECRYPT_CERTSTORE *CRYPT_FileNameOpenStoreA(HCRYPTPROV hCryptProv,
385  DWORD dwFlags, const void *pvPara)
386 {
387     int len;
388     WINECRYPT_CERTSTORE *ret = NULL;
389 
390     TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags,
391      debugstr_a(pvPara));
392 
393     if (!pvPara)
394     {
395         SetLastError(ERROR_FILE_NOT_FOUND);
396         return NULL;
397     }
398     len = MultiByteToWideChar(CP_ACP, 0, pvPara, -1, NULL, 0);
399     if (len)
400     {
401         LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR));
402 
403         if (storeName)
404         {
405             MultiByteToWideChar(CP_ACP, 0, pvPara, -1, storeName, len);
406             ret = CRYPT_FileNameOpenStoreW(hCryptProv, dwFlags, storeName);
407             CryptMemFree(storeName);
408         }
409     }
410     return ret;
411 }
412