]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/PeerConnector.cc
Do not blame cache_peer for CONNECT errors (#1772)
[thirdparty/squid.git] / src / security / PeerConnector.cc
CommitLineData
a23223bf 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
a23223bf 3 *
bbc27441
AJ
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.
a23223bf
CT
7 */
8
32f1ca3f 9/* DEBUG: section 83 TLS Server/Peer negotiation */
bbc27441 10
a23223bf
CT
11#include "squid.h"
12#include "acl/FilledChecklist.h"
e5ddd4ce 13#include "base/AsyncCallbacks.h"
675b8408 14#include "base/IoManip.h"
022dbabd 15#include "CachePeer.h"
a23223bf 16#include "comm/Loops.h"
f5e17947 17#include "comm/Read.h"
55369ae6 18#include "Downloader.h"
a23223bf
CT
19#include "errorpage.h"
20#include "fde.h"
25b0ce45 21#include "FwdState.h"
54fb1cbf 22#include "http/Stream.h"
a23223bf 23#include "HttpRequest.h"
25b0ce45
CT
24#include "neighbors.h"
25#include "pconn.h"
907831e6 26#include "security/Certificate.h"
bb4cc8e6 27#include "security/Io.h"
36698640 28#include "security/NegotiationHistory.h"
a72b6e88 29#include "security/PeerConnector.h"
7f4e9b73 30#include "SquidConfig.h"
a72b6e88 31#if USE_OPENSSL
31855516 32#include "ssl/bio.h"
a23223bf
CT
33#include "ssl/cert_validate_message.h"
34#include "ssl/Config.h"
a23223bf 35#include "ssl/helper.h"
a72b6e88 36#endif
a23223bf 37
e5ddd4ce 38Security::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, const AsyncCallback<EncryptorAnswer> &aCallback, const AccessLogEntryPointer &alp, const time_t timeout):
a72b6e88 39 AsyncJob("Security::PeerConnector"),
25b0ce45 40 noteFwdPconnUse(false),
f53969cc 41 serverConn(aServerConn),
d4ddb3e6 42 al(alp),
f53969cc
SM
43 callback(aCallback),
44 negotiationTimeout(timeout),
45 startTime(squid_curtime),
54fb1cbf 46 useCertValidator_(true),
4e526b93 47 certsDownloads(0)
a23223bf 48{
25b0ce45
CT
49 debugs(83, 5, serverConn);
50
25b0ce45
CT
51 // watch for external connection closures
52 Must(Comm::IsConnOpen(serverConn));
53 Must(!fd_table[serverConn->fd].closing());
54 typedef CommCbMemFunT<Security::PeerConnector, CommCloseCbParams> Dialer;
55 closeHandler = JobCallback(9, 5, Dialer, this, Security::PeerConnector::commCloseHandler);
56 comm_add_close_handler(serverConn->fd, closeHandler);
a23223bf
CT
57}
58
800967af
CT
59Security::PeerConnector::~PeerConnector() = default;
60
a72b6e88 61bool Security::PeerConnector::doneAll() const
a23223bf
CT
62{
63 return (!callback || callback->canceled()) && AsyncJob::doneAll();
64}
65
66/// Preps connection and SSL state. Calls negotiate().
67void
a72b6e88 68Security::PeerConnector::start()
a23223bf
CT
69{
70 AsyncJob::start();
9c8549cf 71 debugs(83, 5, "this=" << (void*)this);
a23223bf 72
25b0ce45
CT
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
eba8d9bb 81 Security::SessionPointer tmp;
25b0ce45 82 if (initialize(tmp))
0166128b 83 negotiate();
ae3ac744
AJ
84 else
85 mustStop("Security::PeerConnector TLS socket initialize failed");
a23223bf
CT
86}
87
e227da8d
AR
88void
89Security::PeerConnector::fillChecklist(ACLFilledChecklist &checklist) const
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
a23223bf 106void
a72b6e88 107Security::PeerConnector::commCloseHandler(const CommCloseCbParams &params)
a23223bf 108{
2b6b1bcb
AR
109 debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data);
110
25b0ce45 111 closeHandler = nullptr;
022dbabd
EB
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
2b6b1bcb 117 if (serverConn) {
2e7dea3c 118 countFailingConnection();
2b6b1bcb
AR
119 serverConn->noteClosure();
120 serverConn = nullptr;
121 }
25b0ce45 122
25b0ce45 123 bail(err);
a23223bf
CT
124}
125
126void
25b0ce45 127Security::PeerConnector::commTimeoutHandler(const CommTimeoutCbParams &)
a23223bf 128{
25b0ce45
CT
129 debugs(83, 5, serverConnection() << " timedout. this=" << (void*)this);
130 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scGatewayTimeout, request.getRaw(), al);
83b053a0
CT
131 static const auto d = MakeNamedErrorDetail("TLS_CONNECT_TIMEOUT");
132 err->detailError(d);
25b0ce45 133 bail(err);
a23223bf
CT
134}
135
eba8d9bb 136bool
0166128b 137Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
a23223bf 138{
2b6b1bcb
AR
139 Must(Comm::IsConnOpen(serverConnection()));
140
b23f5f9c 141 Security::ContextPointer ctx(getTlsContext());
9c8549cf 142 debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
a23223bf 143
2e0d4c02 144 if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
ea574635 145 const auto xerrno = errno;
2e0d4c02 146 if (!ctx) {
d816f28d 147 debugs(83, DBG_IMPORTANT, "ERROR: initializing TLS connection: No security context.");
2e0d4c02 148 } // else CreateClientSession() did the appropriate debugs() already
7e6eabbc 149 const auto anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw(), al);
ea574635 150 anErr->xerrno = xerrno;
1b091aec
CT
151 noteNegotiationDone(anErr);
152 bail(anErr);
eba8d9bb 153 return false;
a23223bf
CT
154 }
155
157c5ace 156 // A TLS/SSL session has now been created for the connection and stored in fd_table
eba8d9bb 157 serverSession = fd_table[serverConnection()->fd].ssl;
9c8549cf 158 debugs(83, 5, serverConnection() << ", session=" << (void*)serverSession.get());
157c5ace 159
2e0d4c02 160#if USE_OPENSSL
a23223bf
CT
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().
757a738c
AR
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.
a23223bf
CT
169 if (acl_access *acl = ::Config.ssl_client.cert_error) {
170 ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
e227da8d 171 fillChecklist(*check);
eba8d9bb 172 SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
a23223bf
CT
173 }
174 }
800967af
CT
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;
2e0d4c02 185#endif
a72b6e88 186
eba8d9bb 187 return true;
a23223bf
CT
188}
189
36698640 190void
a72b6e88 191Security::PeerConnector::recordNegotiationDetails()
36698640 192{
2b6b1bcb
AR
193 Must(Comm::IsConnOpen(serverConnection()));
194
36698640 195 const int fd = serverConnection()->fd;
ad23e748 196 Security::SessionPointer session(fd_table[fd].ssl);
36698640
CT
197
198 // retrieve TLS server negotiated information if any
ad23e748
AJ
199 serverConnection()->tlsNegotiations()->retrieveNegotiatedInfo(session);
200
201#if USE_OPENSSL
36698640 202 // retrieve TLS parsed extra info
ad23e748 203 BIO *b = SSL_get_rbio(session.get());
2a268a06 204 Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
36698640
CT
205 if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails())
206 serverConnection()->tlsNegotiations()->retrieveParsedInfo(details);
a72b6e88 207#endif
36698640
CT
208}
209
a23223bf 210void
0166128b 211Security::PeerConnector::negotiate()
a23223bf 212{
2b6b1bcb
AR
213 Must(Comm::IsConnOpen(serverConnection()));
214
a23223bf 215 const int fd = serverConnection()->fd;
0166128b
AJ
216 if (fd_table[fd].closing())
217 return;
218
83b053a0 219 const auto result = Security::Connect(*serverConnection());
800967af
CT
220
221#if USE_OPENSSL
222 auto &sconn = *fd_table[fd].ssl;
223
e227da8d
AR
224 // log ASAP, even if the handshake has not completed (or failed)
225 keyLogger.checkpoint(sconn, *this);
226
800967af
CT
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 ||
f70aedc4 245 !(result.errorDetail && result.errorDetail->errorNo() == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY))
800967af
CT
246 return handleMissingCertificates(result);
247
d816f28d 248 debugs(83, DBG_IMPORTANT, "ERROR: Squid BUG: Honoring unexpected SSL_connect() failure: X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY");
800967af
CT
249 // fall through to regular error handling
250 }
251#endif
252
253 handleNegotiationResult(result);
254}
255
256void
257Security::PeerConnector::handleNegotiationResult(const Security::IoResult &result)
258{
83b053a0
CT
259 switch (result.category) {
260 case Security::IoResult::ioSuccess:
261 recordNegotiationDetails();
2b6b1bcb 262 if (sslFinalized() && callback)
83b053a0
CT
263 sendSuccess();
264 return; // we may be gone by now
a23223bf 265
83b053a0
CT
266 case Security::IoResult::ioWantRead:
267 noteWantRead();
268 return;
36698640 269
83b053a0
CT
270 case Security::IoResult::ioWantWrite:
271 noteWantWrite();
c91d4d4e
CT
272 return;
273
83b053a0
CT
274 case Security::IoResult::ioError:
275 break; // fall through to error handling
276 }
277
278 // TODO: Honor result.important when working in a reverse proxy role?
70638a6a
AR
279 debugs(83, 2, "ERROR: Cannot establish a TLS connection to " << serverConnection() << ':' <<
280 Debug::Extra << "problem: " << result.errorDescription <<
281 RawPointer("detail: ", result.errorDetail).asExtra());
83b053a0
CT
282 recordNegotiationDetails();
283 noteNegotiationError(result.errorDetail);
c91d4d4e
CT
284}
285
286bool
a72b6e88 287Security::PeerConnector::sslFinalized()
c91d4d4e 288{
a72b6e88 289#if USE_OPENSSL
1b091aec 290 if (Ssl::TheConfig.ssl_crt_validator && useCertValidator_) {
2b6b1bcb 291 Must(Comm::IsConnOpen(serverConnection()));
1b091aec 292 const int fd = serverConnection()->fd;
ad23e748 293 Security::SessionPointer session(fd_table[fd].ssl);
a23223bf 294
a23223bf 295 Ssl::CertValidationRequest validationRequest;
9c8549cf
AJ
296 // WARNING: Currently we do not use any locking for 'errors' member
297 // of the Ssl::CertValidationRequest class. In this code the
a23223bf
CT
298 // Ssl::CertValidationRequest object used only to pass data to
299 // Ssl::CertValidationHelper::submit method.
9c8549cf 300 validationRequest.ssl = session;
cb171ead
CT
301 if (SBuf *dName = (SBuf *)SSL_get_ex_data(session.get(), ssl_ex_index_server))
302 validationRequest.domainName = dName->c_str();
ad23e748 303 if (Security::CertErrors *errs = static_cast<Security::CertErrors *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors)))
a23223bf
CT
304 // validationRequest disappears on return so no need to cbdataReference
305 validationRequest.errors = errs;
a23223bf
CT
306 try {
307 debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
e5ddd4ce 308 const auto call = asyncCallback(83, 5, Security::PeerConnector::sslCrtvdHandleReply, this);
23da195f 309 Ssl::CertValidationHelper::Submit(validationRequest, call);
c91d4d4e 310 return false;
a23223bf
CT
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.
7e6eabbc 317 const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
1b091aec
CT
318
319 noteNegotiationDone(anErr);
a23223bf 320 bail(anErr);
c91d4d4e 321 return true;
a23223bf
CT
322 }
323 }
a72b6e88 324#endif
1b091aec 325
aee3523a 326 noteNegotiationDone(nullptr);
c91d4d4e 327 return true;
a23223bf
CT
328}
329
a72b6e88 330#if USE_OPENSSL
a23223bf 331void
e5ddd4ce 332Security::PeerConnector::sslCrtvdHandleReply(Ssl::CertValidationResponse::Pointer &validationResponse)
a23223bf 333{
aee3523a 334 Must(validationResponse != nullptr);
2b6b1bcb 335 Must(Comm::IsConnOpen(serverConnection()));
a23223bf 336
83b053a0 337 ErrorDetail::Pointer errDetails;
a23223bf 338 bool validatorFailed = false;
a23223bf 339
19ed078f
CT
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));
70638a6a 343 debugs(83, 5, "cert validation result: " << validationResponse->resultCode << RawPointer(" host: ", server));
19ed078f 344 }
a23223bf 345
088f0761 346 if (validationResponse->resultCode == ::Helper::Error) {
92e3827b 347 if (Security::CertErrors *errs = sslCrtvdCheckForErrors(*validationResponse, errDetails)) {
ad23e748
AJ
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);
088f0761
CT
351 delete oldErrs;
352 }
353 } else if (validationResponse->resultCode != ::Helper::Okay)
a23223bf
CT
354 validatorFailed = true;
355
356 if (!errDetails && !validatorFailed) {
aee3523a 357 noteNegotiationDone(nullptr);
2b6b1bcb
AR
358 if (callback)
359 sendSuccess();
a23223bf
CT
360 return;
361 }
362
aee3523a 363 ErrorState *anErr = nullptr;
a23223bf 364 if (validatorFailed) {
7e6eabbc 365 anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
a23223bf 366 } else {
7e6eabbc 367 anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw(), al);
83b053a0 368 anErr->detailError(errDetails);
a23223bf
CT
369 /*anErr->xerrno= Should preserved*/
370 }
371
1b091aec 372 noteNegotiationDone(anErr);
a23223bf 373 bail(anErr);
a23223bf
CT
374 return;
375}
a72b6e88 376#endif
a23223bf 377
a72b6e88 378#if USE_OPENSSL
a23223bf
CT
379/// Checks errors in the cert. validator response against sslproxy_cert_error.
380/// The first honored error, if any, is returned via errDetails parameter.
92e3827b
AJ
381/// The method returns all seen errors except SSL_ERROR_NONE as Security::CertErrors.
382Security::CertErrors *
83b053a0 383Security::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, ErrorDetail::Pointer &errDetails)
a23223bf 384{
2b6b1bcb
AR
385 Must(Comm::IsConnOpen(serverConnection()));
386
aee3523a 387 ACLFilledChecklist *check = nullptr;
fab3a825
CT
388 Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
389
d4ddb3e6 390 if (acl_access *acl = ::Config.ssl_client.cert_error) {
a23223bf 391 check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
e227da8d 392 fillChecklist(*check);
d4ddb3e6 393 }
a23223bf 394
92e3827b 395 Security::CertErrors *errs = nullptr;
a23223bf
CT
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) {
27a1c6de
AR
405 const auto sslErrors = std::make_unique<Security::CertErrors>(Security::CertError(i->error_no, i->cert, i->error_depth));
406 check->sslErrors = sslErrors.get();
06bf5384 407 if (check->fastCheck().allowed())
a23223bf 408 allowed = true;
27a1c6de 409 check->sslErrors.clear();
a23223bf
CT
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);
83b053a0 418 const auto &brokenCert = i->cert;
ad23e748 419 Security::CertPointer peerCert(SSL_get_peer_certificate(session.get()));
aee3523a 420 const char *aReason = i->error_reason.empty() ? nullptr : i->error_reason.c_str();
83b053a0 421 errDetails = new ErrorDetail(i->error_no, peerCert, brokenCert, aReason);
a23223bf 422 }
a23223bf
CT
423 }
424
425 if (!errs)
92e3827b 426 errs = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
a23223bf 427 else
92e3827b 428 errs->push_back_unique(Security::CertError(i->error_no, i->cert, i->error_depth));
a23223bf
CT
429 }
430 if (check)
431 delete check;
432
433 return errs;
434}
a72b6e88 435#endif
a23223bf
CT
436
437/// A wrapper for Comm::SetSelect() notifications.
438void
a72b6e88 439Security::PeerConnector::NegotiateSsl(int, void *data)
a23223bf 440{
ce9bb79c
CT
441 const auto pc = static_cast<PeerConnector::Pointer*>(data);
442 if (pc->valid())
443 (*pc)->negotiateSsl();
444 delete pc;
445}
446
447/// Comm::SetSelect() callback. Direct calls tickle/resume negotiations.
448void
449Security::PeerConnector::negotiateSsl()
450{
a23223bf 451 // Use job calls to add done() checks and other job logic/protections.
ce9bb79c 452 CallJobHere(83, 7, this, Security::PeerConnector, negotiate);
a23223bf
CT
453}
454
1b091aec 455void
a72b6e88 456Security::PeerConnector::noteWantRead()
1b091aec 457{
ca2526bd 458 debugs(83, 5, serverConnection());
f5e17947 459
2b6b1bcb
AR
460 Must(Comm::IsConnOpen(serverConnection()));
461 const int fd = serverConnection()->fd;
462
f5e17947 463 // read timeout to avoid getting stuck while reading from a silent server
25b0ce45
CT
464 typedef CommCbMemFunT<Security::PeerConnector, CommTimeoutCbParams> TimeoutDialer;
465 AsyncCall::Pointer timeoutCall = JobCallback(83, 5,
466 TimeoutDialer, this, Security::PeerConnector::commTimeoutHandler);
f5e17947 467 const auto timeout = Comm::MortalReadTimeout(startTime, negotiationTimeout);
25b0ce45 468 commSetConnTimeout(serverConnection(), timeout, timeoutCall);
f5e17947 469
ce9bb79c 470 Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, new Pointer(this), 0);
1b091aec 471}
7f4e9b73 472
1b091aec 473void
a72b6e88 474Security::PeerConnector::noteWantWrite()
1b091aec 475{
ca2526bd 476 debugs(83, 5, serverConnection());
2b6b1bcb
AR
477 Must(Comm::IsConnOpen(serverConnection()));
478
479 const int fd = serverConnection()->fd;
ce9bb79c 480 Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, new Pointer(this), 0);
1b091aec
CT
481 return;
482}
a23223bf 483
1b091aec 484void
d2f0c106 485Security::PeerConnector::noteNegotiationError(const Security::ErrorDetailPointer &detail)
1b091aec 486{
83b053a0 487 const auto anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request, al);
d2f0c106
CT
488 if (detail) {
489 anErr->xerrno = detail->sysError();
490 anErr->detailError(detail);
491 }
1b091aec 492 noteNegotiationDone(anErr);
a23223bf
CT
493 bail(anErr);
494}
495
2b6b1bcb
AR
496Security::EncryptorAnswer &
497Security::PeerConnector::answer()
498{
499 assert(callback);
e5ddd4ce 500 return callback.answer();
2b6b1bcb
AR
501}
502
a23223bf 503void
a72b6e88 504Security::PeerConnector::bail(ErrorState *error)
a23223bf 505{
2f8abb64 506 Must(error); // or the recipient will not know there was a problem
2b6b1bcb 507 answer().error = error;
a23223bf 508
2b6b1bcb 509 if (const auto failingConnection = serverConn) {
2e7dea3c 510 countFailingConnection();
2b6b1bcb
AR
511 disconnect();
512 failingConnection->close();
513 }
25b0ce45
CT
514
515 callBack();
25b0ce45
CT
516}
517
518void
519Security::PeerConnector::sendSuccess()
520{
2b6b1bcb
AR
521 assert(Comm::IsConnOpen(serverConn));
522 answer().conn = serverConn;
25b0ce45 523 disconnect();
2b6b1bcb
AR
524 callBack();
525}
526
527void
2e7dea3c 528Security::PeerConnector::countFailingConnection()
2b6b1bcb
AR
529{
530 assert(serverConn);
2e7dea3c 531 NoteOutgoingConnectionFailure(serverConn->getPeer());
2b6b1bcb
AR
532 // TODO: Calling PconnPool::noteUses() should not be our responsibility.
533 if (noteFwdPconnUse && serverConn->isOpen())
534 fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
25b0ce45
CT
535}
536
537void
538Security::PeerConnector::disconnect()
539{
2b6b1bcb
AR
540 const auto stillOpen = Comm::IsConnOpen(serverConn);
541
25b0ce45 542 if (closeHandler) {
2b6b1bcb
AR
543 if (stillOpen)
544 comm_remove_close_handler(serverConn->fd, closeHandler);
25b0ce45
CT
545 closeHandler = nullptr;
546 }
547
2b6b1bcb
AR
548 if (stillOpen)
549 commUnsetConnTimeout(serverConn);
550
551 serverConn = nullptr;
a23223bf
CT
552}
553
554void
a72b6e88 555Security::PeerConnector::callBack()
a23223bf 556{
2b6b1bcb 557 debugs(83, 5, "TLS setup ended for " << answer().conn);
e5ddd4ce
AR
558 ScheduleCallHere(callback.release());
559 Assure(done());
a23223bf
CT
560}
561
a23223bf 562void
a72b6e88 563Security::PeerConnector::swanSong()
a23223bf
CT
564{
565 // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any
566 AsyncJob::swanSong();
2b6b1bcb
AR
567
568 if (callback) {
569 // job-ending emergencies like handleStopRequest() or callException()
7e6eabbc 570 const auto anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al);
566f8310
AR
571 bail(anErr);
572 assert(!callback);
573 return;
574 }
a23223bf
CT
575}
576
577const char *
a72b6e88 578Security::PeerConnector::status() const
a23223bf
CT
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);
aee3523a 586 if (stopReason != nullptr) {
07e6d76e
AJ
587 buf.append("Stopped, reason:", 16);
588 buf.appendf("%s",stopReason);
a23223bf 589 }
2b6b1bcb 590 if (Comm::IsConnOpen(serverConn))
07e6d76e 591 buf.appendf(" FD %d", serverConn->fd);
f2e41480 592 buf.appendf(" %s%u]", id.prefix(), id.value);
a23223bf
CT
593 buf.terminate();
594
595 return buf.content();
596}
597
212e5aee 598#if USE_OPENSSL
800967af
CT
599/// the number of concurrent PeerConnector jobs waiting for us
600unsigned int
601Security::PeerConnector::certDownloadNestingLevel() const
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
55369ae6 613void
212e5aee 614Security::PeerConnector::startCertDownloading(SBuf &url)
55369ae6 615{
e5ddd4ce 616 const auto certCallback = asyncCallback(81, 4, Security::PeerConnector::certDownloadingDone, this);
ad05b958 617 const auto dl = new Downloader(url, certCallback,
bb4cc8e6 618 MasterXaction::MakePortless<XactionInitiator::initCertFetcher>(),
619 certDownloadNestingLevel() + 1);
2b6b1bcb 620 certDownloadWait.start(dl, certCallback);
55369ae6
AR
621}
622
623void
e5ddd4ce 624Security::PeerConnector::certDownloadingDone(DownloaderAnswer &downloaderAnswer)
55369ae6 625{
2b6b1bcb
AR
626 certDownloadWait.finish();
627
4b5ea8a6 628 ++certsDownloads;
e5ddd4ce 629 debugs(81, 5, "outcome: " << downloaderAnswer.outcome << "; certificate size: " << downloaderAnswer.resource.length());
55369ae6 630
2b6b1bcb 631 Must(Comm::IsConnOpen(serverConnection()));
800967af 632 const auto &sconn = *fd_table[serverConnection()->fd].ssl;
55369ae6 633
e5ddd4ce 634 // XXX: Do not parse the response when the download has failed.
25d0ea14 635 // Parse Certificate. Assume that it is in DER format.
c31381d0
CT
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
e5ddd4ce
AR
642 auto raw = reinterpret_cast<const unsigned char*>(downloaderAnswer.resource.rawContent());
643 if (auto cert = d2i_X509(nullptr, &raw, downloaderAnswer.resource.length())) {
907831e6 644 debugs(81, 5, "Retrieved certificate: " << *cert);
800967af
CT
645
646 if (!downloadedCerts)
647 downloadedCerts.reset(sk_X509_new_null());
648 sk_X509_push(downloadedCerts.get(), cert);
649
9fdbe165 650 ContextPointer ctx(getTlsContext());
800967af
CT
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)) {
907831e6 654 debugs(81, 5, "certificate " << *cert <<
800967af
CT
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, " <<
907831e6 659 "signed by a missing issuer certificate: " << *cert);
800967af
CT
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 }
55369ae6 665 }
55369ae6
AR
666 }
667
4b5ea8a6
CT
668 // Check if there are URIs to download from and if yes start downloading
669 // the first in queue.
4e526b93 670 if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) {
55369ae6
AR
671 startCertDownloading(urlsOfMissingCerts.front());
672 urlsOfMissingCerts.pop();
673 return;
674 }
675
800967af 676 resumeNegotiation();
55369ae6
AR
677}
678
800967af
CT
679void
680Security::PeerConnector::handleMissingCertificates(const Security::IoResult &ioResult)
55369ae6 681{
2b6b1bcb 682 Must(Comm::IsConnOpen(serverConnection()));
800967af
CT
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
800967af
CT
694 suspendNegotiation(ioResult);
695
43d6b5c8
AR
696 if (!computeMissingCertificateUrls(sconn))
697 return resumeNegotiation();
698
800967af
CT
699 assert(!urlsOfMissingCerts.empty());
700 startCertDownloading(urlsOfMissingCerts.front());
701 urlsOfMissingCerts.pop();
702}
cda7024f 703
800967af
CT
704/// finds URLs of (some) missing intermediate certificates or returns false
705bool
706Security::PeerConnector::computeMissingCertificateUrls(const Connection &sconn)
707{
708 const auto certs = SSL_get_peer_cert_chain(&sconn);
709 if (!certs) {
710 debugs(83, 3, "nothing to bootstrap the fetch with");
4e526b93 711 return false;
800967af
CT
712 }
713 debugs(83, 5, "server certificates: " << sk_X509_num(certs));
4e526b93 714
800967af
CT
715 const auto ctx = getTlsContext();
716 if (!Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, *certs, ctx))
717 return false; // missingChainCertificatesUrls() reports the exact reason
55369ae6 718
800967af
CT
719 debugs(83, 5, "URLs: " << urlsOfMissingCerts.size());
720 assert(!urlsOfMissingCerts.empty());
721 return true;
722}
723
724void
725Security::PeerConnector::suspendNegotiation(const Security::IoResult &ioResult)
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
735Security::PeerConnector::resumeNegotiation()
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);
55369ae6
AR
749 }
750
800967af 751 handleNegotiationResult(*lastError);
55369ae6 752}
800967af 753
212e5aee 754#endif //USE_OPENSSL
3945c91d 755