PeerConnector.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 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 "base/IoManip.h"
14#include "comm/Loops.h"
15#include "comm/Read.h"
16#include "Downloader.h"
17#include "errorpage.h"
18#include "fde.h"
19#include "FwdState.h"
20#include "http/Stream.h"
21#include "HttpRequest.h"
22#include "neighbors.h"
23#include "pconn.h"
25#include "security/Io.h"
28#include "SquidConfig.h"
29#if USE_OPENSSL
30#include "ssl/bio.h"
32#include "ssl/Config.h"
33#include "ssl/helper.h"
34#endif
35
37
38Security::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const AccessLogEntryPointer &alp, const time_t timeout) :
39 AsyncJob("Security::PeerConnector"),
40 noteFwdPconnUse(false),
41 serverConn(aServerConn),
42 al(alp),
43 callback(aCallback),
44 negotiationTimeout(timeout),
45 startTime(squid_curtime),
46 useCertValidator_(true),
47 certsDownloads(0)
48{
49 debugs(83, 5, serverConn);
50
51 // if this throws, the caller's cb dialer is not our CbDialer
52 Must(dynamic_cast<CbDialer*>(callback->getDialer()));
53
54 // watch for external connection closures
56 Must(!fd_table[serverConn->fd].closing());
60}
61
63
65{
66 return (!callback || callback->canceled()) && AsyncJob::doneAll();
67}
68
70void
72{
74 debugs(83, 5, "this=" << (void*)this);
75
76 // we own this Comm::Connection object and its fd exclusively, but must bail
77 // if others started closing the socket while we were waiting to start()
78 assert(Comm::IsConnOpen(serverConn));
79 if (fd_table[serverConn->fd].closing()) {
80 bail(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al));
81 return;
82 }
83
85 if (initialize(tmp))
86 negotiate();
87 else
88 mustStop("Security::PeerConnector TLS socket initialize failed");
89}
90
91void
93{
94 if (!checklist.al)
95 checklist.al = al;
96 checklist.syncAle(request.getRaw(), nullptr);
97 // checklist.fd(fd); XXX: need client FD here
98
99#if USE_OPENSSL
100 if (!checklist.serverCert) {
101 if (const auto session = fd_table[serverConnection()->fd].ssl.get())
102 checklist.serverCert.resetWithoutLocking(SSL_get_peer_certificate(session));
103 }
104#else
105 // checklist.serverCert is not maintained in other builds
106#endif
107}
108
109void
111{
112 debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data);
113
114 closeHandler = nullptr;
115 if (serverConn) {
116 countFailingConnection();
117 serverConn->noteClosure();
118 serverConn = nullptr;
119 }
120
121 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
122 static const auto d = MakeNamedErrorDetail("TLS_CONNECT_CLOSE");
123 err->detailError(d);
124 bail(err);
125}
126
127void
129{
130 debugs(83, 5, serverConnection() << " timedout. this=" << (void*)this);
131 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scGatewayTimeout, request.getRaw(), al);
132 static const auto d = MakeNamedErrorDetail("TLS_CONNECT_TIMEOUT");
133 err->detailError(d);
134 bail(err);
135}
136
137bool
139{
140 Must(Comm::IsConnOpen(serverConnection()));
141
142 Security::ContextPointer ctx(getTlsContext());
143 debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
144
145 if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
146 const auto xerrno = errno;
147 if (!ctx) {
148 debugs(83, DBG_IMPORTANT, "ERROR: initializing TLS connection: No security context.");
149 } // else CreateClientSession() did the appropriate debugs() already
150 const auto anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw(), al);
151 anErr->xerrno = xerrno;
152 noteNegotiationDone(anErr);
153 bail(anErr);
154 return false;
155 }
156
157 // A TLS/SSL session has now been created for the connection and stored in fd_table
158 serverSession = fd_table[serverConnection()->fd].ssl;
159 debugs(83, 5, serverConnection() << ", session=" << (void*)serverSession.get());
160
161#if USE_OPENSSL
162 // If CertValidation Helper used do not lookup checklist for errors,
163 // but keep a list of errors to send it to CertValidator
164 if (!Ssl::TheConfig.ssl_crt_validator) {
165 // Create the ACL check list now, while we have access to more info.
166 // The list is used in ssl_verify_cb() and is freed in ssl_free().
167 // XXX: This info may change, especially if we fetch missing certs.
168 // TODO: Remove ACLFilledChecklist::sslErrors and other pre-computed
169 // state in favor of the ACLs accessing current/fresh info directly.
170 if (acl_access *acl = ::Config.ssl_client.cert_error) {
171 ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
172 fillChecklist(*check);
173 SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
174 }
175 }
176
177 // Protect from cycles in the certificate dependency graph: TLS site S1 is
178 // missing certificate C1 located at TLS site S2. TLS site S2 is missing
179 // certificate C2 located at [...] TLS site S1.
180 const auto cycle = certDownloadNestingLevel() >= MaxNestedDownloads;
181 if (cycle)
182 debugs(83, 3, "will not fetch any missing certificates; suspecting cycle: " << certDownloadNestingLevel() << '/' << MaxNestedDownloads);
183 const auto sessData = Ssl::VerifyCallbackParameters::New(*serverSession);
184 // when suspecting a cycle, break it by not fetching any missing certs
185 sessData->callerHandlesMissingCertificates = !cycle;
186#endif
187
188 return true;
189}
190
191void
193{
194 Must(Comm::IsConnOpen(serverConnection()));
195
196 const int fd = serverConnection()->fd;
197 Security::SessionPointer session(fd_table[fd].ssl);
198
199 // retrieve TLS server negotiated information if any
200 serverConnection()->tlsNegotiations()->retrieveNegotiatedInfo(session);
201
202#if USE_OPENSSL
203 // retrieve TLS parsed extra info
204 BIO *b = SSL_get_rbio(session.get());
205 Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
206 if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails())
207 serverConnection()->tlsNegotiations()->retrieveParsedInfo(details);
208#endif
209}
210
211void
213{
214 Must(Comm::IsConnOpen(serverConnection()));
215
216 const int fd = serverConnection()->fd;
217 if (fd_table[fd].closing())
218 return;
219
220 const auto result = Security::Connect(*serverConnection());
221
222#if USE_OPENSSL
223 auto &sconn = *fd_table[fd].ssl;
224
225 // log ASAP, even if the handshake has not completed (or failed)
226 keyLogger.checkpoint(sconn, *this);
227
228 // OpenSSL v1 APIs do not allow unthreaded applications like Squid to fetch
229 // missing certificates _during_ OpenSSL certificate validation. Our
230 // handling of X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (abbreviated
231 // here as EUNABLE) approximates what would happen if we did (attempt to)
232 // fetch any missing certificates during OpenSSL certificate validation.
233 // * We did not hide EUNABLE; SSL_connect() was successful: Handle success.
234 // * We did not hide EUNABLE; SSL_connect() reported some error E: Honor E.
235 // * We hid EUNABLE; SSL_connect() was successful: Remember success and try
236 // to fetch the missing certificates. If all goes well, honor success.
237 // * We hid EUNABLE; SSL_connect() reported EUNABLE: Warn but honor EUNABLE.
238 // * We hid EUNABLE; SSL_connect() reported some EOTHER: Remember EOTHER and
239 // try to fetch the missing certificates. If all goes well, honor EOTHER.
240 // If fetching or post-fetching validation fails, then honor that failure
241 // because EOTHER would not have happened if we fetched during validation.
242 if (auto &hidMissingIssuer = Ssl::VerifyCallbackParameters::At(sconn).hidMissingIssuer) {
243 hidMissingIssuer = false; // prep for the next SSL_connect()
244
245 if (result.category == IoResult::ioSuccess ||
246 !(result.errorDetail && result.errorDetail->errorNo() == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY))
247 return handleMissingCertificates(result);
248
249 debugs(83, DBG_IMPORTANT, "ERROR: Squid BUG: Honoring unexpected SSL_connect() failure: X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY");
250 // fall through to regular error handling
251 }
252#endif
253
254 handleNegotiationResult(result);
255}
256
257void
259{
260 switch (result.category) {
262 recordNegotiationDetails();
263 if (sslFinalized() && callback)
264 sendSuccess();
265 return; // we may be gone by now
266
268 noteWantRead();
269 return;
270
272 noteWantWrite();
273 return;
274
276 break; // fall through to error handling
277 }
278
279 // TODO: Honor result.important when working in a reverse proxy role?
280 debugs(83, 2, "ERROR: Cannot establish a TLS connection to " << serverConnection() << ':' <<
281 Debug::Extra << "problem: " << result.errorDescription <<
282 RawPointer("detail: ", result.errorDetail).asExtra());
283 recordNegotiationDetails();
284 noteNegotiationError(result.errorDetail);
285}
286
287bool
289{
290#if USE_OPENSSL
291 if (Ssl::TheConfig.ssl_crt_validator && useCertValidator_) {
292 Must(Comm::IsConnOpen(serverConnection()));
293 const int fd = serverConnection()->fd;
294 Security::SessionPointer session(fd_table[fd].ssl);
295
296 Ssl::CertValidationRequest validationRequest;
297 // WARNING: Currently we do not use any locking for 'errors' member
298 // of the Ssl::CertValidationRequest class. In this code the
299 // Ssl::CertValidationRequest object used only to pass data to
300 // Ssl::CertValidationHelper::submit method.
301 validationRequest.ssl = session;
302 if (SBuf *dName = (SBuf *)SSL_get_ex_data(session.get(), ssl_ex_index_server))
303 validationRequest.domainName = dName->c_str();
304 if (Security::CertErrors *errs = static_cast<Security::CertErrors *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors)))
305 // validationRequest disappears on return so no need to cbdataReference
306 validationRequest.errors = errs;
307 try {
308 debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
309 AsyncCall::Pointer call = asyncCall(83,5, "Security::PeerConnector::sslCrtvdHandleReply", Ssl::CertValidationHelper::CbDialer(this, &Security::PeerConnector::sslCrtvdHandleReply, nullptr));
310 Ssl::CertValidationHelper::Submit(validationRequest, call);
311 return false;
312 } catch (const std::exception &e) {
313 debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
314 "request for " << validationRequest.domainName <<
315 " certificate: " << e.what() << "; will now block to " <<
316 "validate that certificate.");
317 // fall through to do blocking in-process generation.
318 const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
319
320 noteNegotiationDone(anErr);
321 bail(anErr);
322 return true;
323 }
324 }
325#endif
326
327 noteNegotiationDone(nullptr);
328 return true;
329}
330
331#if USE_OPENSSL
332void
334{
335 Must(validationResponse != nullptr);
336 Must(Comm::IsConnOpen(serverConnection()));
337
338 ErrorDetail::Pointer errDetails;
339 bool validatorFailed = false;
340
341 if (Debug::Enabled(83, 5)) {
342 Security::SessionPointer ssl(fd_table[serverConnection()->fd].ssl);
343 SBuf *server = static_cast<SBuf *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_server));
344 debugs(83, 5, "cert validation result: " << validationResponse->resultCode << RawPointer(" host: ", server));
345 }
346
347 if (validationResponse->resultCode == ::Helper::Error) {
348 if (Security::CertErrors *errs = sslCrtvdCheckForErrors(*validationResponse, errDetails)) {
349 Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
350 Security::CertErrors *oldErrs = static_cast<Security::CertErrors*>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors));
351 SSL_set_ex_data(session.get(), ssl_ex_index_ssl_errors, (void *)errs);
352 delete oldErrs;
353 }
354 } else if (validationResponse->resultCode != ::Helper::Okay)
355 validatorFailed = true;
356
357 if (!errDetails && !validatorFailed) {
358 noteNegotiationDone(nullptr);
359 if (callback)
360 sendSuccess();
361 return;
362 }
363
364 ErrorState *anErr = nullptr;
365 if (validatorFailed) {
367 } else {
369 anErr->detailError(errDetails);
370 /*anErr->xerrno= Should preserved*/
371 }
372
373 noteNegotiationDone(anErr);
374 bail(anErr);
375 return;
376}
377#endif
378
379#if USE_OPENSSL
385{
386 Must(Comm::IsConnOpen(serverConnection()));
387
388 ACLFilledChecklist *check = nullptr;
389 Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
390
391 if (acl_access *acl = ::Config.ssl_client.cert_error) {
392 check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
393 fillChecklist(*check);
394 }
395
396 Security::CertErrors *errs = nullptr;
397 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
398 for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
399 debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
400
401 assert(i->error_no != SSL_ERROR_NONE);
402
403 if (!errDetails) {
404 bool allowed = false;
405 if (check) {
406 check->sslErrors = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
407 if (check->fastCheck().allowed())
408 allowed = true;
409 }
410 // else the Config.ssl_client.cert_error access list is not defined
411 // and the first error will cause the error page
412
413 if (allowed) {
414 debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer");
415 } else {
416 debugs(83, 5, "confirming SSL error " << i->error_no);
417 const auto &brokenCert = i->cert;
418 Security::CertPointer peerCert(SSL_get_peer_certificate(session.get()));
419 const char *aReason = i->error_reason.empty() ? nullptr : i->error_reason.c_str();
420 errDetails = new ErrorDetail(i->error_no, peerCert, brokenCert, aReason);
421 }
422 if (check) {
423 delete check->sslErrors;
424 check->sslErrors = nullptr;
425 }
426 }
427
428 if (!errs)
429 errs = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
430 else
431 errs->push_back_unique(Security::CertError(i->error_no, i->cert, i->error_depth));
432 }
433 if (check)
434 delete check;
435
436 return errs;
437}
438#endif
439
441void
443{
444 const auto pc = static_cast<PeerConnector::Pointer*>(data);
445 if (pc->valid())
446 (*pc)->negotiateSsl();
447 delete pc;
448}
449
451void
453{
454 // Use job calls to add done() checks and other job logic/protections.
455 CallJobHere(83, 7, this, Security::PeerConnector, negotiate);
456}
457
458void
460{
461 debugs(83, 5, serverConnection());
462
463 Must(Comm::IsConnOpen(serverConnection()));
464 const int fd = serverConnection()->fd;
465
466 // read timeout to avoid getting stuck while reading from a silent server
468 AsyncCall::Pointer timeoutCall = JobCallback(83, 5,
470 const auto timeout = Comm::MortalReadTimeout(startTime, negotiationTimeout);
471 commSetConnTimeout(serverConnection(), timeout, timeoutCall);
472
473 Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, new Pointer(this), 0);
474}
475
476void
478{
479 debugs(83, 5, serverConnection());
480 Must(Comm::IsConnOpen(serverConnection()));
481
482 const int fd = serverConnection()->fd;
483 Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, new Pointer(this), 0);
484 return;
485}
486
487void
489{
491 if (detail) {
492 anErr->xerrno = detail->sysError();
493 anErr->detailError(detail);
494 }
495 noteNegotiationDone(anErr);
496 bail(anErr);
497}
498
501{
502 assert(callback);
503 const auto dialer = dynamic_cast<CbDialer*>(callback->getDialer());
504 assert(dialer);
505 return dialer->answer();
506}
507
508void
510{
511 Must(error); // or the recipient will not know there was a problem
512 answer().error = error;
513
514 if (const auto failingConnection = serverConn) {
515 countFailingConnection();
516 disconnect();
517 failingConnection->close();
518 }
519
520 callBack();
521}
522
523void
525{
526 assert(Comm::IsConnOpen(serverConn));
527 answer().conn = serverConn;
528 disconnect();
529 callBack();
530}
531
532void
534{
535 assert(serverConn);
536 if (const auto p = serverConn->getPeer())
538 // TODO: Calling PconnPool::noteUses() should not be our responsibility.
539 if (noteFwdPconnUse && serverConn->isOpen())
540 fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
541}
542
543void
545{
546 const auto stillOpen = Comm::IsConnOpen(serverConn);
547
548 if (closeHandler) {
549 if (stillOpen)
550 comm_remove_close_handler(serverConn->fd, closeHandler);
551 closeHandler = nullptr;
552 }
553
554 if (stillOpen)
555 commUnsetConnTimeout(serverConn);
556
557 serverConn = nullptr;
558}
559
560void
562{
563 debugs(83, 5, "TLS setup ended for " << answer().conn);
564
565 AsyncCall::Pointer cb = callback;
566 // Do this now so that if we throw below, swanSong() assert that we _tried_
567 // to call back holds.
568 callback = nullptr; // this should make done() true
570}
571
572void
574{
575 // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any
577
578 if (callback) {
579 // job-ending emergencies like handleStopRequest() or callException()
580 const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
581 bail(anErr);
582 assert(!callback);
583 return;
584 }
585}
586
587const char *
589{
590 static MemBuf buf;
591 buf.reset();
592
593 // TODO: redesign AsyncJob::status() API to avoid this
594 // id and stop reason reporting duplication.
595 buf.append(" [", 2);
596 if (stopReason != nullptr) {
597 buf.append("Stopped, reason:", 16);
598 buf.appendf("%s",stopReason);
599 }
600 if (Comm::IsConnOpen(serverConn))
601 buf.appendf(" FD %d", serverConn->fd);
602 buf.appendf(" %s%u]", id.prefix(), id.value);
603 buf.terminate();
604
605 return buf.content();
606}
607
608#if USE_OPENSSL
611{
612public:
613 typedef void (Security::PeerConnector::*Method)(SBuf &object, int status);
614
616 method_(method),
617 peerConnector_(pc) {}
618
619 /* CallDialer API */
620 virtual bool canDial(AsyncCall &) { return peerConnector_.valid(); }
621 virtual void dial(AsyncCall &) { ((&(*peerConnector_))->*method_)(object, status); }
624};
625
627unsigned int
629{
630 if (request) {
631 // Nesting level increases when a PeerConnector (at level L) creates a
632 // Downloader (which is assigned level L+1). If we were initiated by
633 // such a Downloader, then their nesting level is our nesting level.
634 if (const auto previousDownloader = request->downloader.get())
635 return previousDownloader->nestedLevel();
636 }
637 return 0; // no other PeerConnector job waits for us
638}
639
640void
642{
643 AsyncCall::Pointer certCallback = asyncCall(81, 4,
644 "Security::PeerConnector::certDownloadingDone",
646
647 const auto dl = new Downloader(url, certCallback,
648 MasterXaction::MakePortless<XactionInitiator::initCertFetcher>(),
649 certDownloadNestingLevel() + 1);
650 certDownloadWait.start(dl, certCallback);
651}
652
653void
655{
656 certDownloadWait.finish();
657
658 ++certsDownloads;
659 debugs(81, 5, "Certificate downloading status: " << downloadStatus << " certificate size: " << obj.length());
660
661 Must(Comm::IsConnOpen(serverConnection()));
662 const auto &sconn = *fd_table[serverConnection()->fd].ssl;
663
664 // Parse Certificate. Assume that it is in DER format.
665 // According to RFC 4325:
666 // The server must provide a DER encoded certificate or a collection
667 // collection of certificates in a "certs-only" CMS message.
668 // The applications MUST accept DER encoded certificates and SHOULD
669 // be able to accept collection of certificates.
670 // TODO: support collection of certificates
671 const unsigned char *raw = (const unsigned char*)obj.rawContent();
672 if (X509 *cert = d2i_X509(nullptr, &raw, obj.length())) {
673 debugs(81, 5, "Retrieved certificate: " << *cert);
674
675 if (!downloadedCerts)
676 downloadedCerts.reset(sk_X509_new_null());
677 sk_X509_push(downloadedCerts.get(), cert);
678
679 ContextPointer ctx(getTlsContext());
680 const auto certsList = SSL_get_peer_cert_chain(&sconn);
681 if (!Ssl::findIssuerCertificate(cert, certsList, ctx)) {
682 if (const auto issuerUri = Ssl::findIssuerUri(cert)) {
683 debugs(81, 5, "certificate " << *cert <<
684 " points to its missing issuer certificate at " << issuerUri);
685 urlsOfMissingCerts.push(SBuf(issuerUri));
686 } else {
687 debugs(81, 3, "found a certificate with no IAI, " <<
688 "signed by a missing issuer certificate: " << *cert);
689 // We could short-circuit here, proceeding to chain validation
690 // that is likely to fail. Instead, we keep going because we
691 // hope that if we find at least one certificate to fetch, it
692 // will complete the chain (that contained extra certificates).
693 }
694 }
695 }
696
697 // Check if there are URIs to download from and if yes start downloading
698 // the first in queue.
699 if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) {
700 startCertDownloading(urlsOfMissingCerts.front());
701 urlsOfMissingCerts.pop();
702 return;
703 }
704
705 resumeNegotiation();
706}
707
708void
710{
711 Must(Comm::IsConnOpen(serverConnection()));
712 auto &sconn = *fd_table[serverConnection()->fd].ssl;
713
714 // We download the missing certificate(s) once. We would prefer to clear
715 // this right after the first validation, but that ideal place is _inside_
716 // OpenSSL if validation is triggered by SSL_connect(). That function and
717 // our OpenSSL verify_callback function (\ref OpenSSL_vcb_disambiguation)
718 // may be called multiple times, so we cannot reset there.
719 auto &callerHandlesMissingCertificates = Ssl::VerifyCallbackParameters::At(sconn).callerHandlesMissingCertificates;
720 Must(callerHandlesMissingCertificates);
721 callerHandlesMissingCertificates = false;
722
723 suspendNegotiation(ioResult);
724
725 if (!computeMissingCertificateUrls(sconn))
726 return resumeNegotiation();
727
728 assert(!urlsOfMissingCerts.empty());
729 startCertDownloading(urlsOfMissingCerts.front());
730 urlsOfMissingCerts.pop();
731}
732
734bool
736{
737 const auto certs = SSL_get_peer_cert_chain(&sconn);
738 if (!certs) {
739 debugs(83, 3, "nothing to bootstrap the fetch with");
740 return false;
741 }
742 debugs(83, 5, "server certificates: " << sk_X509_num(certs));
743
744 const auto ctx = getTlsContext();
745 if (!Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, *certs, ctx))
746 return false; // missingChainCertificatesUrls() reports the exact reason
747
748 debugs(83, 5, "URLs: " << urlsOfMissingCerts.size());
749 assert(!urlsOfMissingCerts.empty());
750 return true;
751}
752
753void
755{
756 debugs(83, 5, "after " << ioResult);
757 Must(!isSuspended());
758 suspendedError_ = new Security::IoResult(ioResult);
759 Must(isSuspended());
760 // negotiations resume with a resumeNegotiation() call
761}
762
763void
765{
766 Must(isSuspended());
767
768 auto lastError = suspendedError_; // may be reset below
769 suspendedError_ = nullptr;
770
771 auto &sconn = *fd_table[serverConnection()->fd].ssl;
772 if (!Ssl::VerifyConnCertificates(sconn, downloadedCerts)) {
773 // simulate an earlier SSL_connect() failure with a new error
774 // TODO: When we can use Security::ErrorDetail, we should resume with a
775 // detailed _validation_ error, not just a generic SSL_ERROR_SSL!
776 const ErrorDetail::Pointer errorDetail = new ErrorDetail(SQUID_TLS_ERR_CONNECT, SSL_ERROR_SSL, 0);
777 lastError = new Security::IoResult(errorDetail);
778 }
779
780 handleNegotiationResult(*lastError);
781}
782
783#endif //USE_OPENSSL
784
#define ScheduleCallHere(call)
Definition: AsyncCall.h:164
AsyncCall * asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:154
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
ErrorDetail::Pointer MakeNamedErrorDetail(const char *name)
Definition: Detail.cc:54
PconnPool * fwdPconnPool
a collection of previously used persistent Squid-to-peer HTTP(S) connections
Definition: FwdState.cc:76
#define true
Definition: GnuRegex.c:241
#define false
Definition: GnuRegex.c:240
RawPointerT< Pointer > RawPointer(const char *label, const Pointer &ptr)
convenience wrapper for creating RawPointerT<> objects
Definition: IoManip.h:36
CBDATA_NAMESPACED_CLASS_INIT(Security, PeerConnector)
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
#define Must(condition)
Definition: TextException.h:71
int conn
the current server connection FD
Definition: Transport.cc:26
void error(char *format,...)
#define acl_access
Definition: forward.h:45
#define assert(EX)
Definition: assert.h:19
static char server[MAXLINE]
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const
assigns uninitialized adapted_request and url ALE components
const Security::CertErrors * sslErrors
Security::CertPointer serverCert
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
bool allowed() const
Definition: Acl.h:150
virtual CallDialer * getDialer()=0
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:97
virtual void start()
called by AsyncStart; do not call directly
Definition: AsyncJob.cc:44
virtual void swanSong()
Definition: AsyncJob.h:59
bool push_back_unique(C const &element)
Definition: CbDataList.h:87
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
static bool Enabled(const int section, const int level)
whether debugging the given section and the given level produces output
Definition: Stream.h:79
static std::ostream & Extra(std::ostream &os)
prefixes each grouped debugs() line after the first one in the group
Definition: Stream.h:117
Callback data to use with Downloader callbacks.
Definition: Downloader.h:36
static ErrorState * NewForwarding(err_type, HttpRequestPointer &, const AccessLogEntryPointer &)
Creates a general request forwarding error with the right http_status.
Definition: errorpage.cc:675
void detailError(const ErrorDetail::Pointer &dCode)
set error type-specific detail code
Definition: errorpage.h:109
Definition: MemBuf.h:24
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:209
char * content()
start of the added data
Definition: MemBuf.h:41
void reset()
Definition: MemBuf.cc:129
void terminate()
Definition: MemBuf.cc:241
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
void noteUses(int uses)
Definition: pconn.cc:536
CallDialer to allow use Downloader objects within PeerConnector class.
virtual void dial(AsyncCall &)
CbcPointer< Security::PeerConnector > peerConnector_
The Security::PeerConnector object.
virtual bool canDial(AsyncCall &)
PeerConnectorCertDownloaderDialer(Method method, Security::PeerConnector *pc)
Method method_
The Security::PeerConnector method to dial.
Definition: SBuf.h:94
const char * rawContent() const
Definition: SBuf.cc:509
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
int sysError() const
Definition: ErrorDetail.h:66
a summary a TLS I/O operation outcome
Definition: Io.h:19
ErrorDetailPointer errorDetail
ioError case details (or nil)
Definition: Io.h:38
Category category
primary outcome classification
Definition: Io.h:40
const char * errorDescription
a brief description of an error
Definition: Io.h:43
void resetWithoutLocking(T *t)
Reset raw pointer - unlock any previous one and save new one without locking.
Callback dialer API to allow PeerConnector to set the answer.
Definition: PeerConnector.h:57
void countFailingConnection()
updates connection usage history before the connection is closed
virtual bool doneAll() const
whether positive goal has been reached
void sslCrtvdHandleReply(Ssl::CertValidationResponsePointer)
Process response from cert validator helper.
void negotiateSsl()
Comm::SetSelect() callback. Direct calls tickle/resume negotiations.
void commCloseHandler(const CommCloseCbParams &params)
The comm_close callback handler.
void certDownloadingDone(SBuf &object, int status)
Called by Downloader after a certificate object downloaded.
AsyncCall::Pointer closeHandler
we call this when the connection closed
virtual void fillChecklist(ACLFilledChecklist &) const
configure the given checklist (to reflect the current transaction state)
void startCertDownloading(SBuf &url)
Start downloading procedure for the given URL.
virtual bool initialize(Security::SessionPointer &)
void bail(ErrorState *error)
sends the given error to the initiator
virtual void noteNegotiationError(const Security::ErrorDetailPointer &)
Called when the SSL_connect function aborts with an SSL negotiation error.
EncryptorAnswer & answer()
convenience method to get to the answer fields
virtual void start()
Preps connection and SSL state. Calls negotiate().
virtual const char * status() const
internal cleanup; do not call directly
virtual void noteWantWrite()
bool computeMissingCertificateUrls(const Connection &)
finds URLs of (some) missing intermediate certificates or returns false
void handleMissingCertificates(const Security::IoResult &lastError)
Either initiates fetching of missing certificates or bails with an error.
void resumeNegotiation()
Resumes TLS negotiation paused by suspendNegotiation()
void handleNegotiationResult(const Security::IoResult &)
Called after each negotiation step to handle the result.
void commTimeoutHandler(const CommTimeoutCbParams &)
The connection read timeout callback handler.
AsyncCall::Pointer callback
we call this with the results
static void NegotiateSsl(int fd, void *data)
A wrapper for Comm::SetSelect() notifications.
void disconnect()
a bail(), sendSuccess() helper: stops monitoring the connection
void sendSuccess()
sends the encrypted connection to the initiator
unsigned int certDownloadNestingLevel() const
the number of concurrent PeerConnector jobs waiting for us
void callBack()
a bail(), sendSuccess() helper: sends results to the initiator
Comm::ConnectionPointer serverConn
TCP connection to the peer.
void suspendNegotiation(const Security::IoResult &lastError)
Security::CertErrors * sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, ErrorDetailPointer &)
Check SSL errors returned from cert validator against sslproxy_cert_error access list.
PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const AccessLogEntryPointer &alp, const time_t timeout=0)
acl_access * cert_error
Definition: SquidConfig.h:517
static void Submit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &)
Submit crtd request message to external crtd server.
Definition: helper.cc:301
Security::SessionPointer ssl
Security::CertErrors * errors
The list of errors detected.
std::string domainName
The server name.
RecvdErrors errors
The list of parsed errors.
const Security::TlsDetails::Pointer & receivedHelloDetails() const
Definition: bio.h:170
static VerifyCallbackParameters & At(Security::Connection &)
Definition: support.cc:551
static VerifyCallbackParameters * New(Security::Connection &)
Definition: support.cc:539
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:947
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:976
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:589
int commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition: comm.cc:615
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
#define COMM_SELECT_READ
Definition: defines.h:24
#define COMM_SELECT_WRITE
Definition: defines.h:25
@ ERR_CONNECT_FAIL
Definition: forward.h:30
@ ERR_SECURE_CONNECT_FAIL
Definition: forward.h:31
@ ERR_GATEWAY_FAILURE
Definition: forward.h:67
@ ERR_SOCKET_FAILURE
Definition: forward.h:32
#define fd_table
Definition: fde.h:189
int ssl_ex_index_server
const char * dash_str
int ssl_ex_index_ssl_errors
int ssl_ex_index_cert_error_check
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
time_t MortalReadTimeout(const time_t startTime, const time_t lifetimeLimit)
maximum read delay for readers with limited lifetime
Definition: Read.cc:248
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:223
SBuf ErrorDetail
Definition: Esi.h:29
@ Error
Definition: ResultCode.h:19
@ Okay
Definition: ResultCode.h:18
@ scGatewayTimeout
Definition: StatusCode.h:75
@ scInternalServerError
Definition: StatusCode.h:71
@ scBadGateway
Definition: StatusCode.h:73
@ scServiceUnavailable
Definition: StatusCode.h:74
Network/connection security abstraction layer.
Definition: Connection.h:34
std::shared_ptr< SSL_CTX > ContextPointer
Definition: Context.h:29
SSL Connection
Definition: Session.h:45
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:49
IoResult Connect(Comm::Connection &transport)
establish a TLS connection over the specified from-Squid transport connection
Definition: Io.cc:212
CbDataList< Security::CertError > CertErrors
Holds a list of X.509 certificate errors.
Definition: forward.h:70
bool CreateClientSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx)
Definition: Session.cc:183
bool VerifyConnCertificates(Security::Connection &, const Ssl::X509_STACK_Pointer &extraCerts)
Definition: support.cc:441
Security::CertPointer findIssuerCertificate(X509 *cert, const STACK_OF(X509) *serverCertificates, const Security::ContextPointer &context)
Definition: support.cc:1207
bool missingChainCertificatesUrls(std::queue< SBuf > &URIs, const STACK_OF(X509) &serverCertificates, const Security::ContextPointer &context)
Definition: support.cc:1233
Config TheConfig
Definition: Config.cc:12
const char * findIssuerUri(X509 *cert)
finds certificate issuer URI in the Authority Info Access extension
Definition: support.cc:1090
void peerConnectFailed(CachePeer *p)
Definition: neighbors.cc:1298
void * BIO_get_data(BIO *table)
Definition: openssl.h:62
@ SQUID_TLS_ERR_CONNECT
failure to establish a connection with a TLS server
Definition: forward.h:226
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors