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 a signing certificate from certFile
20 Security::KeyData::loadX509CertFromFile()
22 debugs(83, DBG_IMPORTANT
, "Using certificate in " << certFile
);
23 cert
.reset(); // paranoid: ensure cert is unset
26 const char *certFilename
= certFile
.c_str();
27 Ssl::BIO_Pointer
bio(BIO_new(BIO_s_file()));
28 if (!bio
|| !BIO_read_filename(bio
.get(), certFilename
)) {
29 const auto x
= ERR_get_error();
30 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "': " << ErrorString(x
));
35 cert
= Ssl::ReadCertificate(bio
);
39 // TODO: Convert the rest of this method to throw on errors instead.
40 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "':" <<
41 Debug::Extra
<< "problem: " << CurrentException
);
46 const char *certFilename
= certFile
.c_str();
48 Security::LibErrorCode x
= gnutls_load_file(certFilename
, &data
);
49 if (x
!= GNUTLS_E_SUCCESS
) {
50 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "': " << ErrorString(x
));
55 x
= gnutls_pcert_import_x509_raw(&pcrt
, &data
, GNUTLS_X509_FMT_PEM
, 0);
56 if (x
!= GNUTLS_E_SUCCESS
) {
57 debugs(83, DBG_IMPORTANT
, "ERROR: unable to import certificate from '" << certFile
<< "': " << ErrorString(x
));
60 gnutls_free(data
.data
);
62 gnutls_x509_crt_t certificate
;
63 x
= gnutls_pcert_export_x509(&pcrt
, &certificate
);
64 if (x
!= GNUTLS_E_SUCCESS
) {
65 debugs(83, DBG_IMPORTANT
, "ERROR: unable to X.509 convert certificate from '" << certFile
<< "': " << ErrorString(x
));
70 cert
= Security::CertPointer(certificate
, [](gnutls_x509_crt_t p
) {
71 debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p
);
72 gnutls_x509_crt_deinit(p
);
81 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate from '" << certFile
<< "'");
87 /// load any intermediate certs that form the chain with the loaded signing cert
89 Security::KeyData::loadX509ChainFromFile()
92 const char *certFilename
= certFile
.c_str();
93 Ssl::BIO_Pointer
bio(BIO_new(BIO_s_file()));
94 if (!bio
|| !BIO_read_filename(bio
.get(), certFilename
)) {
95 const auto x
= ERR_get_error();
96 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load chain file '" << certFile
<< "': " << ErrorString(x
));
100 #if TLS_CHAIN_NO_SELFSIGNED // ignore self-signed certs in the chain
101 if (SelfSigned(*cert
)) {
102 debugs(83, DBG_PARSE_NOTE(2), "Certificate is self-signed, will not be chained: " << *cert
);
106 debugs(83, DBG_PARSE_NOTE(3), "Using certificate chain in " << certFile
);
107 // and add to the chain any other certificate exist in the file
108 CertPointer latestCert
= cert
;
110 while (const auto ca
= Ssl::ReadOptionalCertificate(bio
)) {
112 #if TLS_CHAIN_NO_SELFSIGNED // ignore self-signed certs in the chain
113 // self-signed certificates are not valid in a sent chain
114 if (SelfSigned(*ca
)) {
115 debugs(83, DBG_PARSE_NOTE(2), "CA certificate is self-signed, will not be chained: " << *ca
);
119 // checks that the chained certs are actually part of a chain for validating cert
120 if (IssuedBy(*latestCert
, *ca
)) {
121 debugs(83, DBG_PARSE_NOTE(3), "Adding issuer CA: " << *ca
);
122 // OpenSSL API requires that we order certificates such that the
123 // chain can be appended directly into the on-wire traffic.
124 latestCert
= CertPointer(ca
);
125 chain
.emplace_back(latestCert
);
127 debugs(83, DBG_PARSE_NOTE(2), certFile
<< ": Ignoring non-issuer CA " << *ca
);
133 // XXX: implement chain loading
134 debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
142 * Read X.509 private key from file.
145 Security::KeyData::loadX509PrivateKeyFromFile()
147 debugs(83, DBG_IMPORTANT
, "Using key in " << privateKeyFile
);
150 const char *keyFilename
= privateKeyFile
.c_str();
151 // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
152 // so this may not fully work iff Config.Program.ssl_password is set.
153 pem_password_cb
*cb
= ::Config
.Program
.ssl_password
? &Ssl::AskPasswordCb
: nullptr;
154 Ssl::ReadPrivateKeyFromFile(keyFilename
, pkey
, cb
);
156 if (pkey
&& !X509_check_private_key(cert
.get(), pkey
.get())) {
157 debugs(83, DBG_IMPORTANT
, "WARNING: '" << privateKeyFile
<< "' X509_check_private_key() failed");
162 const char *keyFilename
= privateKeyFile
.c_str();
164 if (gnutls_load_file(keyFilename
, &data
) == GNUTLS_E_SUCCESS
) {
165 gnutls_privkey_t key
;
166 (void)gnutls_privkey_init(&key
);
167 Security::ErrorCode x
= gnutls_privkey_import_x509_raw(key
, &data
, GNUTLS_X509_FMT_PEM
, nullptr, 0);
168 if (x
== GNUTLS_E_SUCCESS
) {
169 gnutls_x509_privkey_t xkey
;
170 gnutls_privkey_export_x509(key
, &xkey
);
171 gnutls_privkey_deinit(key
);
172 pkey
= Security::PrivateKeyPointer(xkey
, [](gnutls_x509_privkey_t p
) {
173 debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p
);
174 gnutls_x509_privkey_deinit(p
);
178 gnutls_free(data
.data
);
188 Security::KeyData::loadFromFiles(const AnyP::PortCfg
&port
, const char *portType
)
191 if (!loadX509CertFromFile()) {
192 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing certificate in '" << certFile
<< "'");
196 // certificate chain in the PEM file is optional
198 loadX509ChainFromFile();
201 // XXX: Reject malformed configurations by letting exceptions propagate.
202 debugs(83, DBG_CRITICAL
, "ERROR: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' cannot load intermediate certificates from '" << certFile
<< "':" <<
203 Debug::Extra
<< "problem: " << CurrentException
);
206 // pkey is mandatory, not having it makes cert and chain pointless.
207 if (!loadX509PrivateKeyFromFile()) {
208 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing private key in '" << privateKeyFile
<< "'");