]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/KeyData.cc
Bug 5343: Fix GCC v14 not finding std::find() (#1672)
[thirdparty/squid.git] / src / security / KeyData.cc
CommitLineData
51e09c08 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
51e09c08
AJ
3 *
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.
7 */
8
9#include "squid.h"
10#include "anyp/PortCfg.h"
11#include "fatal.h"
907831e6 12#include "security/Certificate.h"
51e09c08
AJ
13#include "security/KeyData.h"
14#include "SquidConfig.h"
15#include "ssl/bio.h"
b05c1954 16#include "ssl/gadgets.h"
51e09c08 17
0002264b
FC
18#include <algorithm>
19
c25648fb
AR
20/// load the signing certificate and its chain, if any, from certFile
21/// \return true if the signing certificate was obtained
51e09c08 22bool
c25648fb 23Security::KeyData::loadCertificates()
51e09c08 24{
9259c796 25 debugs(83, 2, "from " << certFile);
1700fab7 26 cert.reset(); // paranoid: ensure cert is unset
51e09c08
AJ
27
28#if USE_OPENSSL
29 const char *certFilename = certFile.c_str();
30 Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
31 if (!bio || !BIO_read_filename(bio.get(), certFilename)) {
32 const auto x = ERR_get_error();
33 debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate file '" << certFile << "': " << ErrorString(x));
34 return false;
35 }
36
ac1b16b5
AR
37 try {
38 cert = Ssl::ReadCertificate(bio);
c25648fb 39 debugs(83, DBG_PARSE_NOTE(2), "Loaded signing certificate: " << *cert);
ac1b16b5
AR
40 }
41 catch (...) {
42 // TODO: Convert the rest of this method to throw on errors instead.
43 debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate file '" << certFile << "':" <<
44 Debug::Extra << "problem: " << CurrentException);
45 return false;
46 }
51e09c08 47
c25648fb 48 try {
53eede78
AR
49 // Squid sends `cert` (loaded above) followed by certificates in `chain`
50 // (formed below by loading and sorting the remaining certificates).
c25648fb 51
53eede78
AR
52 // load all the remaining configured certificates
53 CertList candidates;
54 while (const auto c = Ssl::ReadOptionalCertificate(bio))
55 candidates.emplace_back(c);
c25648fb 56
53eede78
AR
57 // Push certificates into `chain` in on-the-wire order, as defined by
58 // RFC 8446 Section 4.4.2: "Each following certificate SHOULD directly
59 // certify the one immediately preceding it."
60 while (!candidates.empty()) {
61 const auto precedingCert = chain.empty() ? cert : chain.back();
c25648fb 62
53eede78
AR
63 // We cannot chain any certificate after a self-signed certificate.
64 // This check also protects the IssuedBy() search below from adding
65 // duplicated (i.e. listed multiple times) self-signed certificates.
66 if (SelfSigned(*precedingCert))
67 break;
c25648fb 68
53eede78
AR
69 const auto issuerPos = std::find_if(candidates.begin(), candidates.end(), [&](const CertPointer &i) {
70 return IssuedBy(*precedingCert, *i);
71 });
72 if (issuerPos == candidates.end())
73 break;
74
75 const auto &issuer = *issuerPos;
76 debugs(83, DBG_PARSE_NOTE(3), "Adding CA certificate: " << *issuer);
77 chain.emplace_back(issuer);
78 candidates.erase(issuerPos);
c25648fb 79 }
53eede78
AR
80
81 for (const auto &c: candidates)
82 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring certificate that does not extend the chain: " << *c);
c25648fb
AR
83 }
84 catch (...) {
85 // TODO: Reject configs with malformed intermediate certs instead.
86 debugs(83, DBG_IMPORTANT, "ERROR: Failure while loading intermediate certificate(s) from '" << certFile << "':" <<
87 Debug::Extra << "problem: " << CurrentException);
88 }
89
51e09c08
AJ
90#elif USE_GNUTLS
91 const char *certFilename = certFile.c_str();
92 gnutls_datum_t data;
83b053a0 93 Security::LibErrorCode x = gnutls_load_file(certFilename, &data);
51e09c08
AJ
94 if (x != GNUTLS_E_SUCCESS) {
95 debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate file '" << certFile << "': " << ErrorString(x));
96 return false;
97 }
98
99 gnutls_pcert_st pcrt;
100 x = gnutls_pcert_import_x509_raw(&pcrt, &data, GNUTLS_X509_FMT_PEM, 0);
101 if (x != GNUTLS_E_SUCCESS) {
102 debugs(83, DBG_IMPORTANT, "ERROR: unable to import certificate from '" << certFile << "': " << ErrorString(x));
103 return false;
104 }
105 gnutls_free(data.data);
106
107 gnutls_x509_crt_t certificate;
108 x = gnutls_pcert_export_x509(&pcrt, &certificate);
109 if (x != GNUTLS_E_SUCCESS) {
110 debugs(83, DBG_IMPORTANT, "ERROR: unable to X.509 convert certificate from '" << certFile << "': " << ErrorString(x));
111 return false;
112 }
113
114 if (certificate) {
115 cert = Security::CertPointer(certificate, [](gnutls_x509_crt_t p) {
f3a5731a 116 debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p);
117 gnutls_x509_crt_deinit(p);
118 });
51e09c08
AJ
119 }
120
c25648fb
AR
121 // XXX: implement chain loading
122 debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
123
51e09c08
AJ
124#else
125 // do nothing.
126#endif
127
128 if (!cert) {
129 debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate from '" << certFile << "'");
130 }
131
132 return bool(cert);
133}
134
51e09c08
AJ
135/**
136 * Read X.509 private key from file.
137 */
138bool
139Security::KeyData::loadX509PrivateKeyFromFile()
140{
9259c796 141 debugs(83, 2, "from " << privateKeyFile);
51e09c08
AJ
142
143#if USE_OPENSSL
144 const char *keyFilename = privateKeyFile.c_str();
145 // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
146 // so this may not fully work iff Config.Program.ssl_password is set.
147 pem_password_cb *cb = ::Config.Program.ssl_password ? &Ssl::AskPasswordCb : nullptr;
148 Ssl::ReadPrivateKeyFromFile(keyFilename, pkey, cb);
149
150 if (pkey && !X509_check_private_key(cert.get(), pkey.get())) {
151 debugs(83, DBG_IMPORTANT, "WARNING: '" << privateKeyFile << "' X509_check_private_key() failed");
152 pkey.reset();
153 }
154
155#elif USE_GNUTLS
156 const char *keyFilename = privateKeyFile.c_str();
157 gnutls_datum_t data;
158 if (gnutls_load_file(keyFilename, &data) == GNUTLS_E_SUCCESS) {
159 gnutls_privkey_t key;
160 (void)gnutls_privkey_init(&key);
161 Security::ErrorCode x = gnutls_privkey_import_x509_raw(key, &data, GNUTLS_X509_FMT_PEM, nullptr, 0);
162 if (x == GNUTLS_E_SUCCESS) {
163 gnutls_x509_privkey_t xkey;
164 gnutls_privkey_export_x509(key, &xkey);
165 gnutls_privkey_deinit(key);
166 pkey = Security::PrivateKeyPointer(xkey, [](gnutls_x509_privkey_t p) {
f3a5731a 167 debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p);
168 gnutls_x509_privkey_deinit(p);
169 });
51e09c08
AJ
170 }
171 }
172 gnutls_free(data.data);
173
174#else
175 // nothing to do.
176#endif
177
178 return bool(pkey);
179}
180
181void
182Security::KeyData::loadFromFiles(const AnyP::PortCfg &port, const char *portType)
183{
184 char buf[128];
c25648fb 185 if (!loadCertificates()) {
51e09c08
AJ
186 debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing certificate in '" << certFile << "'");
187 return;
188 }
189
51e09c08
AJ
190 // pkey is mandatory, not having it makes cert and chain pointless.
191 if (!loadX509PrivateKeyFromFile()) {
192 debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing private key in '" << privateKeyFile << "'");
193 cert.reset();
194 chain.clear();
195 }
196}
f3a5731a 197