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
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
24 #ifdef HAVE_SYS_STAT_H
35 #define WIN32_NO_STATUS
41 #include "wine/debug.h"
42 #include "crypt32_private.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(crypt
);
46 #define INITIAL_CERT_BUFFER 1024
55 static inline void reset_buffer(struct DynamicBuffer
*buffer
)
58 if (buffer
->data
) buffer
->data
[0] = 0;
61 static BOOL
add_line_to_buffer(struct DynamicBuffer
*buffer
, LPCSTR line
)
65 if (buffer
->used
+ strlen(line
) + 1 > buffer
->allocated
)
67 if (!buffer
->allocated
)
69 buffer
->data
= CryptMemAlloc(INITIAL_CERT_BUFFER
);
73 buffer
->allocated
= INITIAL_CERT_BUFFER
;
78 DWORD new_size
= max(buffer
->allocated
* 2,
79 buffer
->used
+ strlen(line
) + 1);
81 buffer
->data
= CryptMemRealloc(buffer
->data
, new_size
);
83 buffer
->allocated
= new_size
;
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
);
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
)
107 BOOL in_cert
= FALSE
;
108 struct DynamicBuffer saved_cert
= { 0, 0, NULL
};
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");
121 reset_buffer(&saved_cert
);
123 else if (!strncmp(line
, trailer
, strlen(trailer
)))
127 TRACE("end of certificate, adding cert\n");
129 if (CryptStringToBinaryA((char *)saved_cert
.data
, saved_cert
.used
,
130 CRYPT_STRING_BASE64
, NULL
, &size
, NULL
, NULL
))
132 LPBYTE buf
= CryptMemAlloc(size
);
136 CryptStringToBinaryA((char *)saved_cert
.data
,
137 saved_cert
.used
, CRYPT_STRING_BASE64
, buf
, &size
, NULL
,
139 if (CertAddEncodedCertificateToStore(store
,
140 X509_ASN_ENCODING
, buf
, size
, CERT_STORE_ADD_NEW
, NULL
))
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];
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");
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
;
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
,
220 PCERT_RDN_ATTR commonName
= CertFindRDNAttr(szOID_COMMON_NAME
,
225 CertRDNValueToStrA(commonName
->dwValueType
,
226 &commonName
->Value
, buf
, sizeof(buf
));
234 static void check_and_store_certs(HCERTSTORE from
, HCERTSTORE to
)
236 PCCERT_CONTEXT cert
= NULL
;
237 DWORD root_count
= 0;
242 cert
= CertEnumCertificatesInStore(from
, cert
);
245 CERT_CHAIN_ENGINE_CONFIG chainEngineConfig
=
246 { sizeof(chainEngineConfig
), 0 };
247 HCERTCHAINENGINE engine
= CRYPT_CreateChainEngine(to
,
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
);
258 TRACE("rejecting %s: %s\n", get_cert_common_name(cert
),
259 "chain creation failed");
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
));
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
))
280 CertFreeCertificateChainEngine(engine
);
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
)
297 fp
= fdopen(fd
, "r");
300 ret
= import_base64_certs_from_fp(fp
, store
);
306 static BOOL
import_certs_from_path(LPCSTR path
, HCERTSTORE store
,
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
)
318 TRACE("(%s, %p)\n", debugstr_a(path
), store
);
323 size_t bufsize
= strlen(path
) + 1 + PATH_MAX
+ 1;
324 char *filebuf
= CryptMemAlloc(bufsize
);
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
)
339 CryptMemFree(filebuf
);
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
,
355 TRACE("(%s, %p, %d)\n", debugstr_a(path
), store
, allow_dir
);
357 fd
= open(path
, O_RDONLY
);
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
))
369 ret
= import_certs_from_dir(path
, store
);
371 WARN("%s is a directory and directories are disallowed\n",
375 ERR("%s: invalid file type\n", path
);
382 static BOOL WINAPI
CRYPT_RootWriteCert(HCERTSTORE hCertStore
,
383 PCCERT_CONTEXT cert
, DWORD dwFlags
)
385 /* The root store can't have certs added */
389 static BOOL WINAPI
CRYPT_RootDeleteCert(HCERTSTORE hCertStore
,
390 PCCERT_CONTEXT cert
, DWORD dwFlags
)
392 /* The root store can't have certs deleted */
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.
405 static BOOL WINAPI
CRYPT_RootDeleteCRL(HCERTSTORE hCertStore
,
406 PCCRL_CONTEXT crl
, DWORD dwFlags
)
408 /* The root store can't have CRLs deleted */
412 static void *rootProvFuncs
[] = {
413 NULL
, /* CERT_STORE_PROV_CLOSE_FUNC */
414 NULL
, /* CERT_STORE_PROV_READ_CERT_FUNC */
416 CRYPT_RootDeleteCert
,
417 NULL
, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
418 NULL
, /* CERT_STORE_PROV_READ_CRL_FUNC */
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",
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
);
450 CERT_STORE_PROV_INFO provInfo
= {
451 sizeof(CERT_STORE_PROV_INFO
),
452 sizeof(rootProvFuncs
) / sizeof(rootProvFuncs
[0]),
462 i
< sizeof(CRYPT_knownLocations
) / sizeof(CRYPT_knownLocations
[0]);
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
);
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
);
485 switch (dwFlags
& CERT_SYSTEM_STORE_LOCATION_MASK
)
487 case CERT_SYSTEM_STORE_LOCAL_MACHINE
:
488 case CERT_SYSTEM_STORE_CURRENT_USER
:
491 TRACE("location %08x unsupported\n",
492 dwFlags
& CERT_SYSTEM_STORE_LOCATION_MASK
);
493 SetLastError(E_INVALIDARG
);
496 if (!CRYPT_rootStore
)
498 HCERTSTORE root
= CRYPT_RootOpenStoreFromKnownLocations();
500 InterlockedCompareExchangePointer((PVOID
*)&CRYPT_rootStore
, root
,
502 if (CRYPT_rootStore
!= root
)
503 CertCloseStore(root
, 0);
505 CertDuplicateStore(CRYPT_rootStore
);
506 return CRYPT_rootStore
;