cert_validate_message.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 "acl/FilledChecklist.h"
11 #include "globals.h"
12 #include "helper.h"
13 #include "sbuf/Stream.h"
14 #include "security/CertError.h"
16 #include "ssl/ErrorDetail.h"
17 #include "ssl/support.h"
18 #include "util.h"
19 
24 static STACK_OF(X509) *
25 PeerValidationCertificatesChain(const Security::SessionPointer &ssl)
26 {
27  assert(ssl);
28  // The full chain built by openSSL while verifying the server cert,
29  // retrieved from verify callback:
30  if (const auto certs = static_cast<STACK_OF(X509) *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_ssl_cert_chain)))
31  return certs;
32 
34  return SSL_get_peer_cert_chain(ssl.get()); // may be nil
35 }
36 
37 void
39 {
40  body.clear();
42 
43  if (const char *sslVersion = SSL_get_version(vcert.ssl.get()))
44  body += "\n" + Ssl::CertValidationMsg::param_proto_version + "=" + sslVersion;
45 
46  if (const char *cipherName = SSL_CIPHER_get_name(SSL_get_current_cipher(vcert.ssl.get())))
47  body += "\n" + Ssl::CertValidationMsg::param_cipher + "=" + cipherName;
48 
49  STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(vcert.ssl);
50  if (peerCerts) {
51  Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
52  for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
53  X509 *cert = sk_X509_value(peerCerts, i);
54  PEM_write_bio_X509(bio.get(), cert);
55  body = body + "\n" + param_cert + xitoa(i) + "=";
56  char *ptr;
57  long len = BIO_get_mem_data(bio.get(), &ptr);
58  body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
59  if (!BIO_reset(bio.get())) {
60  // print an error?
61  }
62  }
63  }
64 
65  if (vcert.errors) {
66  int i = 0;
67  for (const Security::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
68  body +="\n";
69  body = body + param_error_name + xitoa(i) + "=" + GetErrorName(err->element.code) + "\n";
70  int errorCertPos = -1;
71  if (err->element.cert.get())
72  errorCertPos = sk_X509_find(peerCerts, err->element.cert.get());
73  if (errorCertPos < 0) {
74  // assert this error ?
75  debugs(83, 4, "WARNING: wrong cert in cert validator request");
76  }
77  body += param_error_cert + xitoa(i) + "=";
78  body += param_cert + xitoa((errorCertPos >= 0 ? errorCertPos : 0));
79  }
80  }
81 }
82 
83 static int
84 get_error_id(const char *label, size_t len)
85 {
86  const char *e = label + len -1;
87  while (e != label && xisdigit(*e)) --e;
88  if (e != label) ++e;
89  return strtol(e, nullptr, 10);
90 }
91 
92 bool
94 {
95  try {
96  tryParsingResponse(resp);
97  return true;
98  }
99  catch (...) {
100  debugs(83, DBG_IMPORTANT, "ERROR: Cannot parse sslcrtvalidator_program response:" <<
101  Debug::Extra << "problem: " << CurrentException);
102  return false;
103  }
104 }
105 
108 void
110 {
111  std::vector<CertItem> certs;
112 
113  const STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(resp.ssl);
114 
115  const char *param = body.c_str();
116  while (*param) {
117  while (xisspace(*param)) param++;
118  if (! *param)
119  break;
120 
121  size_t param_len = strcspn(param, "=\r\n");
122  if (param[param_len] != '=') {
123  throw TextException(ToSBuf("Missing parameter value: ", param), Here());
124  }
125  const char *value=param+param_len+1;
126 
127  if (param_len > param_cert.length() &&
128  strncmp(param, param_cert.c_str(), param_cert.length()) == 0) {
129  CertItem ci;
130  ci.name.assign(param, param_len);
131  const auto x509 = ReadCertificate(ReadOnlyBioTiedTo(value));
132  ci.setCert(x509.get());
133  certs.push_back(ci);
134 
135  const char *b = strstr(value, "-----END CERTIFICATE-----");
136  if (b == nullptr) {
137  throw TextException(ToSBuf("Missing END CERTIFICATE boundary: ", value), Here());
138  }
139  b += strlen("-----END CERTIFICATE-----");
140  param = b + 1;
141  continue;
142  }
143 
144  size_t value_len = strcspn(value, "\r\n");
145  std::string v(value, value_len);
146 
147  debugs(83, 5, "Returned value: " << std::string(param, param_len).c_str() << ": " <<
148  v.c_str());
149 
150  int errorId = get_error_id(param, param_len);
151  Ssl::CertValidationResponse::RecvdError &currentItem = resp.getError(errorId);
152 
153  if (param_len > param_error_name.length() &&
154  strncmp(param, param_error_name.c_str(), param_error_name.length()) == 0) {
155  currentItem.error_no = Ssl::GetErrorCode(v.c_str());
156  if (currentItem.error_no == SSL_ERROR_NONE) {
157  throw TextException(ToSBuf("Unknown TLS error name: ", v), Here());
158  }
159  } else if (param_len > param_error_reason.length() &&
160  strncmp(param, param_error_reason.c_str(), param_error_reason.length()) == 0) {
161  currentItem.error_reason = v;
162  } else if (param_len > param_error_cert.length() &&
163  strncmp(param, param_error_cert.c_str(), param_error_cert.length()) == 0) {
164 
165  if (X509 *cert = getCertByName(certs, v)) {
166  debugs(83, 6, "The certificate with id \"" << v << "\" found.");
167  currentItem.setCert(cert);
168  } else {
169  //In this case we assume that the certID is one of the certificates sent
170  // to cert validator. The certificates sent to cert validator have names in
171  // form "cert_xx" where the "xx" is an integer represents the position of
172  // the certificate inside peer certificates list.
173  const int certId = get_error_id(v.c_str(), v.length());
174  debugs(83, 6, "Cert index in peer certificates list:" << certId);
175  //if certId is not correct sk_X509_value returns NULL
176  currentItem.setCert(sk_X509_value(peerCerts, certId));
177  }
178  } else if (param_len > param_error_depth.length() &&
179  strncmp(param, param_error_depth.c_str(), param_error_depth.length()) == 0 &&
180  std::all_of(v.begin(), v.end(), isdigit)) {
181  currentItem.error_depth = atoi(v.c_str());
182  } else {
183  throw TextException(ToSBuf("Unknown parameter name: ", std::string(param, param_len)), Here());
184  }
185 
186  param = value + value_len;
187  }
188 
189  /*Run through parsed errors to check for errors*/
190  typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
191  for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
192  if (i->error_no == SSL_ERROR_NONE) {
193  throw TextException(ToSBuf("Incomplete response; missing error name from error_id: ", i->id), Here());
194  }
195  }
196 }
197 
198 X509 *
199 Ssl::CertValidationMsg::getCertByName(std::vector<CertItem> const &certs, std::string const & name)
200 {
201  typedef std::vector<CertItem>::const_iterator SVCI;
202  for (SVCI ci = certs.begin(); ci != certs.end(); ++ci) {
203  if (ci->name.compare(name) == 0)
204  return ci->cert.get();
205  }
206  return nullptr;
207 }
208 
209 uint64_t
211 {
212  // XXX: This math does not account for most of the response size!
213  return sizeof(CertValidationResponse);
214 }
215 
218 {
219  typedef Ssl::CertValidationResponse::RecvdErrors::iterator SVCREI;
220  for (SVCREI i = errors.begin(); i != errors.end(); ++i) {
221  if (i->id == errorId)
222  return *i;
223  }
225  errItem.id = errorId;
226  errors.push_back(errItem);
227  return errors.back();
228 }
229 
230 void
232 {
233  cert.resetAndLock(aCert);
234 }
235 
236 void
238 {
239  cert.resetAndLock(aCert);
240 }
241 
242 const std::string Ssl::CertValidationMsg::code_cert_validate("cert_validate");
243 const std::string Ssl::CertValidationMsg::param_domain("domain");
244 const std::string Ssl::CertValidationMsg::param_cert("cert_");
245 const std::string Ssl::CertValidationMsg::param_error_name("error_name_");
246 const std::string Ssl::CertValidationMsg::param_error_reason("error_reason_");
247 const std::string Ssl::CertValidationMsg::param_error_cert("error_cert_");
248 const std::string Ssl::CertValidationMsg::param_error_depth("error_depth_");
249 const std::string Ssl::CertValidationMsg::param_proto_version("proto_version");
250 const std::string Ssl::CertValidationMsg::param_cipher("cipher");
251 
Security::ErrorCode error_no
The OpenSSL error code.
static const std::string param_host
Parameter name for passing hostname.
Definition: crtd_message.h:78
#define Here()
source code location of the caller
Definition: Here.h:15
Security::SessionPointer ssl
void setCert(X509 *)
Sets cert to the given certificate.
Security::CertPointer ReadCertificate(const BIO_Pointer &)
Definition: gadgets.cc:816
static const std::string param_error_reason
Parameter name for passing the error reason.
static const std::string code_cert_validate
String code for "cert_validate" messages.
void composeRequest(CertValidationRequest const &vcert)
const char * GetErrorName(const Security::ErrorCode code, const bool prefixRawCode=false)
Definition: ErrorDetail.h:38
std::unique_ptr< BIO, HardFun< void, BIO *, &BIO_vfree > > BIO_Pointer
Definition: gadgets.h:57
RecvdError & getError(int errorId)
RecvdErrors errors
The list of parsed errors.
CbDataList * next
Definition: CbDataList.h:31
static const std::string param_error_depth
Parameter name for passing the error depth.
BIO_Pointer ReadOnlyBioTiedTo(const char *)
Definition: gadgets.cc:172
static int get_error_id(const char *label, size_t len)
static const std::string param_cipher
Parameter name for SSL cipher.
int ssl_ex_index_ssl_cert_chain
static const std::string param_proto_version
Parameter name for SSL version.
std::string domainName
The server name.
static const std::string param_error_name
Parameter name for passing the major SSL error.
X509 * getCertByName(std::vector< CertItem > const &, std::string const &name)
Search a CertItems list for the certificate with ID "name".
std::string body
Current body.
Definition: crtd_message.h:101
#define assert(EX)
Definition: assert.h:17
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define xisdigit(x)
Definition: xis.h:18
Security::ErrorCode GetErrorCode(const char *name)
The Security::ErrorCode code of the error described by "name".
Definition: ErrorDetail.h:30
const char * xitoa(int num)
Definition: util.cc:60
static std::ostream & Extra(std::ostream &)
Definition: debug.cc:1316
bool parseResponse(CertValidationResponse &resp)
Parse a response message and fill the resp object with parsed information.
std::string error_reason
A string describing the error.
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:53
static const std::string param_cert
Parameter name for passing SSL certificates.
Security::SessionPointer ssl
an std::runtime_error with thrower location info
Definition: TextException.h:20
static const std::string param_domain
Parameter name for passing intended domain name.
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
#define DBG_IMPORTANT
Definition: Stream.h:38
std::string name
The certificate Id to use.
void setCert(X509 *)
Sets cert to the given certificate.
static STACK_OF(X509) *PeerValidationCertificatesChain(const Security
static const std::string param_error_cert
Parameter name for passing the error cert ID.
#define xisspace(x)
Definition: xis.h:15
Security::CertErrors * errors
The list of errors detected.
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
void tryParsingResponse(CertValidationResponse &)
static uint64_t MemoryUsedByResponse(const CertValidationResponse::Pointer &)

 

Introduction

Documentation

Support

Miscellaneous