f3e618f693f6a5b1657913fb010e468240f5961f
[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
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 }