0f7d8d5150f3fd94e0f69c2f8a4f93eeb551fd3e
[reactos.git] / dll / win32 / 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;
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 }