KeyData.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 
20 bool
22 {
23  debugs(83, DBG_IMPORTANT, "Using certificate in " << certFile);
24  cert.reset(); // paranoid: ensure cert is unset
25 
26 #if USE_OPENSSL
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));
32  return false;
33  }
34 
35  try {
37  debugs(83, DBG_PARSE_NOTE(2), "Loaded signing certificate: " << *cert);
38  }
39  catch (...) {
40  // TODO: Convert the rest of this method to throw on errors instead.
41  debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate file '" << certFile << "':" <<
42  Debug::Extra << "problem: " << CurrentException);
43  return false;
44  }
45 
46  try {
47  // selected bundled certificates in sending order: wireCerts = cert + chain
48  CertList wireCerts(1, cert);
49 
50  while (const auto bundledCert = Ssl::ReadOptionalCertificate(bio)) {
51  assert(!wireCerts.empty()); // this->cert is there (at least)
52 
53  // We cannot chain any certificate after a self-signed certificate. This
54  // check also protects the IssuedBy() check below from adding duplicated
55  // (i.e. listed multiple times in the bundle) self-signed certificates.
56  if (SelfSigned(*wireCerts.back())) {
57  debugs(83, DBG_PARSE_NOTE(2), "WARNING: Ignoring certificate after a self-signed one: " << *bundledCert);
58  continue; // ... but keep going to report all ignored certificates
59  }
60 
61  // add the bundled certificate if it extends the chain further
62  if (IssuedBy(*wireCerts.back(), *bundledCert)) {
63  debugs(83, DBG_PARSE_NOTE(3), "Adding issuer CA: " << *bundledCert);
64  // OpenSSL API requires that we order certificates such that the
65  // chain can be appended directly into the on-wire traffic.
66  chain.emplace_back(bundledCert);
67  wireCerts.emplace_back(bundledCert);
68  continue;
69  }
70 
71  debugs(83, DBG_PARSE_NOTE(2), "WARNING: Ignoring certificate that does not extend the chain: " << *bundledCert);
72  }
73  }
74  catch (...) {
75  // TODO: Reject configs with malformed intermediate certs instead.
76  debugs(83, DBG_IMPORTANT, "ERROR: Failure while loading intermediate certificate(s) from '" << certFile << "':" <<
77  Debug::Extra << "problem: " << CurrentException);
78  }
79 
80 #elif USE_GNUTLS
81  const char *certFilename = certFile.c_str();
82  gnutls_datum_t data;
83  Security::LibErrorCode x = gnutls_load_file(certFilename, &data);
84  if (x != GNUTLS_E_SUCCESS) {
85  debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate file '" << certFile << "': " << ErrorString(x));
86  return false;
87  }
88 
89  gnutls_pcert_st pcrt;
90  x = gnutls_pcert_import_x509_raw(&pcrt, &data, GNUTLS_X509_FMT_PEM, 0);
91  if (x != GNUTLS_E_SUCCESS) {
92  debugs(83, DBG_IMPORTANT, "ERROR: unable to import certificate from '" << certFile << "': " << ErrorString(x));
93  return false;
94  }
95  gnutls_free(data.data);
96 
97  gnutls_x509_crt_t certificate;
98  x = gnutls_pcert_export_x509(&pcrt, &certificate);
99  if (x != GNUTLS_E_SUCCESS) {
100  debugs(83, DBG_IMPORTANT, "ERROR: unable to X.509 convert certificate from '" << certFile << "': " << ErrorString(x));
101  return false;
102  }
103 
104  if (certificate) {
105  cert = Security::CertPointer(certificate, [](gnutls_x509_crt_t p) {
106  debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p);
107  gnutls_x509_crt_deinit(p);
108  });
109  }
110 
111  // XXX: implement chain loading
112  debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
113 
114 #else
115  // do nothing.
116 #endif
117 
118  if (!cert) {
119  debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate from '" << certFile << "'");
120  }
121 
122  return bool(cert);
123 }
124 
128 bool
130 {
131  debugs(83, DBG_IMPORTANT, "Using key in " << privateKeyFile);
132 
133 #if USE_OPENSSL
134  const char *keyFilename = privateKeyFile.c_str();
135  // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
136  // so this may not fully work iff Config.Program.ssl_password is set.
137  pem_password_cb *cb = ::Config.Program.ssl_password ? &Ssl::AskPasswordCb : nullptr;
138  Ssl::ReadPrivateKeyFromFile(keyFilename, pkey, cb);
139 
140  if (pkey && !X509_check_private_key(cert.get(), pkey.get())) {
141  debugs(83, DBG_IMPORTANT, "WARNING: '" << privateKeyFile << "' X509_check_private_key() failed");
142  pkey.reset();
143  }
144 
145 #elif USE_GNUTLS
146  const char *keyFilename = privateKeyFile.c_str();
147  gnutls_datum_t data;
148  if (gnutls_load_file(keyFilename, &data) == GNUTLS_E_SUCCESS) {
149  gnutls_privkey_t key;
150  (void)gnutls_privkey_init(&key);
151  Security::ErrorCode x = gnutls_privkey_import_x509_raw(key, &data, GNUTLS_X509_FMT_PEM, nullptr, 0);
152  if (x == GNUTLS_E_SUCCESS) {
153  gnutls_x509_privkey_t xkey;
154  gnutls_privkey_export_x509(key, &xkey);
155  gnutls_privkey_deinit(key);
156  pkey = Security::PrivateKeyPointer(xkey, [](gnutls_x509_privkey_t p) {
157  debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p);
158  gnutls_x509_privkey_deinit(p);
159  });
160  }
161  }
162  gnutls_free(data.data);
163 
164 #else
165  // nothing to do.
166 #endif
167 
168  return bool(pkey);
169 }
170 
171 void
173 {
174  char buf[128];
175  if (!loadCertificates()) {
176  debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing certificate in '" << certFile << "'");
177  return;
178  }
179 
180  // pkey is mandatory, not having it makes cert and chain pointless.
181  if (!loadX509PrivateKeyFromFile()) {
182  debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing private key in '" << privateKeyFile << "'");
183  cert.reset();
184  chain.clear();
185  }
186 }
187 
class SquidConfig Config
Definition: SquidConfig.cc:12
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define assert(EX)
Definition: assert.h:19
static std::ostream & Extra(std::ostream &os)
prefixes each grouped debugs() line after the first one in the group
Definition: Stream.h:117
const char * c_str()
Definition: SBuf.cc:516
SBuf certFile
path of file containing PEM format X.509 certificate
Definition: KeyData.h:27
void loadFromFiles(const AnyP::PortCfg &, const char *portType)
load the contents of certFile and privateKeyFile into memory cert, pkey and chain
Definition: KeyData.cc:172
bool loadCertificates()
Definition: KeyData.cc:21
Security::CertPointer cert
public X.509 certificate from certFile
Definition: KeyData.h:31
Security::CertList chain
any certificates which must be chained from cert
Definition: KeyData.h:35
bool loadX509PrivateKeyFromFile()
Definition: KeyData.cc:129
void reset()
Forget the raw pointer - unlock if any value was set. Become a nil pointer.
char * ssl_password
Definition: SquidConfig.h:209
struct SquidConfig::@102 Program
#define DBG_PARSE_NOTE(x)
Definition: Stream.h:45
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
static int port
Definition: ldap_backend.cc:70
void ReadPrivateKeyFromFile(char const *keyFilename, Security::PrivateKeyPointer &pkey, pem_password_cb *passwd_callback)
Definition: gadgets.cc:787
const char * ErrorString(const LibErrorCode code)
converts numeric LibErrorCode into a human-friendlier string
Definition: forward.h:131
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:110
unsigned long LibErrorCode
TLS library-reported non-validation error.
Definition: forward.h:120
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition: forward.h:82
bool SelfSigned(Certificate &c)
Whether the given certificate is self-signed.
Definition: Certificate.h:33
bool IssuedBy(Certificate &cert, Certificate &issuer)
Definition: Certificate.cc:108
std::list< Security::CertPointer > CertList
Definition: forward.h:99
std::unique_ptr< BIO, HardFun< void, BIO *, &BIO_vfree > > BIO_Pointer
Definition: gadgets.h:51
Security::CertPointer ReadOptionalCertificate(const BIO_Pointer &)
Definition: gadgets.cc:741
Security::CertPointer ReadCertificate(const BIO_Pointer &)
Definition: gadgets.cc:766
int AskPasswordCb(char *buf, int size, int rwflag, void *userdata)
Definition: support.cc:64

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors