push 9ed7f32abbb93ea3a813fd6b1650e5bcfc506606
[wine/hacks.git] / dlls / crypt32 / rootstore.c
blobb96e456d30ebf930458fd5e89340dd648c3b434f
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 #include <sys/types.h>
22 #ifdef HAVE_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #include <dirent.h>
26 #include <fcntl.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #include <errno.h>
31 #include <limits.h>
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winreg.h"
37 #include "wincrypt.h"
38 #include "winternl.h"
39 #include "wine/debug.h"
40 #include "crypt32_private.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
44 #define INITIAL_CERT_BUFFER 1024
46 struct DynamicBuffer
48 DWORD allocated;
49 DWORD used;
50 BYTE *data;
53 static inline void reset_buffer(struct DynamicBuffer *buffer)
55 buffer->used = 0;
56 if (buffer->data) buffer->data[0] = 0;
59 static BOOL add_line_to_buffer(struct DynamicBuffer *buffer, LPCSTR line)
61 BOOL ret;
63 if (buffer->used + strlen(line) + 1 > buffer->allocated)
65 if (!buffer->allocated)
67 buffer->data = CryptMemAlloc(INITIAL_CERT_BUFFER);
68 if (buffer->data)
70 buffer->data[0] = 0;
71 buffer->allocated = INITIAL_CERT_BUFFER;
74 else
76 DWORD new_size = max(buffer->allocated * 2,
77 buffer->used + strlen(line) + 1);
79 buffer->data = CryptMemRealloc(buffer->data, new_size);
80 if (buffer->data)
81 buffer->allocated = new_size;
84 if (buffer->data)
86 strcpy((char *)buffer->data + strlen((char *)buffer->data), line);
87 /* Not strlen + 1, otherwise we'd count the NULL for every line's
88 * addition (but we overwrite the previous NULL character.) Not an
89 * overrun, we allocate strlen + 1 bytes above.
91 buffer->used += strlen(line);
92 ret = TRUE;
94 else
95 ret = FALSE;
96 return ret;
99 /* Reads any base64-encoded certificates present in fp and adds them to store.
100 * Returns TRUE if any certifcates were successfully imported.
102 static BOOL import_base64_certs_from_fp(FILE *fp, HCERTSTORE store)
104 char line[1024];
105 BOOL in_cert = FALSE;
106 struct DynamicBuffer saved_cert = { 0, 0, NULL };
107 int num_certs = 0;
109 TRACE("\n");
110 while (fgets(line, sizeof(line), fp))
112 static const char header[] = "-----BEGIN CERTIFICATE-----";
113 static const char trailer[] = "-----END CERTIFICATE-----";
115 if (!strncmp(line, header, strlen(header)))
117 TRACE("begin new certificate\n");
118 in_cert = TRUE;
119 reset_buffer(&saved_cert);
121 else if (!strncmp(line, trailer, strlen(trailer)))
123 DWORD size;
125 TRACE("end of certificate, adding cert\n");
126 in_cert = FALSE;
127 if (CryptStringToBinaryA((char *)saved_cert.data, saved_cert.used,
128 CRYPT_STRING_BASE64, NULL, &size, NULL, NULL))
130 LPBYTE buf = CryptMemAlloc(size);
132 if (buf)
134 CryptStringToBinaryA((char *)saved_cert.data,
135 saved_cert.used, CRYPT_STRING_BASE64, buf, &size, NULL,
136 NULL);
137 if (CertAddEncodedCertificateToStore(store,
138 X509_ASN_ENCODING, buf, size, CERT_STORE_ADD_NEW, NULL))
139 num_certs++;
140 CryptMemFree(buf);
144 else if (in_cert)
145 add_line_to_buffer(&saved_cert, line);
147 CryptMemFree(saved_cert.data);
148 TRACE("Read %d certs\n", num_certs);
149 return num_certs > 0;
152 static const char *trust_status_to_str(DWORD status)
154 static char buf[1024];
155 int pos = 0;
157 if (status & CERT_TRUST_IS_NOT_TIME_VALID)
158 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\texpired");
159 if (status & CERT_TRUST_IS_NOT_TIME_NESTED)
160 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad time nesting");
161 if (status & CERT_TRUST_IS_REVOKED)
162 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\trevoked");
163 if (status & CERT_TRUST_IS_NOT_SIGNATURE_VALID)
164 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad signature");
165 if (status & CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
166 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad usage");
167 if (status & CERT_TRUST_IS_UNTRUSTED_ROOT)
168 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tuntrusted root");
169 if (status & CERT_TRUST_REVOCATION_STATUS_UNKNOWN)
170 pos += snprintf(buf + pos, sizeof(buf) - pos,
171 "\n\tunknown revocation status");
172 if (status & CERT_TRUST_IS_CYCLIC)
173 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tcyclic chain");
174 if (status & CERT_TRUST_INVALID_EXTENSION)
175 pos += snprintf(buf + pos, sizeof(buf) - pos,
176 "\n\tunsupported critical extension");
177 if (status & CERT_TRUST_INVALID_POLICY_CONSTRAINTS)
178 pos += snprintf(buf + pos, sizeof(buf) - pos, "\n\tbad policy");
179 if (status & CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
180 pos += snprintf(buf + pos, sizeof(buf) - pos,
181 "\n\tbad basic constraints");
182 if (status & CERT_TRUST_INVALID_NAME_CONSTRAINTS)
183 pos += snprintf(buf + pos, sizeof(buf) - pos,
184 "\n\tbad name constraints");
185 if (status & CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT)
186 pos += snprintf(buf + pos, sizeof(buf) - pos,
187 "\n\tunsuported name constraint");
188 if (status & CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT)
189 pos += snprintf(buf + pos, sizeof(buf) - pos,
190 "\n\tundefined name constraint");
191 if (status & CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT)
192 pos += snprintf(buf + pos, sizeof(buf) - pos,
193 "\n\tdisallowed name constraint");
194 if (status & CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT)
195 pos += snprintf(buf + pos, sizeof(buf) - pos,
196 "\n\texcluded name constraint");
197 if (status & CERT_TRUST_IS_OFFLINE_REVOCATION)
198 pos += snprintf(buf + pos, sizeof(buf) - pos,
199 "\n\trevocation server offline");
200 if (status & CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY)
201 pos += snprintf(buf + pos, sizeof(buf) - pos,
202 "\n\tno issuance policy");
203 return buf;
206 static const char *get_cert_common_name(PCCERT_CONTEXT cert)
208 static char buf[1024];
209 const char *name = NULL;
210 CERT_NAME_INFO *nameInfo;
211 DWORD size;
212 BOOL ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME,
213 cert->pCertInfo->Subject.pbData, cert->pCertInfo->Subject.cbData,
214 CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo,
215 &size);
217 if (ret)
219 PCERT_RDN_ATTR commonName = CertFindRDNAttr(szOID_COMMON_NAME,
220 nameInfo);
222 if (commonName)
224 CertRDNValueToStrA(commonName->dwValueType,
225 &commonName->Value, buf, sizeof(buf));
226 name = buf;
228 LocalFree(nameInfo);
230 return name;
233 static void check_and_store_certs(HCERTSTORE from, HCERTSTORE to)
235 PCCERT_CONTEXT cert = NULL;
236 DWORD root_count = 0;
238 TRACE("\n");
240 do {
241 cert = CertEnumCertificatesInStore(from, cert);
242 if (cert)
244 CERT_CHAIN_ENGINE_CONFIG chainEngineConfig =
245 { sizeof(chainEngineConfig), 0 };
246 HCERTCHAINENGINE engine = CRYPT_CreateChainEngine(to,
247 &chainEngineConfig);
249 if (engine)
251 CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
252 PCCERT_CHAIN_CONTEXT chain;
253 BOOL ret = CertGetCertificateChain(engine, cert, NULL, from,
254 &chainPara, 0, NULL, &chain);
256 if (!ret)
257 TRACE("rejecting %s: %s\n", get_cert_common_name(cert),
258 "chain creation failed");
259 else
261 /* The only allowed error is CERT_TRUST_IS_UNTRUSTED_ROOT */
262 if (chain->TrustStatus.dwErrorStatus &
263 ~CERT_TRUST_IS_UNTRUSTED_ROOT)
264 TRACE("rejecting %s: %s\n", get_cert_common_name(cert),
265 trust_status_to_str(chain->TrustStatus.dwErrorStatus &
266 ~CERT_TRUST_IS_UNTRUSTED_ROOT));
267 else
269 DWORD i, j;
271 for (i = 0; i < chain->cChain; i++)
272 for (j = 0; j < chain->rgpChain[i]->cElement; j++)
273 if (CertAddCertificateContextToStore(to,
274 chain->rgpChain[i]->rgpElement[j]->pCertContext,
275 CERT_STORE_ADD_NEW, NULL))
276 root_count++;
279 CertFreeCertificateChainEngine(engine);
282 } while (cert);
283 TRACE("Added %d root certificates\n", root_count);
286 /* Reads the file fd, and imports any certificates in it into store.
287 * Returns TRUE if any certificates were successfully imported.
289 static BOOL import_certs_from_file(int fd, HCERTSTORE store)
291 BOOL ret = FALSE;
292 FILE *fp;
294 TRACE("\n");
296 fp = fdopen(fd, "r");
297 if (fp)
299 ret = import_base64_certs_from_fp(fp, store);
300 fclose(fp);
302 return ret;
305 static BOOL import_certs_from_path(LPCSTR path, HCERTSTORE store,
306 BOOL allow_dir);
308 /* Opens path, which must be a directory, and imports certificates from every
309 * file in the directory into store.
310 * Returns TRUE if any certificates were successfully imported.
312 static BOOL import_certs_from_dir(LPCSTR path, HCERTSTORE store)
314 BOOL ret = FALSE;
315 DIR *dir;
317 TRACE("(%s, %p)\n", debugstr_a(path), store);
319 dir = opendir(path);
320 if (dir)
322 size_t bufsize = strlen(path) + 1 + PATH_MAX + 1;
323 char *filebuf = CryptMemAlloc(bufsize);
325 if (filebuf)
327 struct dirent *entry;
328 while ((entry = readdir(dir)))
330 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
332 snprintf(filebuf, bufsize, "%s/%s", path, entry->d_name);
333 if (import_certs_from_path(filebuf, store, FALSE) && !ret)
334 ret = TRUE;
337 closedir(dir);
338 CryptMemFree(filebuf);
341 return ret;
344 /* Opens path, which may be a file or a directory, and imports any certificates
345 * it finds into store.
346 * Returns TRUE if any certificates were successfully imported.
348 static BOOL import_certs_from_path(LPCSTR path, HCERTSTORE store,
349 BOOL allow_dir)
351 BOOL ret = FALSE;
352 int fd;
354 TRACE("(%s, %p, %d)\n", debugstr_a(path), store, allow_dir);
356 fd = open(path, O_RDONLY);
357 if (fd != -1)
359 struct stat st;
361 if (fstat(fd, &st) == 0)
363 if (S_ISREG(st.st_mode))
364 ret = import_certs_from_file(fd, store);
365 else if (S_ISDIR(st.st_mode))
367 if (allow_dir)
368 ret = import_certs_from_dir(path, store);
369 else
370 WARN("%s is a directory and directories are disallowed\n",
371 debugstr_a(path));
373 else
374 ERR("%s: invalid file type\n", path);
376 close(fd);
378 return ret;
381 static BOOL WINAPI CRYPT_RootWriteCert(HCERTSTORE hCertStore,
382 PCCERT_CONTEXT cert, DWORD dwFlags)
384 /* The root store can't have certs added */
385 return FALSE;
388 static BOOL WINAPI CRYPT_RootDeleteCert(HCERTSTORE hCertStore,
389 PCCERT_CONTEXT cert, DWORD dwFlags)
391 /* The root store can't have certs deleted */
392 return FALSE;
395 static BOOL WINAPI CRYPT_RootWriteCRL(HCERTSTORE hCertStore,
396 PCCRL_CONTEXT crl, DWORD dwFlags)
398 /* The root store can have CRLs added. At worst, a malicious application
399 * can DoS itself, as the changes aren't persisted in any way.
401 return TRUE;
404 static BOOL WINAPI CRYPT_RootDeleteCRL(HCERTSTORE hCertStore,
405 PCCRL_CONTEXT crl, DWORD dwFlags)
407 /* The root store can't have CRLs deleted */
408 return FALSE;
411 static void *rootProvFuncs[] = {
412 NULL, /* CERT_STORE_PROV_CLOSE_FUNC */
413 NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
414 CRYPT_RootWriteCert,
415 CRYPT_RootDeleteCert,
416 NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
417 NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
418 CRYPT_RootWriteCRL,
419 CRYPT_RootDeleteCRL,
420 NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
421 NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
422 NULL, /* CERT_STORE_PROV_WRITE_CTL_FUNC */
423 NULL, /* CERT_STORE_PROV_DELETE_CTL_FUNC */
424 NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
425 NULL, /* CERT_STORE_PROV_CONTROL_FUNC */
428 static const char * const CRYPT_knownLocations[] = {
429 "/etc/ssl/certs/ca-certificates.crt",
430 "/etc/ssl/certs",
431 "/etc/pki/tls/certs/ca-bundle.crt",
434 /* Reads certificates from the list of known locations. Stops when any
435 * location contains any certificates, to prevent spending unnecessary time
436 * adding redundant certificates, e.g. when both a certificate bundle and
437 * individual certificates exist in the same directory.
439 static PWINECRYPT_CERTSTORE CRYPT_RootOpenStoreFromKnownLocations(void)
441 HCERTSTORE root = NULL;
442 HCERTSTORE from = CertOpenStore(CERT_STORE_PROV_MEMORY,
443 X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
444 HCERTSTORE to = CertOpenStore(CERT_STORE_PROV_MEMORY,
445 X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
447 if (from && to)
449 CERT_STORE_PROV_INFO provInfo = {
450 sizeof(CERT_STORE_PROV_INFO),
451 sizeof(rootProvFuncs) / sizeof(rootProvFuncs[0]),
452 rootProvFuncs,
453 NULL,
455 NULL
457 DWORD i;
458 BOOL ret = FALSE;
460 for (i = 0; !ret &&
461 i < sizeof(CRYPT_knownLocations) / sizeof(CRYPT_knownLocations[0]);
462 i++)
463 ret = import_certs_from_path(CRYPT_knownLocations[i], from, TRUE);
464 check_and_store_certs(from, to);
465 root = CRYPT_ProvCreateStore(0, to, &provInfo);
467 CertCloseStore(from, 0);
468 TRACE("returning %p\n", root);
469 return root;
472 static PWINECRYPT_CERTSTORE CRYPT_rootStore;
474 PWINECRYPT_CERTSTORE CRYPT_RootOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags)
476 TRACE("(%ld, %08x)\n", hCryptProv, dwFlags);
478 if (dwFlags & CERT_STORE_DELETE_FLAG)
480 WARN("root store can't be deleted\n");
481 SetLastError(ERROR_ACCESS_DENIED);
482 return NULL;
484 switch (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK)
486 case CERT_SYSTEM_STORE_LOCAL_MACHINE:
487 case CERT_SYSTEM_STORE_CURRENT_USER:
488 break;
489 default:
490 TRACE("location %08x unsupported\n",
491 dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
492 SetLastError(E_INVALIDARG);
493 return NULL;
495 if (!CRYPT_rootStore)
497 HCERTSTORE root = CRYPT_RootOpenStoreFromKnownLocations();
499 InterlockedCompareExchangePointer((PVOID *)&CRYPT_rootStore, root,
500 NULL);
501 if (CRYPT_rootStore != root)
502 CertCloseStore(root, 0);
504 CertDuplicateStore(CRYPT_rootStore);
505 return CRYPT_rootStore;
508 void root_store_free(void)
510 CertCloseStore(CRYPT_rootStore, 0);