KeyData.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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"
12 #include "security/Certificate.h"
13 #include "security/KeyData.h"
14 #include "SquidConfig.h"
15 #include "ssl/bio.h"
16 #include "ssl/gadgets.h"
17 
18 #include <algorithm>
19 
22 bool
24 {
25  debugs(83, 2, "from " << certFile);
26  cert.reset(); // paranoid: ensure cert is unset
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 
37  try {
39  debugs(83, DBG_PARSE_NOTE(2), "Loaded signing certificate: " << *cert);
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  }
47 
48  try {
49  // Squid sends `cert` (loaded above) followed by certificates in `chain`
50  // (formed below by loading and sorting the remaining certificates).
51 
52  // load all the remaining configured certificates
53  CertList candidates;
54  while (const auto c = Ssl::ReadOptionalCertificate(bio))
55  candidates.emplace_back(c);
56 
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();
62 
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;
68 
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);
79  }
80 
81  for (const auto &c: candidates)
82  debugs(83, DBG_IMPORTANT, "WARNING: Ignoring certificate that does not extend the chain: " << *c);
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 
90 #elif HAVE_LIBGNUTLS
91  const char *certFilename = certFile.c_str();
92  gnutls_datum_t data;
93  Security::LibErrorCode x = gnutls_load_file(certFilename, &data);
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) {
116  debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p);
117  gnutls_x509_crt_deinit(p);
118  });
119  }
120 
121  // XXX: implement chain loading
122  debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
123 
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 
138 bool
140 {
141  debugs(83, 2, "from " << privateKeyFile);
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 HAVE_LIBGNUTLS
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) {
167  debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p);
168  gnutls_x509_privkey_deinit(p);
169  });
170  }
171  }
172  gnutls_free(data.data);
173 
174 #else
175  // nothing to do.
176 #endif
177 
178  return bool(pkey);
179 }
180 
181 void
183 {
184  char buf[128];
185  if (!loadCertificates()) {
186  debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing certificate in '" << certFile << "'");
187  return;
188  }
189 
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 }
197 
bool loadCertificates()
Definition: KeyData.cc:23
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:131
Security::CertPointer ReadCertificate(const BIO_Pointer &)
Definition: gadgets.cc:816
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition: forward.h:88
std::unique_ptr< BIO, HardFun< void, BIO *, &BIO_vfree > > BIO_Pointer
Definition: gadgets.h:57
struct SquidConfig::@90 Program
Security::CertPointer cert
public X.509 certificate from certFile
Definition: KeyData.h:31
void reset()
Forget the raw pointer - unlock if any value was set. Become a nil pointer.
static int port
Definition: ldap_backend.cc:70
Security::CertPointer ReadOptionalCertificate(const BIO_Pointer &)
Definition: gadgets.cc:791
void loadFromFiles(const AnyP::PortCfg &, const char *portType)
load the contents of certFile and privateKeyFile into memory cert, pkey and chain
Definition: KeyData.cc:182
#define DBG_PARSE_NOTE(x)
Definition: Stream.h:42
Security::CertList chain
any certificates which must be chained from cert
Definition: KeyData.h:35
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
const char * c_str()
Definition: SBuf.cc:516
static std::ostream & Extra(std::ostream &)
Definition: debug.cc:1316
void ReadPrivateKeyFromFile(char const *keyFilename, Security::PrivateKeyPointer &pkey, pem_password_cb *passwd_callback)
Definition: gadgets.cc:837
bool SelfSigned(Certificate &c)
Whether the given certificate is self-signed.
Definition: Certificate.h:34
int AskPasswordCb(char *buf, int size, int rwflag, void *userdata)
Definition: support.cc:126
std::list< Security::CertPointer > CertList
Definition: forward.h:105
bool loadX509PrivateKeyFromFile()
Definition: KeyData.cc:139
char * ssl_password
Definition: SquidConfig.h:211
#define DBG_IMPORTANT
Definition: Stream.h:38
SBuf certFile
path of file containing PEM format X.509 certificate
Definition: KeyData.h:27
const char * ErrorString(const LibErrorCode code)
converts numeric LibErrorCode into a human-friendlier string
Definition: forward.h:152
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
class SquidConfig Config
Definition: SquidConfig.cc:12
unsigned long LibErrorCode
TLS library-reported non-validation error.
Definition: forward.h:141

 

Introduction

Documentation

Support

Miscellaneous