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