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