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