KeyData.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 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 
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  if (X509 *certificate = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)) {
36  cert.resetWithoutLocking(certificate);
37  }
38 
39 #elif USE_GNUTLS
40  const char *certFilename = certFile.c_str();
41  gnutls_datum_t data;
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));
45  return false;
46  }
47 
48  gnutls_pcert_st pcrt;
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));
52  return false;
53  }
54  gnutls_free(data.data);
55 
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));
60  return false;
61  }
62 
63  if (certificate) {
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);
67  });
68  }
69 
70 #else
71  // do nothing.
72 #endif
73 
74  if (!cert) {
75  debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate from '" << certFile << "'");
76  }
77 
78  return bool(cert);
79 }
80 
85 void
87 {
88 #if USE_OPENSSL
89  const char *certFilename = certFile.c_str();
90  Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
91  if (!bio || !BIO_read_filename(bio.get(), certFilename)) {
92  const auto x = ERR_get_error();
93  debugs(83, DBG_IMPORTANT, "ERROR: unable to load chain file '" << certFile << "': " << ErrorString(x));
94  return;
95  }
96 
97 #if TLS_CHAIN_NO_SELFSIGNED // ignore self-signed certs in the chain
98  if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK) {
99  char *nameStr = X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0);
100  debugs(83, DBG_PARSE_NOTE(2), "Certificate is self-signed, will not be chained: " << nameStr);
101  OPENSSL_free(nameStr);
102  } else
103 #endif
104  {
105  debugs(83, DBG_PARSE_NOTE(3), "Using certificate chain in " << certFile);
106  // and add to the chain any other certificate exist in the file
107  CertPointer latestCert = cert;
108 
109  while (auto ca = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)) {
110  // get Issuer name of the cert for debug display
111  char *nameStr = X509_NAME_oneline(X509_get_subject_name(ca), nullptr, 0);
112 
113 #if TLS_CHAIN_NO_SELFSIGNED // ignore self-signed certs in the chain
114  // self-signed certificates are not valid in a sent chain
115  if (X509_check_issued(ca, ca) == X509_V_OK) {
116  debugs(83, DBG_PARSE_NOTE(2), "CA " << nameStr << " is self-signed, will not be chained: " << nameStr);
117  OPENSSL_free(nameStr);
118  continue;
119  }
120 #endif
121  // checks that the chained certs are actually part of a chain for validating cert
122  const auto checkCode = X509_check_issued(ca, latestCert.get());
123  if (checkCode == X509_V_OK) {
124  debugs(83, DBG_PARSE_NOTE(3), "Adding issuer CA: " << nameStr);
125  // OpenSSL API requires that we order certificates such that the
126  // chain can be appended directly into the on-wire traffic.
127  latestCert = CertPointer(ca);
128  chain.emplace_front(latestCert);
129  } else {
130  debugs(83, DBG_PARSE_NOTE(2), certFile << ": Ignoring non-issuer CA " << nameStr << ": " << X509_verify_cert_error_string(checkCode) << " (" << checkCode << ")");
131  }
132  OPENSSL_free(nameStr);
133  }
134  }
135 
136 #elif USE_GNUTLS
137  // XXX: implement chain loading
138  debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
139 
140 #else
141  // nothing to do.
142 #endif
143 }
144 
148 bool
150 {
151  debugs(83, DBG_IMPORTANT, "Using key in " << privateKeyFile);
152 
153 #if USE_OPENSSL
154  const char *keyFilename = privateKeyFile.c_str();
155  // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
156  // so this may not fully work iff Config.Program.ssl_password is set.
157  pem_password_cb *cb = ::Config.Program.ssl_password ? &Ssl::AskPasswordCb : nullptr;
158  Ssl::ReadPrivateKeyFromFile(keyFilename, pkey, cb);
159 
160  if (pkey && !X509_check_private_key(cert.get(), pkey.get())) {
161  debugs(83, DBG_IMPORTANT, "WARNING: '" << privateKeyFile << "' X509_check_private_key() failed");
162  pkey.reset();
163  }
164 
165 #elif USE_GNUTLS
166  const char *keyFilename = privateKeyFile.c_str();
167  gnutls_datum_t data;
168  if (gnutls_load_file(keyFilename, &data) == GNUTLS_E_SUCCESS) {
169  gnutls_privkey_t key;
170  (void)gnutls_privkey_init(&key);
171  Security::ErrorCode x = gnutls_privkey_import_x509_raw(key, &data, GNUTLS_X509_FMT_PEM, nullptr, 0);
172  if (x == GNUTLS_E_SUCCESS) {
173  gnutls_x509_privkey_t xkey;
174  gnutls_privkey_export_x509(key, &xkey);
175  gnutls_privkey_deinit(key);
176  pkey = Security::PrivateKeyPointer(xkey, [](gnutls_x509_privkey_t p) {
177  debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p);
178  gnutls_x509_privkey_deinit(p);
179  });
180  }
181  }
182  gnutls_free(data.data);
183 
184 #else
185  // nothing to do.
186 #endif
187 
188  return bool(pkey);
189 }
190 
191 void
193 {
194  char buf[128];
195  if (!loadX509CertFromFile()) {
196  debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing certificate in '" << certFile << "'");
197  return;
198  }
199 
200  // certificate chain in the PEM file is optional
202 
203  // pkey is mandatory, not having it makes cert and chain pointless.
205  debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing private key in '" << privateKeyFile << "'");
206  cert.reset();
207  chain.clear();
208  }
209 }
210 
Ip::Address s
Definition: PortCfg.h:31
SBuf certFile
path of file containing PEM format X.509 certificate
Definition: KeyData.h:27
Security::CertPointer cert
public X.509 certificate from certFile
Definition: KeyData.h:31
void resetWithoutLocking(T *t)
Reset raw pointer - unlock any previous one and save new one without locking.
int AskPasswordCb(char *buf, int size, int rwflag, void *userdata)
Definition: support.cc:63
T * get() const
Returns raw and possibly nullptr pointer.
char * p
Definition: membanger.c:43
#define DBG_PARSE_NOTE(x)
Definition: Debug.h:50
void loadFromFiles(const AnyP::PortCfg &, const char *portType)
load the contents of certFile and privateKeyFile into memory cert, pkey and chain ...
Definition: KeyData.cc:192
Security::CertList chain
any certificates which must be chained from cert
Definition: KeyData.h:35
char * ssl_password
Definition: SquidConfig.h:204
SBuf privateKeyFile
path of file containing private key in PEM format
Definition: KeyData.h:28
char * toUrl(char *buf, unsigned int len) const
Definition: Address.cc:884
void const char HLPCB void * data
Definition: stub_helper.cc:16
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
#define DBG_IMPORTANT
Definition: Debug.h:46
int ErrorCode
Squid defined error code (<0), an error code returned by X.509 API, or SSL_ERROR_NONE.
Definition: forward.h:91
void reset()
Forget the raw pointer - unlock if any value was set. Become a nil pointer.
void loadX509ChainFromFile()
Definition: KeyData.cc:86
const char * c_str()
Definition: SBuf.cc:526
std::unique_ptr< BIO, HardFun< void, BIO *, &BIO_vfree > > BIO_Pointer
Definition: gadgets.h:50
static int port
Definition: ldap_backend.cc:69
void const char * buf
Definition: stub_helper.cc:16
struct SquidConfig::@104 Program
void ReadPrivateKeyFromFile(char const *keyFilename, Security::PrivateKeyPointer &pkey, pem_password_cb *passwd_callback)
Definition: gadgets.cc:719
bool loadX509CertFromFile()
Definition: KeyData.cc:21
bool loadX509PrivateKeyFromFile()
Definition: KeyData.cc:149
const char * ErrorString(const ErrorCode code)
Definition: forward.h:96
class SquidConfig Config
Definition: SquidConfig.cc:12
Security::PrivateKeyPointer pkey
private key from privateKeyFile
Definition: KeyData.h:33
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition: forward.h:63

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors