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"
15 #include "ssl/gadgets.h"
18 * Read certificate from file.
19 * See also: Ssl::ReadX509Certificate function, gadgets.cc file
22 Security::KeyData::loadX509CertFromFile()
24 debugs(83, DBG_IMPORTANT
, "Using certificate in " << certFile
);
25 cert
.reset(); // paranoid: ensure cert is unset
28 const char *certFilename
= certFile
.c_str();
29 Ssl::BIO_Pointer
bio(BIO_new(BIO_s_file()));
30 if (!bio
|| !BIO_read_filename(bio
.get(), certFilename
)) {
31 const auto x
= ERR_get_error();
32 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "': " << ErrorString(x
));
36 cert
= Ssl::ReadX509Certificate(bio
); // error detected/reported below
39 const char *certFilename
= certFile
.c_str();
41 Security::LibErrorCode x
= gnutls_load_file(certFilename
, &data
);
42 if (x
!= GNUTLS_E_SUCCESS
) {
43 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate file '" << certFile
<< "': " << ErrorString(x
));
48 x
= gnutls_pcert_import_x509_raw(&pcrt
, &data
, GNUTLS_X509_FMT_PEM
, 0);
49 if (x
!= GNUTLS_E_SUCCESS
) {
50 debugs(83, DBG_IMPORTANT
, "ERROR: unable to import certificate from '" << certFile
<< "': " << ErrorString(x
));
53 gnutls_free(data
.data
);
55 gnutls_x509_crt_t certificate
;
56 x
= gnutls_pcert_export_x509(&pcrt
, &certificate
);
57 if (x
!= GNUTLS_E_SUCCESS
) {
58 debugs(83, DBG_IMPORTANT
, "ERROR: unable to X.509 convert certificate from '" << certFile
<< "': " << ErrorString(x
));
63 cert
= Security::CertPointer(certificate
, [](gnutls_x509_crt_t p
) {
64 debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p
);
65 gnutls_x509_crt_deinit(p
);
74 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load certificate from '" << certFile
<< "'");
81 * Read certificate from file.
82 * See also: Ssl::ReadX509Certificate function, gadgets.cc file
85 Security::KeyData::loadX509ChainFromFile()
88 const char *certFilename
= certFile
.c_str();
89 Ssl::BIO_Pointer
bio(BIO_new(BIO_s_file()));
90 if (!bio
|| !BIO_read_filename(bio
.get(), certFilename
)) {
91 const auto x
= ERR_get_error();
92 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load chain file '" << certFile
<< "': " << ErrorString(x
));
96 #if TLS_CHAIN_NO_SELFSIGNED // ignore self-signed certs in the chain
97 if (X509_check_issued(cert
.get(), cert
.get()) == X509_V_OK
) {
98 char *nameStr
= X509_NAME_oneline(X509_get_subject_name(cert
.get()), nullptr, 0);
99 debugs(83, DBG_PARSE_NOTE(2), "Certificate is self-signed, will not be chained: " << nameStr
);
100 OPENSSL_free(nameStr
);
104 debugs(83, DBG_PARSE_NOTE(3), "Using certificate chain in " << certFile
);
105 // and add to the chain any other certificate exist in the file
106 CertPointer latestCert
= cert
;
108 while (const auto ca
= Ssl::ReadX509Certificate(bio
)) {
109 // get Issuer name of the cert for debug display
110 char *nameStr
= X509_NAME_oneline(X509_get_subject_name(ca
.get()), nullptr, 0);
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 (X509_check_issued(ca
.get(), ca
.get()) == X509_V_OK
) {
115 debugs(83, DBG_PARSE_NOTE(2), "CA " << nameStr
<< " is self-signed, will not be chained: " << nameStr
);
116 OPENSSL_free(nameStr
);
120 // checks that the chained certs are actually part of a chain for validating cert
121 const auto checkCode
= X509_check_issued(ca
.get(), latestCert
.get());
122 if (checkCode
== X509_V_OK
) {
123 debugs(83, DBG_PARSE_NOTE(3), "Adding issuer CA: " << nameStr
);
124 // OpenSSL API requires that we order certificates such that the
125 // chain can be appended directly into the on-wire traffic.
126 latestCert
= CertPointer(ca
);
127 chain
.emplace_back(latestCert
);
129 debugs(83, DBG_PARSE_NOTE(2), certFile
<< ": Ignoring non-issuer CA " << nameStr
<< ": " << X509_verify_cert_error_string(checkCode
) << " (" << checkCode
<< ")");
131 OPENSSL_free(nameStr
);
136 // XXX: implement chain loading
137 debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
145 * Read X.509 private key from file.
148 Security::KeyData::loadX509PrivateKeyFromFile()
150 debugs(83, DBG_IMPORTANT
, "Using key in " << privateKeyFile
);
153 const char *keyFilename
= privateKeyFile
.c_str();
154 // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
155 // so this may not fully work iff Config.Program.ssl_password is set.
156 pem_password_cb
*cb
= ::Config
.Program
.ssl_password
? &Ssl::AskPasswordCb
: nullptr;
157 Ssl::ReadPrivateKeyFromFile(keyFilename
, pkey
, cb
);
159 if (pkey
&& !X509_check_private_key(cert
.get(), pkey
.get())) {
160 debugs(83, DBG_IMPORTANT
, "WARNING: '" << privateKeyFile
<< "' X509_check_private_key() failed");
165 const char *keyFilename
= privateKeyFile
.c_str();
167 if (gnutls_load_file(keyFilename
, &data
) == GNUTLS_E_SUCCESS
) {
168 gnutls_privkey_t key
;
169 (void)gnutls_privkey_init(&key
);
170 Security::ErrorCode x
= gnutls_privkey_import_x509_raw(key
, &data
, GNUTLS_X509_FMT_PEM
, nullptr, 0);
171 if (x
== GNUTLS_E_SUCCESS
) {
172 gnutls_x509_privkey_t xkey
;
173 gnutls_privkey_export_x509(key
, &xkey
);
174 gnutls_privkey_deinit(key
);
175 pkey
= Security::PrivateKeyPointer(xkey
, [](gnutls_x509_privkey_t p
) {
176 debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p
);
177 gnutls_x509_privkey_deinit(p
);
181 gnutls_free(data
.data
);
191 Security::KeyData::loadFromFiles(const AnyP::PortCfg
&port
, const char *portType
)
194 if (!loadX509CertFromFile()) {
195 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing certificate in '" << certFile
<< "'");
199 // certificate chain in the PEM file is optional
200 loadX509ChainFromFile();
202 // pkey is mandatory, not having it makes cert and chain pointless.
203 if (!loadX509PrivateKeyFromFile()) {
204 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing private key in '" << privateKeyFile
<< "'");