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