2 * Copyright (C) 1996-2018 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::ErrorCode 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 // XXX: This BIO loads the public cert as first chain cert,
90 // so the code appending chains sends it twice in handshakes.
91 const char *certFilename
= certFile
.c_str();
92 Ssl::BIO_Pointer
bio(BIO_new(BIO_s_file()));
93 if (!bio
|| !BIO_read_filename(bio
.get(), certFilename
)) {
94 const auto x
= ERR_get_error();
95 debugs(83, DBG_IMPORTANT
, "ERROR: unable to load chain file '" << certFile
<< "': " << ErrorString(x
));
99 if (X509_check_issued(cert
.get(), cert
.get()) == X509_V_OK
)
100 debugs(83, 5, "Certificate is self-signed, will not be chained");
102 // and add to the chain any other certificate exist in the file
103 while (X509
*ca
= PEM_read_bio_X509(bio
.get(), nullptr, nullptr, nullptr)) {
104 // XXX: self-signed check should be applied to all certs loaded.
105 // XXX: missing checks that the chained certs are actually part of a chain for validating cert.
106 chain
.emplace_front(Security::CertPointer(ca
));
111 // XXX: implement chain loading
112 debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
120 * Read X.509 private key from file.
123 Security::KeyData::loadX509PrivateKeyFromFile()
125 debugs(83, DBG_IMPORTANT
, "Using key in " << privateKeyFile
);
128 const char *keyFilename
= privateKeyFile
.c_str();
129 // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
130 // so this may not fully work iff Config.Program.ssl_password is set.
131 pem_password_cb
*cb
= ::Config
.Program
.ssl_password
? &Ssl::AskPasswordCb
: nullptr;
132 Ssl::ReadPrivateKeyFromFile(keyFilename
, pkey
, cb
);
134 if (pkey
&& !X509_check_private_key(cert
.get(), pkey
.get())) {
135 debugs(83, DBG_IMPORTANT
, "WARNING: '" << privateKeyFile
<< "' X509_check_private_key() failed");
140 const char *keyFilename
= privateKeyFile
.c_str();
142 if (gnutls_load_file(keyFilename
, &data
) == GNUTLS_E_SUCCESS
) {
143 gnutls_privkey_t key
;
144 (void)gnutls_privkey_init(&key
);
145 Security::ErrorCode x
= gnutls_privkey_import_x509_raw(key
, &data
, GNUTLS_X509_FMT_PEM
, nullptr, 0);
146 if (x
== GNUTLS_E_SUCCESS
) {
147 gnutls_x509_privkey_t xkey
;
148 gnutls_privkey_export_x509(key
, &xkey
);
149 gnutls_privkey_deinit(key
);
150 pkey
= Security::PrivateKeyPointer(xkey
, [](gnutls_x509_privkey_t p
) {
151 debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p
);
152 gnutls_x509_privkey_deinit(p
);
156 gnutls_free(data
.data
);
166 Security::KeyData::loadFromFiles(const AnyP::PortCfg
&port
, const char *portType
)
169 if (!loadX509CertFromFile()) {
170 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing certificate in '" << certFile
<< "'");
174 // certificate chain in the PEM file is optional
175 loadX509ChainFromFile();
177 // pkey is mandatory, not having it makes cert and chain pointless.
178 if (!loadX509PrivateKeyFromFile()) {
179 debugs(83, DBG_IMPORTANT
, "WARNING: '" << portType
<< "_port " << port
.s
.toUrl(buf
, sizeof(buf
)) << "' missing private key in '" << privateKeyFile
<< "'");