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