PeerConnector.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2020 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 /* DEBUG: section 83 TLS Server/Peer negotiation */
10 
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "comm/Loops.h"
14 #include "comm/Read.h"
15 #include "Downloader.h"
16 #include "errorpage.h"
17 #include "fde.h"
18 #include "FwdState.h"
19 #include "http/Stream.h"
20 #include "HttpRequest.h"
21 #include "neighbors.h"
22 #include "pconn.h"
24 #include "security/PeerConnector.h"
25 #include "SquidConfig.h"
26 #if USE_OPENSSL
27 #include "ssl/bio.h"
29 #include "ssl/Config.h"
30 #include "ssl/helper.h"
31 #endif
32 
34 
35 Security::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const AccessLogEntryPointer &alp, const time_t timeout) :
36  AsyncJob("Security::PeerConnector"),
37  noteFwdPconnUse(false),
38  serverConn(aServerConn),
39  al(alp),
40  callback(aCallback),
41  negotiationTimeout(timeout),
42  startTime(squid_curtime),
43  useCertValidator_(true),
44  certsDownloads(0)
45 {
46  debugs(83, 5, serverConn);
47 
48  // if this throws, the caller's cb dialer is not our CbDialer
49  Must(dynamic_cast<CbDialer*>(callback->getDialer()));
50 
51  // watch for external connection closures
53  Must(!fd_table[serverConn->fd].closing());
57 }
58 
60 {
61  return (!callback || callback->canceled()) && AsyncJob::doneAll();
62 }
63 
65 void
67 {
69  debugs(83, 5, "this=" << (void*)this);
70 
71  // we own this Comm::Connection object and its fd exclusively, but must bail
72  // if others started closing the socket while we were waiting to start()
73  assert(Comm::IsConnOpen(serverConn));
74  if (fd_table[serverConn->fd].closing()) {
75  bail(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al));
76  return;
77  }
78 
80  if (initialize(tmp))
81  negotiate();
82  else
83  mustStop("Security::PeerConnector TLS socket initialize failed");
84 }
85 
86 void
88 {
89  closeHandler = nullptr;
90 
91  debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data);
92  const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
93 #if USE_OPENSSL
94  err->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, nullptr, nullptr);
95 #endif
96  bail(err);
97 }
98 
99 void
101 {
102  debugs(83, 5, serverConnection() << " timedout. this=" << (void*)this);
103  const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scGatewayTimeout, request.getRaw(), al);
104 #if USE_OPENSSL
105  err->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, nullptr, nullptr);
106 #endif
107  bail(err);
108 }
109 
110 bool
112 {
113  Security::ContextPointer ctx(getTlsContext());
114  debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
115 
116  if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
117  const auto xerrno = errno;
118  if (!ctx) {
119  debugs(83, DBG_IMPORTANT, "Error initializing TLS connection: No security context.");
120  } // else CreateClientSession() did the appropriate debugs() already
121  const auto anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw(), al);
122  anErr->xerrno = xerrno;
123  noteNegotiationDone(anErr);
124  bail(anErr);
125  return false;
126  }
127 
128  // A TLS/SSL session has now been created for the connection and stored in fd_table
129  serverSession = fd_table[serverConnection()->fd].ssl;
130  debugs(83, 5, serverConnection() << ", session=" << (void*)serverSession.get());
131 
132 #if USE_OPENSSL
133  // If CertValidation Helper used do not lookup checklist for errors,
134  // but keep a list of errors to send it to CertValidator
135  if (!Ssl::TheConfig.ssl_crt_validator) {
136  // Create the ACL check list now, while we have access to more info.
137  // The list is used in ssl_verify_cb() and is freed in ssl_free().
138  if (acl_access *acl = ::Config.ssl_client.cert_error) {
139  ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
140  check->al = al;
141  check->syncAle(request.getRaw(), nullptr);
142  // check->fd(fd); XXX: need client FD here
143  SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
144  }
145  }
146 #endif
147 
148  return true;
149 }
150 
151 void
153 {
154  const int fd = serverConnection()->fd;
155  Security::SessionPointer session(fd_table[fd].ssl);
156 
157  // retrieve TLS server negotiated information if any
158  serverConnection()->tlsNegotiations()->retrieveNegotiatedInfo(session);
159 
160 #if USE_OPENSSL
161  // retrieve TLS parsed extra info
162  BIO *b = SSL_get_rbio(session.get());
163  Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
164  if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails())
165  serverConnection()->tlsNegotiations()->retrieveParsedInfo(details);
166 #endif
167 }
168 
169 void
171 {
172  const int fd = serverConnection()->fd;
173  if (fd_table[fd].closing())
174  return;
175 
176 #if USE_OPENSSL
177  auto session = fd_table[fd].ssl.get();
178  debugs(83, 5, "SSL_connect session=" << (void*)session);
179  const int result = SSL_connect(session);
180  if (result <= 0) {
181 #elif USE_GNUTLS
182  auto session = fd_table[fd].ssl.get();
183  const int result = gnutls_handshake(session);
184  debugs(83, 5, "gnutls_handshake session=" << (void*)session << ", result=" << result);
185 
186  if (result == GNUTLS_E_SUCCESS) {
187  char *desc = gnutls_session_get_desc(session);
188  debugs(83, 2, serverConnection() << " TLS Session info: " << desc);
189  gnutls_free(desc);
190  }
191 
192  if (result != GNUTLS_E_SUCCESS) {
193  // debug the TLS session state so far
194  auto descIn = gnutls_handshake_get_last_in(session);
195  debugs(83, 2, "handshake IN: " << gnutls_handshake_description_get_name(descIn));
196  auto descOut = gnutls_handshake_get_last_out(session);
197  debugs(83, 2, "handshake OUT: " << gnutls_handshake_description_get_name(descOut));
198 #else
199  if (const int result = -1) {
200 #endif
201  handleNegotiateError(result);
202  return; // we might be gone by now
203  }
204 
205  recordNegotiationDetails();
206 
207  if (!sslFinalized())
208  return;
209 
210  sendSuccess();
211 }
212 
213 bool
215 {
216 #if USE_OPENSSL
217  if (Ssl::TheConfig.ssl_crt_validator && useCertValidator_) {
218  const int fd = serverConnection()->fd;
219  Security::SessionPointer session(fd_table[fd].ssl);
220 
221  Ssl::CertValidationRequest validationRequest;
222  // WARNING: Currently we do not use any locking for 'errors' member
223  // of the Ssl::CertValidationRequest class. In this code the
224  // Ssl::CertValidationRequest object used only to pass data to
225  // Ssl::CertValidationHelper::submit method.
226  validationRequest.ssl = session;
227  if (SBuf *dName = (SBuf *)SSL_get_ex_data(session.get(), ssl_ex_index_server))
228  validationRequest.domainName = dName->c_str();
229  if (Security::CertErrors *errs = static_cast<Security::CertErrors *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors)))
230  // validationRequest disappears on return so no need to cbdataReference
231  validationRequest.errors = errs;
232  try {
233  debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
234  AsyncCall::Pointer call = asyncCall(83,5, "Security::PeerConnector::sslCrtvdHandleReply", Ssl::CertValidationHelper::CbDialer(this, &Security::PeerConnector::sslCrtvdHandleReply, nullptr));
235  Ssl::CertValidationHelper::Submit(validationRequest, call);
236  return false;
237  } catch (const std::exception &e) {
238  debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
239  "request for " << validationRequest.domainName <<
240  " certificate: " << e.what() << "; will now block to " <<
241  "validate that certificate.");
242  // fall through to do blocking in-process generation.
243  const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
244 
245  noteNegotiationDone(anErr);
246  bail(anErr);
247  return true;
248  }
249  }
250 #endif
251 
252  noteNegotiationDone(NULL);
253  return true;
254 }
255 
256 #if USE_OPENSSL
257 void
259 {
260  Must(validationResponse != NULL);
261 
262  Ssl::ErrorDetail *errDetails = NULL;
263  bool validatorFailed = false;
264 
265  if (Debug::Enabled(83, 5)) {
266  Security::SessionPointer ssl(fd_table[serverConnection()->fd].ssl);
267  SBuf *server = static_cast<SBuf *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_server));
268  debugs(83,5, RawPointer("host", server) << " cert validation result: " << validationResponse->resultCode);
269  }
270 
271  if (validationResponse->resultCode == ::Helper::Error) {
272  if (Security::CertErrors *errs = sslCrtvdCheckForErrors(*validationResponse, errDetails)) {
273  Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
274  Security::CertErrors *oldErrs = static_cast<Security::CertErrors*>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors));
275  SSL_set_ex_data(session.get(), ssl_ex_index_ssl_errors, (void *)errs);
276  delete oldErrs;
277  }
278  } else if (validationResponse->resultCode != ::Helper::Okay)
279  validatorFailed = true;
280 
281  if (!errDetails && !validatorFailed) {
282  noteNegotiationDone(NULL);
283  sendSuccess();
284  return;
285  }
286 
287  ErrorState *anErr = NULL;
288  if (validatorFailed) {
290  } else {
292  anErr->detail = errDetails;
293  /*anErr->xerrno= Should preserved*/
294  }
295 
296  noteNegotiationDone(anErr);
297  bail(anErr);
298  return;
299 }
300 #endif
301 
302 #if USE_OPENSSL
308 {
309  ACLFilledChecklist *check = NULL;
310  Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
311 
312  if (acl_access *acl = ::Config.ssl_client.cert_error) {
313  check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
314  check->al = al;
315  check->syncAle(request.getRaw(), nullptr);
316  check->serverCert.resetWithoutLocking(SSL_get_peer_certificate(session.get()));
317  }
318 
319  Security::CertErrors *errs = nullptr;
320  typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
321  for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
322  debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
323 
324  assert(i->error_no != SSL_ERROR_NONE);
325 
326  if (!errDetails) {
327  bool allowed = false;
328  if (check) {
329  check->sslErrors = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
330  if (check->fastCheck().allowed())
331  allowed = true;
332  }
333  // else the Config.ssl_client.cert_error access list is not defined
334  // and the first error will cause the error page
335 
336  if (allowed) {
337  debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer");
338  } else {
339  debugs(83, 5, "confirming SSL error " << i->error_no);
340  X509 *brokenCert = i->cert.get();
341  Security::CertPointer peerCert(SSL_get_peer_certificate(session.get()));
342  const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str();
343  errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason);
344  }
345  if (check) {
346  delete check->sslErrors;
347  check->sslErrors = NULL;
348  }
349  }
350 
351  if (!errs)
352  errs = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
353  else
354  errs->push_back_unique(Security::CertError(i->error_no, i->cert, i->error_depth));
355  }
356  if (check)
357  delete check;
358 
359  return errs;
360 }
361 #endif
362 
364 void
366 {
367  const auto pc = static_cast<PeerConnector::Pointer*>(data);
368  if (pc->valid())
369  (*pc)->negotiateSsl();
370  delete pc;
371 }
372 
374 void
376 {
377  // Use job calls to add done() checks and other job logic/protections.
378  CallJobHere(83, 7, this, Security::PeerConnector, negotiate);
379 }
380 
381 void
383 {
384  const int fd = serverConnection()->fd;
385  const Security::SessionPointer session(fd_table[fd].ssl);
386  unsigned long ssl_lib_error = ret;
387 
388 #if USE_OPENSSL
389  const int ssl_error = SSL_get_error(session.get(), ret);
390 
391  switch (ssl_error) {
392  case SSL_ERROR_WANT_READ:
393  noteWantRead();
394  return;
395 
396  case SSL_ERROR_WANT_WRITE:
397  noteWantWrite();
398  return;
399 
400  case SSL_ERROR_SSL:
401  case SSL_ERROR_SYSCALL:
402  ssl_lib_error = ERR_get_error();
403  // proceed to the general error handling code
404  break;
405  default:
406  // no special error handling for all other errors
407  ssl_lib_error = SSL_ERROR_NONE;
408  break;
409  }
410 
411 #elif USE_GNUTLS
412  const int ssl_error = ret;
413 
414  switch (ret) {
415  case GNUTLS_E_WARNING_ALERT_RECEIVED: {
416  auto alert = gnutls_alert_get(session.get());
417  debugs(83, DBG_IMPORTANT, "TLS ALERT: " << gnutls_alert_get_name(alert));
418  }
419  // drop through to next case
420 
421  case GNUTLS_E_AGAIN:
422  case GNUTLS_E_INTERRUPTED:
423  if (gnutls_record_get_direction(session.get()) == 0)
424  noteWantRead();
425  else
426  noteWantWrite();
427  return;
428 
429  default:
430  // no special error handling for all other errors
431  break;
432  }
433 
434 #else
435  // this avoids unused variable compiler warnings.
436  Must(!session);
437  const int ssl_error = ret;
438 #endif
439 
440  // Log connection details, if any
441  recordNegotiationDetails();
442  noteNegotiationError(ret, ssl_error, ssl_lib_error);
443 }
444 
445 void
447 {
448  const int fd = serverConnection()->fd;
449  debugs(83, 5, serverConnection());
450 #if USE_OPENSSL
451  Security::SessionPointer session(fd_table[fd].ssl);
452  BIO *b = SSL_get_rbio(session.get());
453  Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
454  if (srvBio->holdRead()) {
455  if (srvBio->gotHello()) {
456  if (checkForMissingCertificates())
457  return; // Wait to download certificates before proceed.
458 
459  srvBio->holdRead(false);
460  // schedule a negotiateSSl to allow openSSL parse received data
461  negotiateSsl();
462  return;
463  } else if (srvBio->gotHelloFailed()) {
464  srvBio->holdRead(false);
465  debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd);
466  // schedule a negotiateSSl to allow openSSL parse received data
467  negotiateSsl();
468  return;
469  }
470  }
471 #endif
472 
473  // read timeout to avoid getting stuck while reading from a silent server
475  AsyncCall::Pointer timeoutCall = JobCallback(83, 5,
476  TimeoutDialer, this, Security::PeerConnector::commTimeoutHandler);
477  const auto timeout = Comm::MortalReadTimeout(startTime, negotiationTimeout);
478  commSetConnTimeout(serverConnection(), timeout, timeoutCall);
479 
480  Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, new Pointer(this), 0);
481 }
482 
483 void
485 {
486  const int fd = serverConnection()->fd;
487  debugs(83, 5, serverConnection());
488  Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, new Pointer(this), 0);
489  return;
490 }
491 
492 void
493 Security::PeerConnector::noteNegotiationError(const int ret, const int ssl_error, const int ssl_lib_error)
494 {
495 #if defined(EPROTO)
496  int sysErrNo = EPROTO;
497 #else
498  int sysErrNo = EACCES;
499 #endif
500 
501 #if USE_OPENSSL
502  // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
503  if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0)
504  sysErrNo = errno;
505 #endif
506  int xerr = errno;
507 
508  const int fd = serverConnection()->fd;
509  debugs(83, DBG_IMPORTANT, "ERROR: negotiating TLS on FD " << fd <<
510  ": " << Security::ErrorString(ssl_lib_error) << " (" <<
511  ssl_error << "/" << ret << "/" << xerr << ")");
512 
514  anErr->xerrno = sysErrNo;
515 
516 #if USE_OPENSSL
517  Security::SessionPointer session(fd_table[fd].ssl);
518  Ssl::ErrorDetail *errFromFailure = static_cast<Ssl::ErrorDetail *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_error_detail));
519  if (errFromFailure != NULL) {
520  // The errFromFailure is attached to the ssl object
521  // and will be released when ssl object destroyed.
522  // Copy errFromFailure to a new Ssl::ErrorDetail object
523  anErr->detail = new Ssl::ErrorDetail(*errFromFailure);
524  } else {
525  // server_cert can be NULL here
526  X509 *server_cert = SSL_get_peer_certificate(session.get());
527  anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert, NULL);
528  X509_free(server_cert);
529  }
530 
531  if (ssl_lib_error != SSL_ERROR_NONE)
532  anErr->detail->setLibError(ssl_lib_error);
533 #endif
534 
535  noteNegotiationDone(anErr);
536  bail(anErr);
537 }
538 
539 void
541 {
542  Must(error); // or the recipient will not know there was a problem
543  Must(callback != NULL);
544  CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
545  Must(dialer);
546  dialer->answer().error = error;
547 
548  if (const auto p = serverConnection()->getPeer())
550 
551  callBack();
552  disconnect();
553 
554  if (noteFwdPconnUse)
555  fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
556  serverConn->close();
557  serverConn = nullptr;
558 }
559 
560 void
562 {
563  callBack();
564  disconnect();
565 }
566 
567 void
569 {
570  if (closeHandler) {
571  comm_remove_close_handler(serverConnection()->fd, closeHandler);
572  closeHandler = nullptr;
573  }
574 
575  commUnsetConnTimeout(serverConnection());
576 }
577 
578 void
580 {
581  debugs(83, 5, "TLS setup ended for " << serverConnection());
582 
584  // Do this now so that if we throw below, swanSong() assert that we _tried_
585  // to call back holds.
586  callback = NULL; // this should make done() true
587  CbDialer *dialer = dynamic_cast<CbDialer*>(cb->getDialer());
588  Must(dialer);
589  dialer->answer().conn = serverConnection();
590  ScheduleCallHere(cb);
591 }
592 
593 void
595 {
596  // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any
598  if (callback != NULL) { // paranoid: we have left the caller waiting
599  debugs(83, DBG_IMPORTANT, "BUG: Unexpected state while connecting to a cache_peer or origin server");
600  const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
601  bail(anErr);
602  assert(!callback);
603  return;
604  }
605 }
606 
607 const char *
609 {
610  static MemBuf buf;
611  buf.reset();
612 
613  // TODO: redesign AsyncJob::status() API to avoid this
614  // id and stop reason reporting duplication.
615  buf.append(" [", 2);
616  if (stopReason != NULL) {
617  buf.append("Stopped, reason:", 16);
618  buf.appendf("%s",stopReason);
619  }
620  if (serverConn != NULL)
621  buf.appendf(" FD %d", serverConn->fd);
622  buf.appendf(" %s%u]", id.prefix(), id.value);
623  buf.terminate();
624 
625  return buf.content();
626 }
627 
628 #if USE_OPENSSL
631 {
632 public:
633  typedef void (Security::PeerConnector::*Method)(SBuf &object, int status);
634 
636  method_(method),
637  peerConnector_(pc) {}
638 
639  /* CallDialer API */
640  virtual bool canDial(AsyncCall &call) { return peerConnector_.valid(); }
641  virtual void dial(AsyncCall &call) { ((&(*peerConnector_))->*method_)(object, status); }
644 };
645 
646 void
648 {
649  AsyncCall::Pointer certCallback = asyncCall(81, 4,
650  "Security::PeerConnector::certDownloadingDone",
652 
653  const Downloader *csd = (request ? dynamic_cast<const Downloader*>(request->downloader.valid()) : nullptr);
654  Downloader *dl = new Downloader(url, certCallback, XactionInitiator::initCertFetcher, csd ? csd->nestedLevel() + 1 : 1);
655  AsyncJob::Start(dl);
656 }
657 
658 void
660 {
661  ++certsDownloads;
662  debugs(81, 5, "Certificate downloading status: " << downloadStatus << " certificate size: " << obj.length());
663 
664  // get ServerBio from SSL object
665  const int fd = serverConnection()->fd;
666  Security::SessionPointer session(fd_table[fd].ssl);
667  BIO *b = SSL_get_rbio(session.get());
668  Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
669 
670  // Parse Certificate. Assume that it is in DER format.
671  // According to RFC 4325:
672  // The server must provide a DER encoded certificate or a collection
673  // collection of certificates in a "certs-only" CMS message.
674  // The applications MUST accept DER encoded certificates and SHOULD
675  // be able to accept collection of certificates.
676  // TODO: support collection of certificates
677  const unsigned char *raw = (const unsigned char*)obj.rawContent();
678  if (X509 *cert = d2i_X509(NULL, &raw, obj.length())) {
679  char buffer[1024];
680  debugs(81, 5, "Retrieved certificate: " << X509_NAME_oneline(X509_get_subject_name(cert), buffer, 1024));
681  ContextPointer ctx(getTlsContext());
682  const Security::CertList &certsList = srvBio->serverCertificatesIfAny();
683  if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert, certsList, ctx)) {
684  urlsOfMissingCerts.push(SBuf(issuerUri));
685  }
686  Ssl::SSL_add_untrusted_cert(session.get(), cert);
687  }
688 
689  // Check if there are URIs to download from and if yes start downloading
690  // the first in queue.
691  if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) {
692  startCertDownloading(urlsOfMissingCerts.front());
693  urlsOfMissingCerts.pop();
694  return;
695  }
696 
697  srvBio->holdRead(false);
698  negotiateSsl();
699 }
700 
701 bool
703 {
704  // Check for nested SSL certificates downloads. For example when the
705  // certificate located in an SSL site which requires to download a
706  // a missing certificate (... from an SSL site which requires to ...).
707 
708  const Downloader *csd = (request ? request->downloader.get() : nullptr);
709  if (csd && csd->nestedLevel() >= MaxNestedDownloads)
710  return false;
711 
712  const int fd = serverConnection()->fd;
713  Security::SessionPointer session(fd_table[fd].ssl);
714  BIO *b = SSL_get_rbio(session.get());
715  Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
716  const Security::CertList &certs = srvBio->serverCertificatesIfAny();
717 
718  if (certs.size()) {
719  debugs(83, 5, "SSL server sent " << certs.size() << " certificates");
720  ContextPointer ctx(getTlsContext());
721  Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs, ctx);
722  if (urlsOfMissingCerts.size()) {
723  startCertDownloading(urlsOfMissingCerts.front());
724  urlsOfMissingCerts.pop();
725  return true;
726  }
727  }
728 
729  return false;
730 }
731 #endif //USE_OPENSSL
732 
bool CreateClientSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx)
Definition: Session.cc:184
PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const AccessLogEntryPointer &alp, const time_t timeout=0)
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:983
Security::SessionPointer ssl
Ssl::ErrorDetail * detail
Definition: errorpage.h:204
const Security::CertList & serverCertificatesIfAny()
Definition: bio.h:175
Security::CertErrors * sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&)
Check SSL errors returned from cert validator against sslproxy_cert_error access list.
CbcPointer< Security::PeerConnector > peerConnector_
The Security::PeerConnector object.
@ initCertFetcher
Missing intermediate certificates fetching code.
void peerConnectFailed(CachePeer *p)
Definition: neighbors.cc:1290
std::shared_ptr< SSL_CTX > ContextPointer
Definition: Context.h:29
virtual bool initialize(Security::SessionPointer &)
virtual void noteNegotiationError(const int result, const int ssl_error, const int ssl_lib_error)
#define SQUID_ERR_SSL_HANDSHAKE
Definition: support.h:43
int ssl_ex_index_ssl_errors
static void NegotiateSsl(int fd, void *data)
A wrapper for Comm::SetSelect() notifications.
@ Error
Definition: ResultCode.h:19
void handleNegotiateError(const int result)
void callBack()
a bail(), sendSuccess() helper: sends results to the initiator
Comm::ConnectionPointer serverConn
TCP connection to the peer.
virtual void swanSong()
Definition: AsyncJob.h:54
CbcPointer< ErrorState > error
problem details (nil on success)
struct SquidConfig::@123 ssl_client
acl_access * cert_error
Definition: SquidConfig.h:523
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
void commTimeoutHandler(const CommTimeoutCbParams &)
The connection read timeout callback handler.
void error(char *format,...)
void SSL_add_untrusted_cert(SSL *ssl, X509 *cert)
Definition: support.cc:1084
Definition: SBuf.h:86
RecvdErrors errors
The list of parsed errors.
void certDownloadingDone(SBuf &object, int status)
Called by Downloader after a certificate object downloaded.
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:567
PconnPool * fwdPconnPool
a collection of previously used persistent Squid-to-peer HTTP(S) connections
Definition: FwdState.cc:79
void noteUses(int uses)
Definition: pconn.cc:536
virtual CallDialer * getDialer()=0
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
@ ERR_SOCKET_FAILURE
Definition: err_type.h:30
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:26
@ ERR_SECURE_CONNECT_FAIL
Definition: err_type.h:29
void * BIO_get_data(BIO *table)
Definition: openssl.h:60
#define DBG_IMPORTANT
Definition: Debug.h:46
AsyncCall::Pointer closeHandler
we call this when the connection closed
std::string domainName
The server name.
void bail(ErrorState *error)
sends the given error to the initiator
const Security::TlsDetails::Pointer & receivedHelloDetails() const
Definition: bio.h:178
static bool Enabled(const int section, const int level)
whether debugging the given section and the given level produces output
Definition: Debug.h:75
void disconnect()
a bail(), sendSuccess() helper: stops monitoring the connection
@ scGatewayTimeout
Definition: StatusCode.h:75
const char * ErrorString(const ErrorCode code)
Definition: forward.h:97
Comm::ConnectionPointer conn
peer connection (secured on success)
Security::CertPointer serverCert
#define NULL
Definition: types.h:166
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:96
const char * rawContent() const
Definition: SBuf.cc:519
const char * uriOfIssuerIfMissing(X509 *cert, Security::CertList const &serverCertificates, const Security::ContextPointer &context)
Definition: support.cc:1051
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
const Acl::Answer & fastCheck()
Definition: Checklist.cc:336
#define true
Definition: GnuRegex.c:234
static Pointer Start(AsyncJob *job)
starts a freshly created job (i.e., makes the job asynchronous)
Definition: AsyncJob.cc:23
@ scBadGateway
Definition: StatusCode.h:73
Definition: MemBuf.h:23
@ ERR_CONNECT_FAIL
Definition: err_type.h:28
void const char HLPCB void * data
Definition: stub_helper.cc:16
Callback dialer API to allow PeerConnector to set the answer.
Definition: PeerConnector.h:49
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
Config TheConfig
Definition: Config.cc:12
const char * dash_str
#define assert(EX)
Definition: assert.h:19
@ Okay
Definition: ResultCode.h:18
virtual const char * status() const
internal cleanup; do not call directly
SBuf ErrorDetail
Definition: Esi.h:29
RawPointerT< Pointer > RawPointer(const char *label, const Pointer &ptr)
convenience wrapper for creating RawPointerT<> objects
Definition: Debug.h:244
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const
assigns uninitialized adapted_request and url ALE components
virtual bool canDial(AsyncCall &call)
@ scServiceUnavailable
Definition: StatusCode.h:74
#define COMM_SELECT_READ
Definition: defines.h:36
void negotiateSsl()
Comm::SetSelect() callback. Direct calls tickle/resume negotiations.
CBDATA_NAMESPACED_CLASS_INIT(Security, PeerConnector)
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
@ scInternalServerError
Definition: StatusCode.h:71
int commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition: comm.cc:593
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:404
CbDataList< Security::CertError > CertErrors
Holds a list of X.509 certificate errors.
Definition: forward.h:58
time_t squid_curtime
Definition: stub_time.cc:17
virtual void dial(AsyncCall &call)
int ssl_ex_index_server
#define fd_table
Definition: fde.h:189
bool push_back_unique(C const &element)
Definition: CbDataList.h:87
AsyncCall::Pointer callback
we call this with the results
@ ERR_GATEWAY_FAILURE
Definition: err_type.h:65
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:44
bool allowed() const
Definition: Acl.h:143
virtual bool doneAll() const
whether positive goal has been reached
const Security::CertErrors * sslErrors
SSL [certificate validation] errors, in undefined order.
unsigned int nestedLevel() const
The nested level of Downloader object (downloads inside downloads).
Definition: Downloader.h:57
std::list< Security::CertPointer > CertList
Definition: forward.h:81
virtual Security::EncryptorAnswer & answer()=0
gives PeerConnector access to the in-dialer answer
PeerConnectorCertDownloaderDialer(Method method, Security::PeerConnector *pc)
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
static ErrorState * NewForwarding(err_type, HttpRequestPointer &, const AccessLogEntryPointer &)
Creates a general request forwarding error with the right http_status.
Definition: errorpage.cc:667
static char server[MAXLINE]
#define acl_access
Definition: forward.h:44
int ssl_ex_index_cert_error_check
size_t HttpReply *STUB StoreEntry const KeyScope scope const HttpRequestMethod & method
Definition: stub_store.cc:108
virtual void start()
called by AsyncStart; do not call directly
Definition: AsyncJob.cc:43
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
Method method_
The Security::PeerConnector method to dial.
CallDialer to allow use Downloader objects within PeerConnector class.
void const char HLPCB * callback
Definition: stub_helper.cc:16
AsyncCall * asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
static void Submit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &)
Submit crtd request message to external crtd server.
Definition: helper.cc:301
void commCloseHandler(const CommCloseCbParams &params)
The comm_close callback handler.
time_t MortalReadTimeout(const time_t startTime, const time_t lifetimeLimit)
maximum read delay for readers with limited lifetime
Definition: Read.cc:246
void sslCrtvdHandleReply(Ssl::CertValidationResponsePointer)
Process response from cert validator helper.
void missingChainCertificatesUrls(std::queue< SBuf > &URIs, Security::CertList const &serverCertificates, const Security::ContextPointer &context)
Definition: support.cc:1072
Security::CertErrors * errors
The list of errors detected.
bool gotHelloFailed() const
Return true if the Server Hello parsing failed.
Definition: bio.h:172
bool gotHello() const
Definition: bio.h:169
virtual void start()
Preps connection and SSL state. Calls negotiate().
#define false
Definition: GnuRegex.c:233
Callback data to use with Downloader callbacks.
Definition: Downloader.h:35
Network/connection security abstraction layer.
Definition: Connection.h:33
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
virtual void noteWantWrite()
bool holdRead() const
The read hold state.
Definition: bio.h:155
#define COMM_SELECT_WRITE
Definition: defines.h:37
void resetWithoutLocking(T *t)
Reset raw pointer - unlock any previous one and save new one without locking.
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:1010
void const char * buf
Definition: stub_helper.cc:16
int ssl_ex_index_ssl_error_detail
T * get() const
Returns raw and possibly nullptr pointer.
class SquidConfig Config
Definition: SquidConfig.cc:12
void sendSuccess()
sends the encrypted connection to the initiator
void startCertDownloading(SBuf &url)
Start downloading procedure for the given URL.

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors