PeerConnector.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* 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#endif
37
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 // watch for external connection closures
53 Must(!fd_table[serverConn->fd].closing());
57}
58
60
62{
63 return (!callback || callback->canceled()) && AsyncJob::doneAll();
64}
65
67void
69{
71 debugs(83, 5, "this=" << (void*)this);
72
73 // we own this Comm::Connection object and its fd exclusively, but must bail
74 // if others started closing the socket while we were waiting to start()
75 assert(Comm::IsConnOpen(serverConn));
76 if (fd_table[serverConn->fd].closing()) {
77 bail(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al));
78 return;
79 }
80
82 if (initialize(tmp))
83 negotiate();
84 else
85 mustStop("Security::PeerConnector TLS socket initialize failed");
86}
87
88void
90{
91 if (!checklist.al)
92 checklist.al = al;
93 checklist.syncAle(request.getRaw(), nullptr);
94 // checklist.fd(fd); XXX: need client FD here
95
96#if USE_OPENSSL
97 if (!checklist.serverCert) {
98 if (const auto session = fd_table[serverConnection()->fd].ssl.get())
99 checklist.serverCert.resetWithoutLocking(SSL_get_peer_certificate(session));
100 }
101#else
102 // checklist.serverCert is not maintained in other builds
103#endif
104}
105
106void
108{
109 debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data);
110
111 closeHandler = nullptr;
112
113 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
114 static const auto d = MakeNamedErrorDetail("TLS_CONNECT_CLOSE");
115 err->detailError(d);
116
117 if (serverConn) {
118 countFailingConnection(err);
119 serverConn->noteClosure();
120 serverConn = nullptr;
121 }
122
123 bail(err);
124}
125
126void
128{
129 debugs(83, 5, serverConnection() << " timedout. this=" << (void*)this);
130 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scGatewayTimeout, request.getRaw(), al);
131 static const auto d = MakeNamedErrorDetail("TLS_CONNECT_TIMEOUT");
132 err->detailError(d);
133 bail(err);
134}
135
136bool
138{
139 Must(Comm::IsConnOpen(serverConnection()));
140
141 Security::ContextPointer ctx(getTlsContext());
142 debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
143
144 if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
145 const auto xerrno = errno;
146 if (!ctx) {
147 debugs(83, DBG_IMPORTANT, "ERROR: initializing TLS connection: No security context.");
148 } // else CreateClientSession() did the appropriate debugs() already
149 const auto anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw(), al);
150 anErr->xerrno = xerrno;
151 noteNegotiationDone(anErr);
152 bail(anErr);
153 return false;
154 }
155
156 // A TLS/SSL session has now been created for the connection and stored in fd_table
157 serverSession = fd_table[serverConnection()->fd].ssl;
158 debugs(83, 5, serverConnection() << ", session=" << (void*)serverSession.get());
159
160#if USE_OPENSSL
161 // If CertValidation Helper used do not lookup checklist for errors,
162 // but keep a list of errors to send it to CertValidator
163 if (!Ssl::TheConfig.ssl_crt_validator) {
164 // Create the ACL check list now, while we have access to more info.
165 // The list is used in ssl_verify_cb() and is freed in ssl_free().
166 // XXX: This info may change, especially if we fetch missing certs.
167 // TODO: Remove ACLFilledChecklist::sslErrors and other pre-computed
168 // state in favor of the ACLs accessing current/fresh info directly.
169 if (acl_access *acl = ::Config.ssl_client.cert_error) {
170 ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
171 fillChecklist(*check);
172 SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
173 }
174 }
175
176 // Protect from cycles in the certificate dependency graph: TLS site S1 is
177 // missing certificate C1 located at TLS site S2. TLS site S2 is missing
178 // certificate C2 located at [...] TLS site S1.
179 const auto cycle = certDownloadNestingLevel() >= MaxNestedDownloads;
180 if (cycle)
181 debugs(83, 3, "will not fetch any missing certificates; suspecting cycle: " << certDownloadNestingLevel() << '/' << MaxNestedDownloads);
182 const auto sessData = Ssl::VerifyCallbackParameters::New(*serverSession);
183 // when suspecting a cycle, break it by not fetching any missing certs
184 sessData->callerHandlesMissingCertificates = !cycle;
185#endif
186
187 return true;
188}
189
190void
192{
193 Must(Comm::IsConnOpen(serverConnection()));
194
195 const int fd = serverConnection()->fd;
196 Security::SessionPointer session(fd_table[fd].ssl);
197
198 // retrieve TLS server negotiated information if any
199 serverConnection()->tlsNegotiations()->retrieveNegotiatedInfo(session);
200
201#if USE_OPENSSL
202 // retrieve TLS parsed extra info
203 BIO *b = SSL_get_rbio(session.get());
204 Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
205 if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails())
206 serverConnection()->tlsNegotiations()->retrieveParsedInfo(details);
207#endif
208}
209
210void
212{
213 Must(Comm::IsConnOpen(serverConnection()));
214
215 const int fd = serverConnection()->fd;
216 if (fd_table[fd].closing())
217 return;
218
219 const auto result = Security::Connect(*serverConnection());
220
221#if USE_OPENSSL
222 auto &sconn = *fd_table[fd].ssl;
223
224 // log ASAP, even if the handshake has not completed (or failed)
225 keyLogger.checkpoint(sconn, *this);
226
227 // OpenSSL v1 APIs do not allow unthreaded applications like Squid to fetch
228 // missing certificates _during_ OpenSSL certificate validation. Our
229 // handling of X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (abbreviated
230 // here as EUNABLE) approximates what would happen if we did (attempt to)
231 // fetch any missing certificates during OpenSSL certificate validation.
232 // * We did not hide EUNABLE; SSL_connect() was successful: Handle success.
233 // * We did not hide EUNABLE; SSL_connect() reported some error E: Honor E.
234 // * We hid EUNABLE; SSL_connect() was successful: Remember success and try
235 // to fetch the missing certificates. If all goes well, honor success.
236 // * We hid EUNABLE; SSL_connect() reported EUNABLE: Warn but honor EUNABLE.
237 // * We hid EUNABLE; SSL_connect() reported some EOTHER: Remember EOTHER and
238 // try to fetch the missing certificates. If all goes well, honor EOTHER.
239 // If fetching or post-fetching validation fails, then honor that failure
240 // because EOTHER would not have happened if we fetched during validation.
241 if (auto &hidMissingIssuer = Ssl::VerifyCallbackParameters::At(sconn).hidMissingIssuer) {
242 hidMissingIssuer = false; // prep for the next SSL_connect()
243
244 if (result.category == IoResult::ioSuccess ||
245 !(result.errorDetail && result.errorDetail->errorNo() == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY))
246 return handleMissingCertificates(result);
247
248 debugs(83, DBG_IMPORTANT, "ERROR: Squid BUG: Honoring unexpected SSL_connect() failure: X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY");
249 // fall through to regular error handling
250 }
251#endif
252
253 handleNegotiationResult(result);
254}
255
256void
258{
259 switch (result.category) {
261 recordNegotiationDetails();
262 if (sslFinalized() && callback)
263 sendSuccess();
264 return; // we may be gone by now
265
267 noteWantRead();
268 return;
269
271 noteWantWrite();
272 return;
273
275 break; // fall through to error handling
276 }
277
278 // TODO: Honor result.important when working in a reverse proxy role?
279 debugs(83, 2, "ERROR: Cannot establish a TLS connection to " << serverConnection() << ':' <<
280 Debug::Extra << "problem: " << result.errorDescription <<
281 RawPointer("detail: ", result.errorDetail).asExtra());
282 recordNegotiationDetails();
283 noteNegotiationError(result.errorDetail);
284}
285
286bool
288{
289#if USE_OPENSSL
290 if (Ssl::TheConfig.ssl_crt_validator && useCertValidator_) {
291 Must(Comm::IsConnOpen(serverConnection()));
292 const int fd = serverConnection()->fd;
293 Security::SessionPointer session(fd_table[fd].ssl);
294
295 Ssl::CertValidationRequest validationRequest;
296 // WARNING: Currently we do not use any locking for 'errors' member
297 // of the Ssl::CertValidationRequest class. In this code the
298 // Ssl::CertValidationRequest object used only to pass data to
299 // Ssl::CertValidationHelper::submit method.
300 validationRequest.ssl = session;
301 if (SBuf *dName = (SBuf *)SSL_get_ex_data(session.get(), ssl_ex_index_server))
302 validationRequest.domainName = dName->c_str();
303 if (Security::CertErrors *errs = static_cast<Security::CertErrors *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors)))
304 // validationRequest disappears on return so no need to cbdataReference
305 validationRequest.errors = errs;
306 try {
307 debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
308 const auto call = asyncCallback(83, 5, Security::PeerConnector::sslCrtvdHandleReply, this);
309 Ssl::CertValidationHelper::Submit(validationRequest, call);
310 return false;
311 } catch (const std::exception &e) {
312 debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
313 "request for " << validationRequest.domainName <<
314 " certificate: " << e.what() << "; will now block to " <<
315 "validate that certificate.");
316 // fall through to do blocking in-process generation.
317 const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
318
319 noteNegotiationDone(anErr);
320 bail(anErr);
321 return true;
322 }
323 }
324#endif
325
326 noteNegotiationDone(nullptr);
327 return true;
328}
329
330#if USE_OPENSSL
331void
333{
334 Must(validationResponse != nullptr);
335 Must(Comm::IsConnOpen(serverConnection()));
336
337 ErrorDetail::Pointer errDetails;
338 bool validatorFailed = false;
339
340 if (Debug::Enabled(83, 5)) {
341 Security::SessionPointer ssl(fd_table[serverConnection()->fd].ssl);
342 SBuf *server = static_cast<SBuf *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_server));
343 debugs(83, 5, "cert validation result: " << validationResponse->resultCode << RawPointer(" host: ", server));
344 }
345
346 if (validationResponse->resultCode == ::Helper::Error) {
347 if (Security::CertErrors *errs = sslCrtvdCheckForErrors(*validationResponse, errDetails)) {
348 Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
349 Security::CertErrors *oldErrs = static_cast<Security::CertErrors*>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors));
350 SSL_set_ex_data(session.get(), ssl_ex_index_ssl_errors, (void *)errs);
351 delete oldErrs;
352 }
353 } else if (validationResponse->resultCode != ::Helper::Okay)
354 validatorFailed = true;
355
356 if (!errDetails && !validatorFailed) {
357 noteNegotiationDone(nullptr);
358 if (callback)
359 sendSuccess();
360 return;
361 }
362
363 ErrorState *anErr = nullptr;
364 if (validatorFailed) {
365 anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
366 } else {
367 anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
368 anErr->detailError(errDetails);
369 /*anErr->xerrno= Should preserved*/
370 }
371
372 noteNegotiationDone(anErr);
373 bail(anErr);
374 return;
375}
376#endif
377
378#if USE_OPENSSL
384{
385 Must(Comm::IsConnOpen(serverConnection()));
386
387 ACLFilledChecklist *check = nullptr;
388 Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
389
390 if (acl_access *acl = ::Config.ssl_client.cert_error) {
391 check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
392 fillChecklist(*check);
393 }
394
395 Security::CertErrors *errs = nullptr;
396 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
397 for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
398 debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
399
400 assert(i->error_no != SSL_ERROR_NONE);
401
402 if (!errDetails) {
403 bool allowed = false;
404 if (check) {
405 const auto sslErrors = std::make_unique<Security::CertErrors>(Security::CertError(i->error_no, i->cert, i->error_depth));
406 check->sslErrors = sslErrors.get();
407 if (check->fastCheck().allowed())
408 allowed = true;
409 check->sslErrors.clear();
410 }
411 // else the Config.ssl_client.cert_error access list is not defined
412 // and the first error will cause the error page
413
414 if (allowed) {
415 debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer");
416 } else {
417 debugs(83, 5, "confirming SSL error " << i->error_no);
418 const auto &brokenCert = i->cert;
419 Security::CertPointer peerCert(SSL_get_peer_certificate(session.get()));
420 const char *aReason = i->error_reason.empty() ? nullptr : i->error_reason.c_str();
421 errDetails = new ErrorDetail(i->error_no, peerCert, brokenCert, aReason);
422 }
423 }
424
425 if (!errs)
426 errs = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
427 else
428 errs->push_back_unique(Security::CertError(i->error_no, i->cert, i->error_depth));
429 }
430 if (check)
431 delete check;
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(error);
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(), error ? error->httpStatus : Http::scNone);
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 ContextPointer ctx(getTlsContext());
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 ctx = getTlsContext();
716 if (!Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, *certs, ctx))
717 return false; // missingChainCertificatesUrls() reports the exact reason
718
719 debugs(83, 5, "URLs: " << urlsOfMissingCerts.size());
720 assert(!urlsOfMissingCerts.empty());
721 return true;
722}
723
724void
726{
727 debugs(83, 5, "after " << ioResult);
728 Must(!isSuspended());
729 suspendedError_ = new Security::IoResult(ioResult);
730 Must(isSuspended());
731 // negotiations resume with a resumeNegotiation() call
732}
733
734void
736{
737 Must(isSuspended());
738
739 auto lastError = suspendedError_; // may be reset below
740 suspendedError_ = nullptr;
741
742 auto &sconn = *fd_table[serverConnection()->fd].ssl;
743 if (!Ssl::VerifyConnCertificates(sconn, downloadedCerts)) {
744 // simulate an earlier SSL_connect() failure with a new error
745 // TODO: When we can use Security::ErrorDetail, we should resume with a
746 // detailed _validation_ error, not just a generic SSL_ERROR_SSL!
747 const ErrorDetail::Pointer errorDetail = new ErrorDetail(SQUID_TLS_ERR_CONNECT, SSL_ERROR_SSL, 0);
748 lastError = new Security::IoResult(errorDetail);
749 }
750
751 handleNegotiationResult(*lastError);
752}
753
754#endif //USE_OPENSSL
755
#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.
Definition: AsyncJobCalls.h:69
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
void NoteOutgoingConnectionFailure(CachePeer *const peer, const Http::StatusCode code)
Definition: CachePeer.h:243
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:43
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
#define Must(condition)
Definition: TextException.h:75
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:17
static char server[MAXLINE]
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
Security::CertPointer serverCert
CbcPointer< Security::CertErrors > sslErrors
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
bool allowed() const
Definition: Acl.h:156
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:1313
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:674
void detailError(const ErrorDetail::Pointer &dCode)
set error type-specific detail code
Definition: errorpage.h:111
Definition: MemBuf.h:24
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:536
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:70
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.
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.
void swanSong() override
virtual bool initialize(Security::SessionPointer &)
PeerConnector(const Comm::ConnectionPointer &aServerConn, const AsyncCallback< EncryptorAnswer > &, const AccessLogEntryPointer &alp, const time_t timeout=0)
void countFailingConnection(const ErrorState *)
updates connection usage history before the connection is closed
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
Definition: SquidConfig.h:509
static void Submit(const Ssl::CertValidationRequest &, const Callback &)
Submit crtd request message to external crtd server.
Definition: helper.cc:299
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
int commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:595
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:949
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:978
int commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition: comm.cc:621
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#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
@ scNone
Definition: StatusCode.h:21
@ scBadGateway
Definition: StatusCode.h:73
@ scServiceUnavailable
Definition: StatusCode.h:74
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:74
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 * 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:230

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors