2 * Copyright (C) 1996-2021 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/KeyData.h"
13 #include "SquidConfig.h"
17 * Read certificate from file.
18 * See also: Ssl::ReadX509Certificate function, gadgets.cc file
21 Security::KeyData::loadX509CertFromFile()
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
));
35 if (X509
*certificate
= PEM_read_bio_X509(bio
.get(), nullptr, nullptr, nullptr)) {
36 cert
.resetWithoutLocking(certificate
);
40 const char *certFilename
= certFile
.c_str();
42 Security::LibErrorCode x
= gnutls_load_file(certFilename
, &data
);
43 if (x
!= GNUTLS_E_SUCCESS
) {
44 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "': " << ErrorString(x
));
49 x
= gnutls_pcert_import_x509_raw(&pcrt
, &data
, GNUTLS_X509_FMT_PEM
, 0);
50 if (x
!= GNUTLS_E_SUCCESS
) {
51 debugs(83, DBG_IMPORTANT
, "ERROR: unable to import certificate from '" << certFile
<< "': " << ErrorString(x
));
54 gnutls_free(data
.data
);
56 gnutls_x509_crt_t certificate
;
57 x
= gnutls_pcert_export_x509(&pcrt
, &certificate
);
58 if (x
!= GNUTLS_E_SUCCESS
) {
59 debugs(83, DBG_IMPORTANT
, "ERROR: unable to X.509 convert certificate from '" << certFile
<< "': " << ErrorString(x
));
64 cert
= Security::CertPointer(certificate
, [](gnutls_x509_crt_t p
) {
65 debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p
);
66 gnutls_x509_crt_deinit(p
);
75 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate from '" << certFile
<< "'");
82 * Read certificate from file.
83 * See also: Ssl::ReadX509Certificate function, gadgets.cc file
86 Security::KeyData::loadX509ChainFromFile()
89 const char *certFilename
= certFile
.c_str();
90 Ssl::BIO_Pointer
bio(BIO_new(BIO_s_file()));
91 if (!bio
|| !BIO_read_filename(bio
.get(), certFilename
)) {
92 const auto x
= ERR_get_error();
93 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load chain file '" << certFile
<< "': " << ErrorString(x
));
97 #if TLS_CHAIN_NO_SELFSIGNED // ignore self-signed certs in the chain
98 if (X509_check_issued(cert
.get(), cert
.get()) == X509_V_OK
) {
99 char *nameStr
= X509_NAME_oneline(X509_get_subject_name(cert
.get()), nullptr, 0);
100 debugs(83, DBG_PARSE_NOTE(2), "Certificate is self-signed, will not be chained: " << nameStr
);
101 OPENSSL_free(nameStr
);
105 debugs(83, DBG_PARSE_NOTE(3), "Using certificate chain in " << certFile
);
106 // and add to the chain any other certificate exist in the file
107 CertPointer latestCert
= cert
;
109 while (auto ca
= PEM_read_bio_X509(bio
.get(), nullptr, nullptr, nullptr)) {
110 // get Issuer name of the cert for debug display
111 char *nameStr
= X509_NAME_oneline(X509_get_subject_name(ca
), nullptr, 0);
113 #if TLS_CHAIN_NO_SELFSIGNED // ignore self-signed certs in the chain
114 // self-signed certificates are not valid in a sent chain
115 if (X509_check_issued(ca
, ca
) == X509_V_OK
) {
116 debugs(83, DBG_PARSE_NOTE(2), "CA " << nameStr
<< " is self-signed, will not be chained: " << nameStr
);
117 OPENSSL_free(nameStr
);
121 // checks that the chained certs are actually part of a chain for validating cert
122 const auto checkCode
= X509_check_issued(ca
, latestCert
.get());
123 if (checkCode
== X509_V_OK
) {
124 debugs(83, DBG_PARSE_NOTE(3), "Adding issuer CA: " << nameStr
);
125 // OpenSSL API requires that we order certificates such that the
126 // chain can be appended directly into the on-wire traffic.
127 latestCert
= CertPointer(ca
);
128 chain
.emplace_front(latestCert
);
130 debugs(83, DBG_PARSE_NOTE(2), certFile
<< ": Ignoring non-issuer CA " << nameStr
<< ": " << X509_verify_cert_error_string(checkCode
) << " (" << checkCode
<< ")");
132 OPENSSL_free(nameStr
);
137 // XXX: implement chain loading
138 debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
146 * Read X.509 private key from file.
149 Security::KeyData::loadX509PrivateKeyFromFile()
151 debugs(83, DBG_IMPORTANT
, "Using key in " << privateKeyFile
);
154 const char *keyFilename
= privateKeyFile
.c_str();
155 // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
156 // so this may not fully work iff Config.Program.ssl_password is set.
157 pem_password_cb
*cb
= ::Config
.Program
.ssl_password
? &Ssl::AskPasswordCb
: nullptr;
158 Ssl::ReadPrivateKeyFromFile(keyFilename
, pkey
, cb
);
160 if (pkey
&& !X509_check_private_key(cert
.get(), pkey
.get())) {
161 debugs(83, DBG_IMPORTANT
, "WARNING: '" << privateKeyFile
<< "' X509_check_private_key() failed");
166 const char *keyFilename
= privateKeyFile
.c_str();
168 if (gnutls_load_file(keyFilename
, &data
) == GNUTLS_E_SUCCESS
) {
169 gnutls_privkey_t key
;
170 (void)gnutls_privkey_init(&key
);
171 Security::ErrorCode x
= gnutls_privkey_import_x509_raw(key
, &data
, GNUTLS_X509_FMT_PEM
, nullptr, 0);
172 if (x
== GNUTLS_E_SUCCESS
) {
173 gnutls_x509_privkey_t xkey
;
174 gnutls_privkey_export_x509(key
, &xkey
);
175 gnutls_privkey_deinit(key
);
176 pkey
= Security::PrivateKeyPointer(xkey
, [](gnutls_x509_privkey_t p
) {
177 debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p
);
178 gnutls_x509_privkey_deinit(p
);
182 gnutls_free(data
.data
);
192 Security::KeyData::loadFromFiles(const AnyP::PortCfg
&port
, const char *portType
)
195 if (!loadX509CertFromFile()) {
196 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing certificate in '" << certFile
<< "'");
200 // certificate chain in the PEM file is optional
201 loadX509ChainFromFile();
203 // pkey is mandatory, not having it makes cert and chain pointless.
204 if (!loadX509PrivateKeyFromFile()) {
205 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing private key in '" << privateKeyFile
<< "'");