push b72af2511d67bded8ece08d825ff0eb4a60c20a6
[wine/hacks.git] / dlls / crypt32 / rootstore.c
blob7941fa21a6975a071ab3a24938bc6782ec36eea0
1 /*
2 * Copyright 2007 Juan Lang
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.
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.
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
18 #include "config.h"
19 #include <stdarg.h>
20 #include <stdio.h>
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
23 #endif
24 #ifdef HAVE_SYS_STAT_H
25 #include <sys/stat.h>
26 #endif
27 #include <dirent.h>
28 #include <fcntl.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <errno.h>
33 #include <limits.h>
34 #include "ntstatus.h"
35 #define WIN32_NO_STATUS
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winreg.h"
39 #include "wincrypt.h"
40 #include "winternl.h"
41 #include "wine/debug.h"
42 #include "crypt32_private.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
46 #define INITIAL_CERT_BUFFER 1024
48 struct DynamicBuffer
50 DWORD allocated;
51 DWORD used;
52 BYTE *data;
55 static inline void reset_buffer(struct DynamicBuffer *buffer)
57 buffer->used = 0;
58 if (buffer->data) buffer->data[0] = 0;
61 static BOOL add_line_to_buffer(struct DynamicBuffer *buffer, LPCSTR line)
63 BOOL ret;
65 if (buffer->used + strlen(line) + 1 > buffer->allocated)
67 if (!buffer->allocated)
69 buffer->data = CryptMemAlloc(INITIAL_CERT_BUFFER);
70 if (buffer->data)
72 buffer->data[0] = 0;
73 buffer->allocated = INITIAL_CERT_BUFFER;
76 else
78 DWORD new_size = max(buffer->allocated * 2,
79 buffer->used + strlen(line) + 1);
81 buffer->data = CryptMemRealloc(buffer->data, new_size);
82 if (buffer->data)
83 buffer->allocated = new_size;
86 if (buffer->data)
88 strcpy((char *)buffer->data + strlen((char *)buffer->data), line);
89 /* Not strlen + 1, otherwise we'd count the NULL for every line's
90 * addition (but we overwrite the previous NULL character.) Not an
91 * overrun, we allocate strlen + 1 bytes above.
93 buffer->used += strlen(line);
94 ret = TRUE;
96 else
97 ret = FALSE;
98 return ret;
101 /* Reads any base64-encoded certificates present in fp and adds them to store.
102 * Returns TRUE if any certifcates were successfully imported.
104 static BOOL import_base64_certs_from_fp(FILE *fp, HCERTSTORE store)
106 char line[1024];
107 BOOL in_cert = FALSE;
108 struct DynamicBuffer saved_cert = { 0, 0, NULL };
109 int num_certs = 0;
111 TRACE("\n");
112 while (fgets(line, sizeof(line), fp))
114 static const char header[] = "-----BEGIN CERTIFICATE-----";
115 static const char trailer[] = "-----END CERTIFICATE-----";
117 if (!strncmp(line, header, strlen(header)))
119 TRACE("begin new certificate\n");
120 in_cert = TRUE;
121 reset_buffer(&saved_cert);
123 else if (!strncmp(line, trailer, strlen(trailer)))
125 DWORD size;
127 TRACE("end of certificate, adding cert\n");
128 in_cert = FALSE;
129 if (CryptStringToBinaryA((char *)saved_cert.data, saved_cert.used,
130 CRYPT_STRING_BASE64, NULL, &size, NULL, NULL))
132 LPBYTE buf = CryptMemAlloc(size);
134 if (buf)
136 CryptStringToBinaryA((char *)saved_cert.data,
137 saved_cert.used, CRYPT_STRING_BASE64, buf, &size, NULL,
138 NULL);
139 if (CertAddEncodedCertificateToStore(store,
140 X509_ASN_ENCODING, buf, size, CERT_STORE_ADD_NEW, NULL))
141 num_certs++;
145 else if (in_cert)
146 add_line_to_buffer(&saved_cert, line);
148 CryptMemFree(saved_cert.data);
149 TRACE("Read %d certs\n", num_certs);
150 return num_certs > 0;
153 static const char *trust_status_to_str(DWORD status)
155 static char buf[1024];
156 int pos = 0;
158 if (status & CERT_TRUST_IS_NOT_TIME_VALID)
159 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\texpired");
160 if (status & CERT_TRUST_IS_NOT_TIME_NESTED)
161 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad time nesting");
162 if (status & CERT_TRUST_IS_REVOKED)
163 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\trevoked");
164 if (status & CERT_TRUST_IS_NOT_SIGNATURE_VALID)
165 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad signature");
166 if (status & CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
167 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad usage");
168 if (status & CERT_TRUST_IS_UNTRUSTED_ROOT)
169 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tuntrusted root");
170 if (status & CERT_TRUST_REVOCATION_STATUS_UNKNOWN)
171 pos += snprintf(buf + pos, sizeof(buf) - pos,
172 "\n\tunknown revocation status");
173 if (status & CERT_TRUST_IS_CYCLIC)
174 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tcyclic chain");
175 if (status & CERT_TRUST_INVALID_EXTENSION)
176 pos += snprintf(buf + pos, sizeof(buf) - pos,
177 "\n\tunsupported critical extension");
178 if (status & CERT_TRUST_INVALID_POLICY_CONSTRAINTS)
179 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad policy");
180 if (status & CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
181 pos += snprintf(buf + pos, sizeof(buf) - pos,
182 "\n\tbad basic constraints");
183 if (status & CERT_TRUST_INVALID_NAME_CONSTRAINTS)
184 pos += snprintf(buf + pos, sizeof(buf) - pos,
185 "\n\tbad name constraints");
186 if (status & CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT)
187 pos += snprintf(buf + pos, sizeof(buf) - pos,
188 "\n\tunsuported name constraint");
189 if (status & CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT)
190 pos += snprintf(buf + pos, sizeof(buf) - pos,
191 "\n\tundefined name constraint");
192 if (status & CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT)
193 pos += snprintf(buf + pos, sizeof(buf) - pos,
194 "\n\tdisallowed name constraint");
195 if (status & CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT)
196 pos += snprintf(buf + pos, sizeof(buf) - pos,
197 "\n\texcluded name constraint");
198 if (status & CERT_TRUST_IS_OFFLINE_REVOCATION)
199 pos += snprintf(buf + pos, sizeof(buf) - pos,
200 "\n\trevocation server offline");
201 if (status & CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY)
202 pos += snprintf(buf + pos, sizeof(buf) - pos,
203 "\n\tno issuance policy");
204 return buf;
207 static const char *get_cert_common_name(PCCERT_CONTEXT cert)
209 static char buf[1024];
210 const char *name = NULL;
211 CERT_NAME_INFO *nameInfo;
212 DWORD size;
213 BOOL ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME,
214 cert->pCertInfo->Subject.pbData, cert->pCertInfo->Subject.cbData,
215 CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo,
216 &size);
218 if (ret)
220 PCERT_RDN_ATTR commonName = CertFindRDNAttr(szOID_COMMON_NAME,
221 nameInfo);
223 if (commonName)
225 CertRDNValueToStrA(commonName->dwValueType,
226 &commonName->Value, buf, sizeof(buf));
227 name = buf;
229 LocalFree(nameInfo);
231 return name;
234 static void check_and_store_certs(HCERTSTORE from, HCERTSTORE to)
236 PCCERT_CONTEXT cert = NULL;
237 DWORD root_count = 0;
239 TRACE("\n");
241 do {
242 cert = CertEnumCertificatesInStore(from, cert);
243 if (cert)
245 CERT_CHAIN_ENGINE_CONFIG chainEngineConfig =
246 { sizeof(chainEngineConfig), 0 };
247 HCERTCHAINENGINE engine = CRYPT_CreateChainEngine(to,
248 &chainEngineConfig);
250 if (engine)
252 CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
253 PCCERT_CHAIN_CONTEXT chain;
254 BOOL ret = CertGetCertificateChain(engine, cert, NULL, from,
255 &chainPara, 0, NULL, &chain);
257 if (!ret)
258 TRACE("rejecting %s: %s\n", get_cert_common_name(cert),
259 "chain creation failed");
260 else
262 /* The only allowed error is CERT_TRUST_IS_UNTRUSTED_ROOT */
263 if (chain->TrustStatus.dwErrorStatus &
264 ~CERT_TRUST_IS_UNTRUSTED_ROOT)
265 TRACE("rejecting %s: %s\n", get_cert_common_name(cert),
266 trust_status_to_str(chain->TrustStatus.dwErrorStatus &
267 ~CERT_TRUST_IS_UNTRUSTED_ROOT));
268 else
270 DWORD i, j;
272 for (i = 0; i < chain->cChain; i++)
273 for (j = 0; j < chain->rgpChain[i]->cElement; j++)
274 if (CertAddCertificateContextToStore(to,
275 chain->rgpChain[i]->rgpElement[j]->pCertContext,
276 CERT_STORE_ADD_NEW, NULL))
277 root_count++;
280 CertFreeCertificateChainEngine(engine);
283 } while (cert);
284 TRACE("Added %d root certificates\n", root_count);
287 /* Reads the file fd, and imports any certificates in it into store.
288 * Returns TRUE if any certificates were successfully imported.
290 static BOOL import_certs_from_file(int fd, HCERTSTORE store)
292 BOOL ret = FALSE;
293 FILE *fp;
295 TRACE("\n");
297 fp = fdopen(fd, "r");
298 if (fp)
300 ret = import_base64_certs_from_fp(fp, store);
301 fclose(fp);
303 return ret;
306 static BOOL import_certs_from_path(LPCSTR path, HCERTSTORE store,
307 BOOL allow_dir);
309 /* Opens path, which must be a directory, and imports certificates from every
310 * file in the directory into store.
311 * Returns TRUE if any certificates were successfully imported.
313 static BOOL import_certs_from_dir(LPCSTR path, HCERTSTORE store)
315 BOOL ret = FALSE;
316 DIR *dir;
318 TRACE("(%s, %p)\n", debugstr_a(path), store);
320 dir = opendir(path);
321 if (dir)
323 size_t bufsize = strlen(path) + 1 + PATH_MAX + 1;
324 char *filebuf = CryptMemAlloc(bufsize);
326 if (filebuf)
328 struct dirent *entry;
329 while ((entry = readdir(dir)))
331 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
333 snprintf(filebuf, bufsize, "%s/%s", path, entry->d_name);
334 if (import_certs_from_path(filebuf, store, FALSE) && !ret)
335 ret = TRUE;
338 closedir(dir);
339 CryptMemFree(filebuf);
342 return ret;
345 /* Opens path, which may be a file or a directory, and imports any certificates
346 * it finds into store.
347 * Returns TRUE if any certificates were successfully imported.
349 static BOOL import_certs_from_path(LPCSTR path, HCERTSTORE store,
350 BOOL allow_dir)
352 BOOL ret = FALSE;
353 int fd;
355 TRACE("(%s, %p, %d)\n", debugstr_a(path), store, allow_dir);
357 fd = open(path, O_RDONLY);
358 if (fd != -1)
360 struct stat st;
362 if (fstat(fd, &st) == 0)
364 if (S_ISREG(st.st_mode))
365 ret = import_certs_from_file(fd, store);
366 else if (S_ISDIR(st.st_mode))
368 if (allow_dir)
369 ret = import_certs_from_dir(path, store);
370 else
371 WARN("%s is a directory and directories are disallowed\n",
372 debugstr_a(path));
374 else
375 ERR("%s: invalid file type\n", path);
377 close(fd);
379 return ret;
382 static BOOL WINAPI CRYPT_RootWriteCert(HCERTSTORE hCertStore,
383 PCCERT_CONTEXT cert, DWORD dwFlags)
385 /* The root store can't have certs added */
386 return FALSE;
389 static BOOL WINAPI CRYPT_RootDeleteCert(HCERTSTORE hCertStore,
390 PCCERT_CONTEXT cert, DWORD dwFlags)
392 /* The root store can't have certs deleted */
393 return FALSE;
396 static BOOL WINAPI CRYPT_RootWriteCRL(HCERTSTORE hCertStore,
397 PCCRL_CONTEXT crl, DWORD dwFlags)
399 /* The root store can have CRLs added. At worst, a malicious application
400 * can DoS itself, as the changes aren't persisted in any way.
402 return TRUE;
405 static BOOL WINAPI CRYPT_RootDeleteCRL(HCERTSTORE hCertStore,
406 PCCRL_CONTEXT crl, DWORD dwFlags)
408 /* The root store can't have CRLs deleted */
409 return FALSE;
412 static void *rootProvFuncs[] = {
413 NULL, /* CERT_STORE_PROV_CLOSE_FUNC */
414 NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
415 CRYPT_RootWriteCert,
416 CRYPT_RootDeleteCert,
417 NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
418 NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
419 CRYPT_RootWriteCRL,
420 CRYPT_RootDeleteCRL,
421 NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
422 NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
423 NULL, /* CERT_STORE_PROV_WRITE_CTL_FUNC */
424 NULL, /* CERT_STORE_PROV_DELETE_CTL_FUNC */
425 NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
426 NULL, /* CERT_STORE_PROV_CONTROL_FUNC */
429 static const char * const CRYPT_knownLocations[] = {
430 "/etc/ssl/certs/ca-certificates.crt",
431 "/etc/ssl/certs",
432 "/etc/pki/tls/certs/ca-bundle.crt",
435 /* Reads certificates from the list of known locations. Stops when any
436 * location contains any certificates, to prevent spending unnecessary time
437 * adding redundant certificates, e.g. when both a certificate bundle and
438 * individual certificates exist in the same directory.
440 static PWINECRYPT_CERTSTORE CRYPT_RootOpenStoreFromKnownLocations(void)
442 HCERTSTORE root = NULL;
443 HCERTSTORE from = CertOpenStore(CERT_STORE_PROV_MEMORY,
444 X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
445 HCERTSTORE to = CertOpenStore(CERT_STORE_PROV_MEMORY,
446 X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
448 if (from && to)
450 CERT_STORE_PROV_INFO provInfo = {
451 sizeof(CERT_STORE_PROV_INFO),
452 sizeof(rootProvFuncs) / sizeof(rootProvFuncs[0]),
453 rootProvFuncs,
454 NULL,
456 NULL
458 DWORD i;
459 BOOL ret = FALSE;
461 for (i = 0; !ret &&
462 i < sizeof(CRYPT_knownLocations) / sizeof(CRYPT_knownLocations[0]);
463 i++)
464 ret = import_certs_from_path(CRYPT_knownLocations[i], from, TRUE);
465 check_and_store_certs(from, to);
466 root = CRYPT_ProvCreateStore(0, to, &provInfo);
468 CertCloseStore(from, 0);
469 TRACE("returning %p\n", root);
470 return root;
473 static PWINECRYPT_CERTSTORE CRYPT_rootStore;
475 PWINECRYPT_CERTSTORE CRYPT_RootOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags)
477 TRACE("(%ld, %08x)\n", hCryptProv, dwFlags);
479 if (dwFlags & CERT_STORE_DELETE_FLAG)
481 WARN("root store can't be deleted\n");
482 SetLastError(ERROR_ACCESS_DENIED);
483 return NULL;
485 switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK)
487 case CERT_SYSTEM_STORE_LOCAL_MACHINE:
488 case CERT_SYSTEM_STORE_CURRENT_USER:
489 break;
490 default:
491 TRACE("location %08x unsupported\n",
492 dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
493 SetLastError(E_INVALIDARG);
494 return NULL;
496 if (!CRYPT_rootStore)
498 HCERTSTORE root = CRYPT_RootOpenStoreFromKnownLocations();
500 InterlockedCompareExchangePointer((PVOID *)&CRYPT_rootStore, root,
501 NULL);
502 if (CRYPT_rootStore != root)
503 CertCloseStore(root, 0);
505 CertDuplicateStore(CRYPT_rootStore);
506 return CRYPT_rootStore;