cert_validate_message.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 "security/CertError.h"
15 #include "ssl/ErrorDetail.h"
16 #include "ssl/support.h"
17 #include "util.h"
18 
23 static STACK_OF(X509) *
24 PeerValidationCertificatesChain(const Security::SessionPointer &ssl)
25 {
26  assert(ssl);
27  // The full chain built by openSSL while verifying the server cert,
28  // retrieved from verify callback:
29  if (const auto certs = static_cast<STACK_OF(X509) *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_ssl_cert_chain)))
30  return certs;
31 
33  return SSL_get_peer_cert_chain(ssl.get()); // may be nil
34 }
35 
36 void
38 {
39  body.clear();
41 
42  if (const char *sslVersion = SSL_get_version(vcert.ssl.get()))
43  body += "\n" + Ssl::CertValidationMsg::param_proto_version + "=" + sslVersion;
44 
45  if (const char *cipherName = SSL_CIPHER_get_name(SSL_get_current_cipher(vcert.ssl.get())))
46  body += "\n" + Ssl::CertValidationMsg::param_cipher + "=" + cipherName;
47 
48  STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(vcert.ssl);
49  if (peerCerts) {
50  Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
51  for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
52  X509 *cert = sk_X509_value(peerCerts, i);
53  PEM_write_bio_X509(bio.get(), cert);
54  body = body + "\n" + param_cert + xitoa(i) + "=";
55  char *ptr;
56  long len = BIO_get_mem_data(bio.get(), &ptr);
57  body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
58  if (!BIO_reset(bio.get())) {
59  // print an error?
60  }
61  }
62  }
63 
64  if (vcert.errors) {
65  int i = 0;
66  for (const Security::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
67  body +="\n";
68  body = body + param_error_name + xitoa(i) + "=" + GetErrorName(err->element.code) + "\n";
69  int errorCertPos = -1;
70  if (err->element.cert.get())
71  errorCertPos = sk_X509_find(peerCerts, err->element.cert.get());
72  if (errorCertPos < 0) {
73  // assert this error ?
74  debugs(83, 4, "WARNING: wrong cert in cert validator request");
75  }
76  body += param_error_cert + xitoa(i) + "=";
77  body += param_cert + xitoa((errorCertPos >= 0 ? errorCertPos : 0));
78  }
79  }
80 }
81 
82 static int
83 get_error_id(const char *label, size_t len)
84 {
85  const char *e = label + len -1;
86  while (e != label && xisdigit(*e)) --e;
87  if (e != label) ++e;
88  return strtol(e, 0, 10);
89 }
90 
91 bool
93 {
94  std::vector<CertItem> certs;
95 
96  const STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(resp.ssl);
97 
98  const char *param = body.c_str();
99  while (*param) {
100  while (xisspace(*param)) param++;
101  if (! *param)
102  break;
103 
104  size_t param_len = strcspn(param, "=\r\n");
105  if (param[param_len] != '=') {
106  debugs(83, DBG_IMPORTANT, "WARNING: cert validator response parse error: " << param);
107  return false;
108  }
109  const char *value=param+param_len+1;
110 
111  if (param_len > param_cert.length() &&
112  strncmp(param, param_cert.c_str(), param_cert.length()) == 0) {
113  CertItem ci;
114  ci.name.assign(param, param_len);
115  Security::CertPointer x509;
116  readCertFromMemory(x509, value);
117  ci.setCert(x509.get());
118  certs.push_back(ci);
119 
120  const char *b = strstr(value, "-----END CERTIFICATE-----");
121  if (b == NULL) {
122  debugs(83, DBG_IMPORTANT, "WARNING: cert Validator response parse error: Failed to find certificate boundary " << value);
123  return false;
124  }
125  b += strlen("-----END CERTIFICATE-----");
126  param = b + 1;
127  continue;
128  }
129 
130  size_t value_len = strcspn(value, "\r\n");
131  std::string v(value, value_len);
132 
133  debugs(83, 5, "Returned value: " << std::string(param, param_len).c_str() << ": " <<
134  v.c_str());
135 
136  int errorId = get_error_id(param, param_len);
137  Ssl::CertValidationResponse::RecvdError &currentItem = resp.getError(errorId);
138 
139  if (param_len > param_error_name.length() &&
140  strncmp(param, param_error_name.c_str(), param_error_name.length()) == 0) {
141  currentItem.error_no = Ssl::GetErrorCode(v.c_str());
142  if (currentItem.error_no == SSL_ERROR_NONE) {
143  debugs(83, DBG_IMPORTANT, "WARNING: cert validator response parse error: Unknown SSL Error: " << v);
144  return false;
145  }
146  } else if (param_len > param_error_reason.length() &&
147  strncmp(param, param_error_reason.c_str(), param_error_reason.length()) == 0) {
148  currentItem.error_reason = v;
149  } else if (param_len > param_error_cert.length() &&
150  strncmp(param, param_error_cert.c_str(), param_error_cert.length()) == 0) {
151 
152  if (X509 *cert = getCertByName(certs, v)) {
153  debugs(83, 6, "The certificate with id \"" << v << "\" found.");
154  currentItem.setCert(cert);
155  } else {
156  //In this case we assume that the certID is one of the certificates sent
157  // to cert validator. The certificates sent to cert validator have names in
158  // form "cert_xx" where the "xx" is an integer represents the position of
159  // the certificate inside peer certificates list.
160  const int certId = get_error_id(v.c_str(), v.length());
161  debugs(83, 6, "Cert index in peer certificates list:" << certId);
162  //if certId is not correct sk_X509_value returns NULL
163  currentItem.setCert(sk_X509_value(peerCerts, certId));
164  }
165  } else if (param_len > param_error_depth.length() &&
166  strncmp(param, param_error_depth.c_str(), param_error_depth.length()) == 0 &&
167  std::all_of(v.begin(), v.end(), isdigit)) {
168  currentItem.error_depth = atoi(v.c_str());
169  } else {
170  debugs(83, DBG_IMPORTANT, "WARNING: cert validator response parse error: Unknown parameter name " << std::string(param, param_len).c_str());
171  return false;
172  }
173 
174  param = value + value_len +1;
175  }
176 
177  /*Run through parsed errors to check for errors*/
178  typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
179  for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
180  if (i->error_no == SSL_ERROR_NONE) {
181  debugs(83, DBG_IMPORTANT, "WARNING: cert validator incomplete response: Missing error name from error_id: " << i->id);
182  return false;
183  }
184  }
185 
186  return true;
187 }
188 
189 X509 *
190 Ssl::CertValidationMsg::getCertByName(std::vector<CertItem> const &certs, std::string const & name)
191 {
192  typedef std::vector<CertItem>::const_iterator SVCI;
193  for (SVCI ci = certs.begin(); ci != certs.end(); ++ci) {
194  if (ci->name.compare(name) == 0)
195  return ci->cert.get();
196  }
197  return NULL;
198 }
199 
202 {
203  typedef Ssl::CertValidationResponse::RecvdErrors::iterator SVCREI;
204  for (SVCREI i = errors.begin(); i != errors.end(); ++i) {
205  if (i->id == errorId)
206  return *i;
207  }
209  errItem.id = errorId;
210  errors.push_back(errItem);
211  return errors.back();
212 }
213 
215 {
216  id = old.id;
217  error_no = old.error_no;
218  error_reason = old.error_reason;
219  error_depth = old.error_depth;
220  setCert(old.cert.get());
221 }
222 
224 {
225  id = old.id;
226  error_no = old.error_no;
227  error_reason = old.error_reason;
228  error_depth = old.error_depth;
229  setCert(old.cert.get());
230  return *this;
231 }
232 
233 void
235 {
236  cert.resetAndLock(aCert);
237 }
238 
240 {
241  name = old.name;
242  setCert(old.cert.get());
243 }
244 
246 {
247  name = old.name;
248  setCert(old.cert.get());
249  return *this;
250 }
251 
252 void
254 {
255  cert.resetAndLock(aCert);
256 }
257 
258 const std::string Ssl::CertValidationMsg::code_cert_validate("cert_validate");
259 const std::string Ssl::CertValidationMsg::param_domain("domain");
260 const std::string Ssl::CertValidationMsg::param_cert("cert_");
261 const std::string Ssl::CertValidationMsg::param_error_name("error_name_");
262 const std::string Ssl::CertValidationMsg::param_error_reason("error_reason_");
263 const std::string Ssl::CertValidationMsg::param_error_cert("error_cert_");
264 const std::string Ssl::CertValidationMsg::param_error_depth("error_depth_");
265 const std::string Ssl::CertValidationMsg::param_proto_version("proto_version");
266 const std::string Ssl::CertValidationMsg::param_cipher("cipher");
267 
#define assert(EX)
Definition: assert.h:17
RecvdErrors errors
The list of parsed errors.
SQUIDCEXTERN const char * xitoa(int num)
Definition: util.c:79
static const std::string param_error_reason
Parameter name for passing the error reason.
const char * GetErrorName(Security::ErrorCode value)
The string representation of the TLS error "value".
Definition: ErrorDetail.cc:392
static const std::string code_cert_validate
String code for "cert_validate" messages.
Security::ErrorCode error_no
The OpenSSL error code.
void composeRequest(CertValidationRequest const &vcert)
void error(char *format,...)
int i
Definition: membanger.c:49
static const std::string param_error_depth
Parameter name for passing the error depth.
Security::ErrorCode GetErrorCode(const char *name)
The Security::ErrorCode code of the error described by "name".
Definition: ErrorDetail.cc:345
#define xisspace(x)
Definition: xis.h:17
static const std::string param_cipher
Parameter name for SSL cipher.
void setCert(X509 *)
Sets cert to the given certificate.
CbDataList * next
Definition: CbDataList.h:31
static const std::string param_proto_version
Parameter name for SSL version.
static const std::string param_error_name
Parameter name for passing the major SSL error.
Security::SessionPointer ssl
Security::CertPointer cert
A pointer to certificate.
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
#define DBG_IMPORTANT
Definition: Debug.h:45
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
std::unique_ptr< BIO, HardFun< void, BIO *,&BIO_vfree > > BIO_Pointer
Definition: gadgets.h:50
std::string error_reason
A string describing the error.
static STACK_OF(X509)*PeerValidationCertificatesChain(const Security
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
RecvdError & operator=(const RecvdError &)
Security::CertErrors * errors
The list of errors detected.
std::string domainName
The server name.
bool readCertFromMemory(Security::CertPointer &cert, char const *bufferToRead)
Definition: gadgets.cc:139
Security::SessionPointer ssl
static const std::string param_cert
Parameter name for passing SSL certificates.
#define xisdigit(x)
Definition: xis.h:20
CertItem & operator=(const CertItem &)
static const std::string param_domain
Parameter name for passing intended domain name.
std::string name
The certificate Id to use.
void setCert(X509 *)
Sets cert to the given certificate.
static int get_error_id(const char *label, size_t len)
static const std::string param_error_cert
Parameter name for passing the error cert ID.
Security::CertPointer cert
The broken certificate.
int ssl_ex_index_ssl_cert_chain
#define NULL
Definition: types.h:166
RecvdError & getError(int errorId)
static const std::string param_host
Parameter name for passing hostname.
Definition: crtd_message.h:78
bool parseResponse(CertValidationResponse &resp, std::string &error)
Parse a response message and fill the resp object with parsed informations.
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:41

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors