KeyData.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 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/KeyData.h"
13 #include "SquidConfig.h"
14 #include "ssl/bio.h"
15 #include "ssl/gadgets.h"
16 
21 bool
23 {
24  debugs(83, DBG_IMPORTANT, "Using certificate in " << certFile);
25  cert.reset(); // paranoid: ensure cert is unset
26 
27 #if USE_OPENSSL
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));
33  return false;
34  }
35 
36  cert = Ssl::ReadX509Certificate(bio); // error detected/reported below
37 
38 #elif USE_GNUTLS
39  const char *certFilename = certFile.c_str();
40  gnutls_datum_t data;
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));
44  return false;
45  }
46 
47  gnutls_pcert_st pcrt;
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));
51  return false;
52  }
53  gnutls_free(data.data);
54 
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));
59  return false;
60  }
61 
62  if (certificate) {
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);
66  });
67  }
68 
69 #else
70  // do nothing.
71 #endif
72 
73  if (!cert) {
74  debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate from '" << certFile << "'");
75  }
76 
77  return bool(cert);
78 }
79 
84 void
86 {
87 #if USE_OPENSSL
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));
93  return;
94  }
95 
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);
101  } else
102 #endif
103  {
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;
107 
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);
111 
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);
117  continue;
118  }
119 #endif
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);
128  } else {
129  debugs(83, DBG_PARSE_NOTE(2), certFile << ": Ignoring non-issuer CA " << nameStr << ": " << X509_verify_cert_error_string(checkCode) << " (" << checkCode << ")");
130  }
131  OPENSSL_free(nameStr);
132  }
133  }
134 
135 #elif USE_GNUTLS
136  // XXX: implement chain loading
137  debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
138 
139 #else
140  // nothing to do.
141 #endif
142 }
143 
147 bool
149 {
150  debugs(83, DBG_IMPORTANT, "Using key in " << privateKeyFile);
151 
152 #if USE_OPENSSL
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);
158 
159  if (pkey && !X509_check_private_key(cert.get(), pkey.get())) {
160  debugs(83, DBG_IMPORTANT, "WARNING: '" << privateKeyFile << "' X509_check_private_key() failed");
161  pkey.reset();
162  }
163 
164 #elif USE_GNUTLS
165  const char *keyFilename = privateKeyFile.c_str();
166  gnutls_datum_t data;
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);
178  });
179  }
180  }
181  gnutls_free(data.data);
182 
183 #else
184  // nothing to do.
185 #endif
186 
187  return bool(pkey);
188 }
189 
190 void
192 {
193  char buf[128];
194  if (!loadX509CertFromFile()) {
195  debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing certificate in '" << certFile << "'");
196  return;
197  }
198 
199  // certificate chain in the PEM file is optional
200  loadX509ChainFromFile();
201 
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 << "'");
205  cert.reset();
206  chain.clear();
207  }
208 }
209 
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:102
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition: forward.h:74
std::unique_ptr< BIO, HardFun< void, BIO *, &BIO_vfree > > BIO_Pointer
Definition: gadgets.h:51
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.
struct SquidConfig::@103 Program
#define DBG_IMPORTANT
Definition: Debug.h:41
static int port
Definition: ldap_backend.cc:69
Security::CertPointer ReadX509Certificate(const BIO_Pointer &)
Definition: gadgets.cc:695
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
void loadFromFiles(const AnyP::PortCfg &, const char *portType)
load the contents of certFile and privateKeyFile into memory cert, pkey and chain
Definition: KeyData.cc:191
const char * c_str()
Definition: SBuf.cc:516
#define DBG_PARSE_NOTE(x)
Definition: Debug.h:45
void ReadPrivateKeyFromFile(char const *keyFilename, Security::PrivateKeyPointer &pkey, pem_password_cb *passwd_callback)
Definition: gadgets.cc:713
void loadX509ChainFromFile()
Definition: KeyData.cc:85
int AskPasswordCb(char *buf, int size, int rwflag, void *userdata)
Definition: support.cc:64
bool loadX509PrivateKeyFromFile()
Definition: KeyData.cc:148
char * ssl_password
Definition: SquidConfig.h:209
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:123
bool loadX509CertFromFile()
Definition: KeyData.cc:22
T * get() const
Returns raw and possibly nullptr pointer.
class SquidConfig Config
Definition: SquidConfig.cc:12
unsigned long LibErrorCode
TLS library-reported non-validation error.
Definition: forward.h:112

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors