2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
10 #include "anyp/PortCfg.h"
12 #include "security/Certificate.h"
13 #include "security/KeyData.h"
14 #include "SquidConfig.h"
16 #include "ssl/gadgets.h"
18 /// load the signing certificate and its chain, if any, from certFile
19 /// \return true if the signing certificate was obtained
21 Security::KeyData::loadCertificates()
23 debugs(83, DBG_IMPORTANT
, "Using certificate in " << certFile
);
24 cert
.reset(); // paranoid: ensure cert is unset
27 const char *certFilename
= certFile
.c_str();
28 Ssl::BIO_Pointer
bio(BIO_new(BIO_s_file()));
29 if (!bio
|| !BIO_read_filename(bio
.get(), certFilename
)) {
30 const auto x
= ERR_get_error();
31 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "': " << ErrorString(x
));
36 cert
= Ssl::ReadCertificate(bio
);
37 debugs(83, DBG_PARSE_NOTE(2), "Loaded signing certificate: " << *cert
);
40 // TODO: Convert the rest of this method to throw on errors instead.
41 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "':" <<
42 Debug::Extra
<< "problem: " << CurrentException
);
47 // selected bundled certificates in sending order: wireCerts = cert + chain
48 CertList
wireCerts(1, cert
);
50 while (const auto bundledCert
= Ssl::ReadOptionalCertificate(bio
)) {
51 assert(!wireCerts
.empty()); // this->cert is there (at least)
53 // We cannot chain any certificate after a self-signed certificate. This
54 // check also protects the IssuedBy() check below from adding duplicated
55 // (i.e. listed multiple times in the bundle) self-signed certificates.
56 if (SelfSigned(*wireCerts
.back())) {
57 debugs(83, DBG_PARSE_NOTE(2), "WARNING: Ignoring certificate after a self-signed one: " << *bundledCert
);
58 continue; // ... but keep going to report all ignored certificates
61 // add the bundled certificate if it extends the chain further
62 if (IssuedBy(*wireCerts
.back(), *bundledCert
)) {
63 debugs(83, DBG_PARSE_NOTE(3), "Adding issuer CA: " << *bundledCert
);
64 // OpenSSL API requires that we order certificates such that the
65 // chain can be appended directly into the on-wire traffic.
66 chain
.emplace_back(bundledCert
);
67 wireCerts
.emplace_back(bundledCert
);
71 debugs(83, DBG_PARSE_NOTE(2), "WARNING: Ignoring certificate that does not extend the chain: " << *bundledCert
);
75 // TODO: Reject configs with malformed intermediate certs instead.
76 debugs(83, DBG_IMPORTANT
, "ERROR: Failure while loading intermediate certificate(s) from '" << certFile
<< "':" <<
77 Debug::Extra
<< "problem: " << CurrentException
);
81 const char *certFilename
= certFile
.c_str();
83 Security::LibErrorCode x
= gnutls_load_file(certFilename
, &data
);
84 if (x
!= GNUTLS_E_SUCCESS
) {
85 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "': " << ErrorString(x
));
90 x
= gnutls_pcert_import_x509_raw(&pcrt
, &data
, GNUTLS_X509_FMT_PEM
, 0);
91 if (x
!= GNUTLS_E_SUCCESS
) {
92 debugs(83, DBG_IMPORTANT
, "ERROR: unable to import certificate from '" << certFile
<< "': " << ErrorString(x
));
95 gnutls_free(data
.data
);
97 gnutls_x509_crt_t certificate
;
98 x
= gnutls_pcert_export_x509(&pcrt
, &certificate
);
99 if (x
!= GNUTLS_E_SUCCESS
) {
100 debugs(83, DBG_IMPORTANT
, "ERROR: unable to X.509 convert certificate from '" << certFile
<< "': " << ErrorString(x
));
105 cert
= Security::CertPointer(certificate
, [](gnutls_x509_crt_t p
) {
106 debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p
);
107 gnutls_x509_crt_deinit(p
);
111 // XXX: implement chain loading
112 debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
119 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate from '" << certFile
<< "'");
126 * Read X.509 private key from file.
129 Security::KeyData::loadX509PrivateKeyFromFile()
131 debugs(83, DBG_IMPORTANT
, "Using key in " << privateKeyFile
);
134 const char *keyFilename
= privateKeyFile
.c_str();
135 // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
136 // so this may not fully work iff Config.Program.ssl_password is set.
137 pem_password_cb
*cb
= ::Config
.Program
.ssl_password
? &Ssl::AskPasswordCb
: nullptr;
138 Ssl::ReadPrivateKeyFromFile(keyFilename
, pkey
, cb
);
140 if (pkey
&& !X509_check_private_key(cert
.get(), pkey
.get())) {
141 debugs(83, DBG_IMPORTANT
, "WARNING: '" << privateKeyFile
<< "' X509_check_private_key() failed");
146 const char *keyFilename
= privateKeyFile
.c_str();
148 if (gnutls_load_file(keyFilename
, &data
) == GNUTLS_E_SUCCESS
) {
149 gnutls_privkey_t key
;
150 (void)gnutls_privkey_init(&key
);
151 Security::ErrorCode x
= gnutls_privkey_import_x509_raw(key
, &data
, GNUTLS_X509_FMT_PEM
, nullptr, 0);
152 if (x
== GNUTLS_E_SUCCESS
) {
153 gnutls_x509_privkey_t xkey
;
154 gnutls_privkey_export_x509(key
, &xkey
);
155 gnutls_privkey_deinit(key
);
156 pkey
= Security::PrivateKeyPointer(xkey
, [](gnutls_x509_privkey_t p
) {
157 debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p
);
158 gnutls_x509_privkey_deinit(p
);
162 gnutls_free(data
.data
);
172 Security::KeyData::loadFromFiles(const AnyP::PortCfg
&port
, const char *portType
)
175 if (!loadCertificates()) {
176 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing certificate in '" << certFile
<< "'");
180 // pkey is mandatory, not having it makes cert and chain pointless.
181 if (!loadX509PrivateKeyFromFile()) {
182 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing private key in '" << privateKeyFile
<< "'");