]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/PeerConnector.cc
SourceLayout: shuffle HelperChildConfig into libhelper.la
[thirdparty/squid.git] / src / ssl / PeerConnector.cc
CommitLineData
a23223bf 1/*
bbc27441 2 * Copyright (C) 1996-2014 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
bbc27441
AJ
9/* DEBUG: section 17 Request Forwarding */
10
a23223bf
CT
11#include "squid.h"
12#include "acl/FilledChecklist.h"
13#include "base/AsyncCbdataCalls.h"
14#include "CachePeer.h"
15#include "client_side.h"
16#include "comm/Loops.h"
17#include "errorpage.h"
18#include "fde.h"
19#include "globals.h"
24438ec5 20#include "helper/ResultCode.h"
a23223bf
CT
21#include "HttpRequest.h"
22#include "neighbors.h"
7f4e9b73 23#include "SquidConfig.h"
31855516 24#include "ssl/bio.h"
a23223bf
CT
25#include "ssl/cert_validate_message.h"
26#include "ssl/Config.h"
27#include "ssl/ErrorDetail.h"
28#include "ssl/helper.h"
29#include "ssl/PeerConnector.h"
30#include "ssl/ServerBump.h"
31#include "ssl/support.h"
a23223bf
CT
32
33CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeerConnector);
34
35Ssl::PeerConnector::PeerConnector(
36 HttpRequestPointer &aRequest,
37 const Comm::ConnectionPointer &aServerConn,
93ead3fd 38 const Comm::ConnectionPointer &aClientConn,
8aec3e1b
CT
39 AsyncCall::Pointer &aCallback,
40 const time_t timeout):
e2849af8
A
41 AsyncJob("Ssl::PeerConnector"),
42 request(aRequest),
43 serverConn(aServerConn),
20b79af2 44 clientConn(aClientConn),
8aec3e1b
CT
45 callback(aCallback),
46 negotiationTimeout(timeout),
47 startTime(squid_curtime)
a23223bf
CT
48{
49 // if this throws, the caller's cb dialer is not our CbDialer
50 Must(dynamic_cast<CbDialer*>(callback->getDialer()));
51}
52
53Ssl::PeerConnector::~PeerConnector()
54{
55 debugs(83, 5, "Peer connector " << this << " gone");
56}
57
58bool Ssl::PeerConnector::doneAll() const
59{
60 return (!callback || callback->canceled()) && AsyncJob::doneAll();
61}
62
63/// Preps connection and SSL state. Calls negotiate().
64void
65Ssl::PeerConnector::start()
66{
67 AsyncJob::start();
68
69 if (prepareSocket()) {
70 initializeSsl();
71 negotiateSsl();
72 }
73}
74
75void
76Ssl::PeerConnector::commCloseHandler(const CommCloseCbParams &params)
77{
78 debugs(83, 5, "FD " << params.fd << ", Ssl::PeerConnector=" << params.data);
79 connectionClosed("Ssl::PeerConnector::commCloseHandler");
80}
81
82void
83Ssl::PeerConnector::connectionClosed(const char *reason)
84{
85 mustStop(reason);
86 callback = NULL;
87}
88
89bool
90Ssl::PeerConnector::prepareSocket()
91{
92 const int fd = serverConnection()->fd;
93 if (!Comm::IsConnOpen(serverConn) || fd_table[serverConn->fd].closing()) {
94 connectionClosed("Ssl::PeerConnector::prepareSocket");
95 return false;
96 }
97
98 // watch for external connection closures
99 typedef CommCbMemFunT<Ssl::PeerConnector, CommCloseCbParams> Dialer;
100 closeHandler = JobCallback(9, 5, Dialer, this, Ssl::PeerConnector::commCloseHandler);
101 comm_add_close_handler(fd, closeHandler);
102 return true;
103}
104
105void
106Ssl::PeerConnector::initializeSsl()
107{
a23223bf
CT
108 SSL_CTX *sslContext = NULL;
109 const CachePeer *peer = serverConnection()->getPeer();
110 const int fd = serverConnection()->fd;
111
112 if (peer) {
113 assert(peer->use_ssl);
114 sslContext = peer->sslContext;
115 } else {
116 sslContext = ::Config.ssl_client.sslContext;
117 }
118
119 assert(sslContext);
120
31855516
CT
121 SSL *ssl = Ssl::CreateClient(sslContext, fd, "server https start");
122 if (!ssl) {
a23223bf
CT
123 ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw());
124 anErr->xerrno = errno;
125 debugs(83, DBG_IMPORTANT, "Error allocating SSL handle: " << ERR_error_string(ERR_get_error(), NULL));
126 bail(anErr);
127 return;
128 }
129
a23223bf
CT
130 if (peer) {
131 if (peer->ssldomain)
132 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
133
134#if NOT_YET
135
136 else if (peer->name)
137 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
138
139#endif
140
141 else
142 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
143
144 if (peer->sslSession)
145 SSL_set_session(ssl, peer->sslSession);
146
5d65362c 147 } else if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) {
20b79af2
CT
148 // client connection is required for Peek or Stare mode in the case we need to splice
149 // or terminate client and server connections
150 assert(clientConn != NULL);
31855516
CT
151 SSL *clientSsl = fd_table[request->clientConnectionManager->clientConnection->fd].ssl;
152 BIO *b = SSL_get_rbio(clientSsl);
153 Ssl::ClientBio *clnBio = static_cast<Ssl::ClientBio *>(b->ptr);
154 const Ssl::Bio::sslFeatures &features = clnBio->getFeatures();
155 if (features.sslVersion != -1) {
a95989ed 156 features.applyToSSL(ssl);
5d65362c 157 // Should we allow it for all protocols?
31855516
CT
158 if (features.sslVersion >= 3) {
159 b = SSL_get_rbio(ssl);
160 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
7f4e9b73 161 srvBio->setClientFeatures(features);
31855516 162 srvBio->recordInput(true);
5d65362c 163 srvBio->mode(request->clientConnectionManager->sslBumpMode);
31855516
CT
164 }
165 }
a23223bf
CT
166 } else {
167 // While we are peeking at the certificate, we may not know the server
168 // name that the client will request (after interception or CONNECT)
169 // unless it was the CONNECT request with a user-typed address.
170 const char *hostname = request->GetHost();
171 const bool hostnameIsIp = request->GetHostIsNumeric();
172 const bool isConnectRequest = request->clientConnectionManager.valid() &&
173 !request->clientConnectionManager->port->flags.isIntercepted();
174 if (!request->flags.sslPeek || isConnectRequest)
175 SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostname);
176
177 // Use SNI TLS extension only when we connect directly
178 // to the origin server and we know the server host name.
179 if (!hostnameIsIp)
180 Ssl::setClientSNI(ssl, hostname);
181 }
182
183 // If CertValidation Helper used do not lookup checklist for errors,
184 // but keep a list of errors to send it to CertValidator
185 if (!Ssl::TheConfig.ssl_crt_validator) {
186 // Create the ACL check list now, while we have access to more info.
187 // The list is used in ssl_verify_cb() and is freed in ssl_free().
188 if (acl_access *acl = ::Config.ssl_client.cert_error) {
189 ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
190 // check->fd(fd); XXX: need client FD here
191 SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check);
192 }
193 }
194
195 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
196 X509 *peeked_cert;
197 if (request->clientConnectionManager.valid() &&
198 request->clientConnectionManager->serverBump() &&
199 (peeked_cert = request->clientConnectionManager->serverBump()->serverCert.get())) {
200 CRYPTO_add(&(peeked_cert->references),1,CRYPTO_LOCK_X509);
201 SSL_set_ex_data(ssl, ssl_ex_index_ssl_peeked_cert, peeked_cert);
202 }
a23223bf
CT
203}
204
8aec3e1b
CT
205void
206Ssl::PeerConnector::setReadTimeout()
207{
208 int timeToRead;
209 if (negotiationTimeout) {
210 const int timeUsed = squid_curtime - startTime;
211 const int timeLeft = max(0, static_cast<int>(negotiationTimeout - timeUsed));
212 timeToRead = min(static_cast<int>(::Config.Timeout.read), timeLeft);
213 } else
214 timeToRead = ::Config.Timeout.read;
215 AsyncCall::Pointer nil;
216 commSetConnTimeout(serverConnection(), timeToRead, nil);
217}
218
a23223bf
CT
219void
220Ssl::PeerConnector::negotiateSsl()
221{
222 if (!Comm::IsConnOpen(serverConnection()) || fd_table[serverConnection()->fd].closing())
223 return;
224
225 const int fd = serverConnection()->fd;
226 SSL *ssl = fd_table[fd].ssl;
227 const int result = SSL_connect(ssl);
228 if (result <= 0) {
229 handleNegotiateError(result);
230 return; // we might be gone by now
231 }
232
233 if (request->clientConnectionManager.valid()) {
234 // remember the server certificate from the ErrorDetail object
235 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
236 serverBump->serverCert.reset(SSL_get_peer_certificate(ssl));
237
238 // remember validation errors, if any
239 if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
240 serverBump->sslErrors = cbdataReference(errs);
241 }
242 }
243
244 if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
245 if (serverConnection()->getPeer()->sslSession)
246 SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
247
248 serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
249 }
250
251 if (Ssl::TheConfig.ssl_crt_validator) {
252 Ssl::CertValidationRequest validationRequest;
253 // WARNING: Currently we do not use any locking for any of the
254 // members of the Ssl::CertValidationRequest class. In this code the
255 // Ssl::CertValidationRequest object used only to pass data to
256 // Ssl::CertValidationHelper::submit method.
257 validationRequest.ssl = ssl;
258 validationRequest.domainName = request->GetHost();
259 if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
260 // validationRequest disappears on return so no need to cbdataReference
261 validationRequest.errors = errs;
262 else
263 validationRequest.errors = NULL;
264 try {
265 debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
266 Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, sslCrtvdHandleReplyWrapper, this);
267 return;
268 } catch (const std::exception &e) {
269 debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
270 "request for " << validationRequest.domainName <<
271 " certificate: " << e.what() << "; will now block to " <<
272 "validate that certificate.");
273 // fall through to do blocking in-process generation.
274 ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw());
275 bail(anErr);
276 if (serverConnection()->getPeer()) {
277 peerConnectFailed(serverConnection()->getPeer());
278 }
279 serverConn->close();
280 return;
281 }
282 }
283
284 callBack();
285}
286
93ead3fd 287void switchToTunnel(HttpRequest *request, int *status_ptr, Comm::ConnectionPointer & clientConn, Comm::ConnectionPointer &srvConn);
7f4e9b73 288
31855516 289void
7f4e9b73
CT
290Ssl::PeerConnector::cbCheckForPeekAndSplice(allow_t answer, void *data)
291{
292 Ssl::PeerConnector *peerConnect = (Ssl::PeerConnector *) data;
293 peerConnect->checkForPeekAndSplice(true, (Ssl::BumpMode)answer.kind);
294}
295
296bool
297Ssl::PeerConnector::checkForPeekAndSplice(bool checkDone, Ssl::BumpMode peekMode)
31855516 298{
789dda8d 299 SSL *ssl = fd_table[serverConn->fd].ssl;
5d65362c
CT
300 // Mark Step3 of bumping
301 if (request->clientConnectionManager.valid()) {
302 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
303 serverBump->step = Ssl::bumpStep3;
789dda8d
CT
304 if (!serverBump->serverCert.get())
305 serverBump->serverCert.reset(SSL_get_peer_certificate(ssl));
5d65362c
CT
306 }
307 }
308
309 if (!checkDone) {
310 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
311 ::Config.accessList.ssl_bump,
312 request.getRaw(), NULL);
313 acl_checklist->nonBlockingCheck(Ssl::PeerConnector::cbCheckForPeekAndSplice, this);
314 return false;
315 }
789dda8d 316
31855516
CT
317 BIO *b = SSL_get_rbio(ssl);
318 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
8693472e 319 debugs(83,5, "Will check for peek and splice on FD " << serverConn->fd);
7f4e9b73 320
5d65362c
CT
321 // bump, peek, stare, server-first,client-first are all mean bump the connection
322 if (peekMode < Ssl::bumpSplice)
323 peekMode = Ssl::bumpBump;
324
5d65362c
CT
325 if (peekMode == Ssl::bumpSplice && !srvBio->canSplice())
326 peekMode = Ssl::bumpPeek;
327 else if (peekMode == Ssl::bumpBump && !srvBio->canBump())
328 peekMode = Ssl::bumpSplice;
5d65362c 329
1110989a 330 if (peekMode == Ssl::bumpTerminate) {
5d65362c
CT
331 comm_close(serverConn->fd);
332 comm_close(clientConn->fd);
333 } else if (peekMode != Ssl::bumpSplice) {
31855516
CT
334 //Allow write, proceed with the connection
335 srvBio->holdWrite(false);
336 srvBio->recordInput(false);
337 Comm::SetSelect(serverConn->fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
8693472e 338 debugs(83,5, "Retry the fwdNegotiateSSL on FD " << serverConn->fd);
7f4e9b73 339 return true;
93ead3fd
CT
340 } else {
341 static int status_code = 0;
8693472e 342 debugs(83,5, "Revert to tunnel FD " << clientConn->fd << " with FD " << serverConn->fd);
93ead3fd 343 switchToTunnel(request.getRaw(), &status_code, clientConn, serverConn);
7f4e9b73 344 return false;
31855516 345 }
5d65362c 346 return false;
31855516
CT
347}
348
a23223bf
CT
349void
350Ssl::PeerConnector::sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &validationResponse)
351{
352 Ssl::PeerConnector *connector = (Ssl::PeerConnector *)(data);
353 connector->sslCrtvdHandleReply(validationResponse);
354}
355
356void
357Ssl::PeerConnector::sslCrtvdHandleReply(Ssl::CertValidationResponse const &validationResponse)
358{
359 Ssl::CertErrors *errs = NULL;
360 Ssl::ErrorDetail *errDetails = NULL;
361 bool validatorFailed = false;
362 if (!Comm::IsConnOpen(serverConnection())) {
363 return;
364 }
365
366 debugs(83,5, request->GetHost() << " cert validation result: " << validationResponse.resultCode);
367
24438ec5 368 if (validationResponse.resultCode == ::Helper::ResultCode::Error)
a23223bf 369 errs = sslCrtvdCheckForErrors(validationResponse, errDetails);
24438ec5 370 else if (validationResponse.resultCode != ::Helper::ResultCode::Okay)
a23223bf
CT
371 validatorFailed = true;
372
373 if (!errDetails && !validatorFailed) {
374 callBack();
375 return;
376 }
377
378 ErrorState *anErr = NULL;
379 if (validatorFailed) {
380 anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw());
381 } else {
382
383 // Check the list error with
384 if (errDetails && request->clientConnectionManager.valid()) {
385 // remember the server certificate from the ErrorDetail object
386 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
387 // remember validation errors, if any
388 if (errs) {
389 if (serverBump->sslErrors)
390 cbdataReferenceDone(serverBump->sslErrors);
391 serverBump->sslErrors = cbdataReference(errs);
392 }
393 }
394 }
395
396 anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw());
397 anErr->detail = errDetails;
398 /*anErr->xerrno= Should preserved*/
399 }
400
401 bail(anErr);
402 if (serverConnection()->getPeer()) {
403 peerConnectFailed(serverConnection()->getPeer());
404 }
405 serverConn->close();
406 return;
407}
408
409/// Checks errors in the cert. validator response against sslproxy_cert_error.
410/// The first honored error, if any, is returned via errDetails parameter.
411/// The method returns all seen errors except SSL_ERROR_NONE as Ssl::CertErrors.
412Ssl::CertErrors *
413Ssl::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails)
414{
415 Ssl::CertErrors *errs = NULL;
416
417 ACLFilledChecklist *check = NULL;
418 if (acl_access *acl = ::Config.ssl_client.cert_error)
419 check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
420
421 SSL *ssl = fd_table[serverConnection()->fd].ssl;
422 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
423 for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
424 debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
425
426 assert(i->error_no != SSL_ERROR_NONE);
427
428 if (!errDetails) {
429 bool allowed = false;
430 if (check) {
431 check->sslErrors = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get()));
432 if (check->fastCheck() == ACCESS_ALLOWED)
433 allowed = true;
434 }
435 // else the Config.ssl_client.cert_error access list is not defined
436 // and the first error will cause the error page
437
438 if (allowed) {
439 debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer");
440 } else {
441 debugs(83, 5, "confirming SSL error " << i->error_no);
442 X509 *brokenCert = i->cert.get();
443 Ssl::X509_Pointer peerCert(SSL_get_peer_certificate(ssl));
444 const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str();
445 errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason);
446 }
447 if (check) {
448 delete check->sslErrors;
449 check->sslErrors = NULL;
450 }
451 }
452
453 if (!errs)
454 errs = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get()));
455 else
456 errs->push_back_unique(Ssl::CertError(i->error_no, i->cert.get()));
457 }
458 if (check)
459 delete check;
460
461 return errs;
462}
463
464/// A wrapper for Comm::SetSelect() notifications.
465void
466Ssl::PeerConnector::NegotiateSsl(int, void *data)
467{
468 PeerConnector *pc = static_cast<PeerConnector*>(data);
469 // Use job calls to add done() checks and other job logic/protections.
470 CallJobHere(83, 7, pc, Ssl::PeerConnector, negotiateSsl);
471}
472
473void
474Ssl::PeerConnector::handleNegotiateError(const int ret)
475{
476 const int fd = serverConnection()->fd;
477 unsigned long ssl_lib_error = SSL_ERROR_NONE;
478 SSL *ssl = fd_table[fd].ssl;
479 int ssl_error = SSL_get_error(ssl, ret);
31855516
CT
480 BIO *b = SSL_get_rbio(ssl);
481 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
a23223bf
CT
482
483#ifdef EPROTO
e2849af8 484 int sysErrNo = EPROTO;
a23223bf 485#else
e2849af8 486 int sysErrNo = EACCES;
a23223bf
CT
487#endif
488
e2849af8 489 switch (ssl_error) {
a23223bf 490
e2849af8 491 case SSL_ERROR_WANT_READ:
8aec3e1b 492 setReadTimeout();
e2849af8
A
493 Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0);
494 return;
a23223bf 495
e2849af8 496 case SSL_ERROR_WANT_WRITE:
20b79af2
CT
497 if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) {
498 debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd);
499 checkForPeekAndSplice(false, Ssl::bumpNone);
a23223bf 500 return;
20b79af2 501 }
e2849af8
A
502 Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
503 return;
a23223bf 504
e2849af8
A
505 case SSL_ERROR_SSL:
506 case SSL_ERROR_SYSCALL:
507 ssl_lib_error = ERR_get_error();
a23223bf 508
20b79af2
CT
509 // If we are in peek-and-splice mode and still we did not write to
510 // server yet, try to see if we should splice.
511 // In this case the connection can be saved.
512 // If the checklist decision is do not splice a new error will
513 // occure in the next SSL_connect call, and we will fail again.
7f4e9b73 514#if 1
20b79af2 515 if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) {
1110989a 516 debugs(81, 3, "Error (" << ERR_error_string(ssl_lib_error, NULL) << ") but, hold write on SSL connection on FD " << fd);
20b79af2
CT
517 checkForPeekAndSplice(false, Ssl::bumpNone);
518 return;
519 }
7f4e9b73
CT
520#endif
521
e2849af8
A
522 // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
523 if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0)
524 sysErrNo = errno;
a23223bf 525
e2849af8
A
526 debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd <<
527 ": " << ERR_error_string(ssl_lib_error, NULL) << " (" <<
528 ssl_error << "/" << ret << "/" << errno << ")");
a23223bf 529
e2849af8 530 break; // proceed to the general error handling code
a23223bf 531
e2849af8
A
532 default:
533 break; // no special error handling for all other errors
534 }
a23223bf
CT
535
536 ErrorState *const anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request.getRaw());
537 anErr->xerrno = sysErrNo;
538
539 Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail);
540 if (errFromFailure != NULL) {
541 // The errFromFailure is attached to the ssl object
542 // and will be released when ssl object destroyed.
543 // Copy errFromFailure to a new Ssl::ErrorDetail object
544 anErr->detail = new Ssl::ErrorDetail(*errFromFailure);
545 } else {
546 // server_cert can be NULL here
547 X509 *server_cert = SSL_get_peer_certificate(ssl);
548 anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert, NULL);
549 X509_free(server_cert);
550 }
551
552 if (ssl_lib_error != SSL_ERROR_NONE)
553 anErr->detail->setLibError(ssl_lib_error);
554
555 if (request->clientConnectionManager.valid()) {
556 // remember the server certificate from the ErrorDetail object
557 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
558 serverBump->serverCert.resetAndLock(anErr->detail->peerCert());
559
560 // remember validation errors, if any
561 if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors*>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
562 serverBump->sslErrors = cbdataReference(errs);
563 }
564
565 // For intercepted connections, set the host name to the server
566 // certificate CN. Otherwise, we just hope that CONNECT is using
567 // a user-entered address (a host name or a user-entered IP).
568 const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
569 if (request->flags.sslPeek && !isConnectRequest) {
570 if (X509 *srvX509 = anErr->detail->peerCert()) {
571 if (const char *name = Ssl::CommonHostName(srvX509)) {
572 request->SetHost(name);
573 debugs(83, 3, HERE << "reset request host: " << name);
574 }
575 }
576 }
577 }
578
579 bail(anErr);
580}
581
582void
583Ssl::PeerConnector::bail(ErrorState *error)
584{
585 Must(error); // or the recepient will not know there was a problem
586
587 // XXX: forward.cc calls peerConnectSucceeded() after an OK TCP connect but
588 // we call peerConnectFailed() if SSL failed afterwards. Is that OK?
589 // It is not clear whether we should call peerConnectSucceeded/Failed()
590 // based on TCP results, SSL results, or both. And the code is probably not
591 // consistent in this aspect across tunnelling and forwarding modules.
592 if (CachePeer *p = serverConnection()->getPeer())
593 peerConnectFailed(p);
594
595 Must(callback != NULL);
596 CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
597 Must(dialer);
598 dialer->answer().error = error;
599
600 callBack();
601 // Our job is done. The callabck recepient will probably close the failed
602 // peer connection and try another peer or go direct (if possible). We
603 // can close the connection ourselves (our error notification would reach
604 // the recepient before the fd-closure notification), but we would rather
605 // minimize the number of fd-closure notifications and let the recepient
606 // manage the TCP state of the connection.
607}
608
609void
610Ssl::PeerConnector::callBack()
611{
612 AsyncCall::Pointer cb = callback;
613 // Do this now so that if we throw below, swanSong() assert that we _tried_
614 // to call back holds.
615 callback = NULL; // this should make done() true
616
617 // remove close handler
618 comm_remove_close_handler(serverConnection()->fd, closeHandler);
619
620 CbDialer *dialer = dynamic_cast<CbDialer*>(cb->getDialer());
621 Must(dialer);
622 dialer->answer().conn = serverConnection();
623 ScheduleCallHere(cb);
624}
625
a23223bf
CT
626void
627Ssl::PeerConnector::swanSong()
628{
629 // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any
630 AsyncJob::swanSong();
631 assert(!callback); // paranoid: we have not left the caller waiting
632}
633
634const char *
635Ssl::PeerConnector::status() const
636{
637 static MemBuf buf;
638 buf.reset();
639
640 // TODO: redesign AsyncJob::status() API to avoid this
641 // id and stop reason reporting duplication.
642 buf.append(" [", 2);
643 if (stopReason != NULL) {
644 buf.Printf("Stopped, reason:");
645 buf.Printf("%s",stopReason);
646 }
647 if (serverConn != NULL)
648 buf.Printf(" FD %d", serverConn->fd);
649 buf.Printf(" %s%u]", id.Prefix, id.value);
650 buf.terminate();
651
652 return buf.content();
653}
654
655/* PeerConnectorAnswer */
656
657Ssl::PeerConnectorAnswer::~PeerConnectorAnswer()
658{
659 delete error.get();
660}
661
662std::ostream &
84321458 663Ssl::operator <<(std::ostream &os, const Ssl::PeerConnectorAnswer &answer)
a23223bf
CT
664{
665 return os << answer.conn << ", " << answer.error;
666}