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