]>
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" | |
55369ae6 | 17 | #include "Downloader.h" |
a23223bf CT |
18 | #include "errorpage.h" |
19 | #include "fde.h" | |
20 | #include "globals.h" | |
24438ec5 | 21 | #include "helper/ResultCode.h" |
a23223bf CT |
22 | #include "HttpRequest.h" |
23 | #include "neighbors.h" | |
7f4e9b73 | 24 | #include "SquidConfig.h" |
31855516 | 25 | #include "ssl/bio.h" |
a23223bf CT |
26 | #include "ssl/cert_validate_message.h" |
27 | #include "ssl/Config.h" | |
28 | #include "ssl/ErrorDetail.h" | |
29 | #include "ssl/helper.h" | |
30 | #include "ssl/PeerConnector.h" | |
31 | #include "ssl/ServerBump.h" | |
32 | #include "ssl/support.h" | |
a23223bf CT |
33 | |
34 | CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeerConnector); | |
1b091aec CT |
35 | CBDATA_NAMESPACED_CLASS_INIT(Ssl, BlindPeerConnector); |
36 | CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector); | |
a23223bf | 37 | |
37611b26 | 38 | Ssl::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const time_t timeout) : |
f53969cc | 39 | AsyncJob("Ssl::PeerConnector"), |
f53969cc | 40 | serverConn(aServerConn), |
79fe57c2 | 41 | certErrors(NULL), |
f53969cc SM |
42 | callback(aCallback), |
43 | negotiationTimeout(timeout), | |
44 | startTime(squid_curtime), | |
4e526b93 CT |
45 | useCertValidator_(false), |
46 | certsDownloads(0) | |
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 | ||
52 | Ssl::PeerConnector::~PeerConnector() | |
53 | { | |
1b091aec | 54 | cbdataReferenceDone(certErrors); |
a23223bf CT |
55 | debugs(83, 5, "Peer connector " << this << " gone"); |
56 | } | |
57 | ||
58 | bool Ssl::PeerConnector::doneAll() const | |
59 | { | |
60 | return (!callback || callback->canceled()) && AsyncJob::doneAll(); | |
61 | } | |
62 | ||
63 | /// Preps connection and SSL state. Calls negotiate(). | |
64 | void | |
65 | Ssl::PeerConnector::start() | |
66 | { | |
67 | AsyncJob::start(); | |
68 | ||
1b091aec | 69 | if (prepareSocket() && (initializeSsl() != NULL)) |
a23223bf | 70 | negotiateSsl(); |
a23223bf CT |
71 | } |
72 | ||
73 | void | |
74 | Ssl::PeerConnector::commCloseHandler(const CommCloseCbParams ¶ms) | |
75 | { | |
76 | debugs(83, 5, "FD " << params.fd << ", Ssl::PeerConnector=" << params.data); | |
77 | connectionClosed("Ssl::PeerConnector::commCloseHandler"); | |
78 | } | |
79 | ||
80 | void | |
81 | Ssl::PeerConnector::connectionClosed(const char *reason) | |
82 | { | |
83 | mustStop(reason); | |
84 | callback = NULL; | |
85 | } | |
86 | ||
87 | bool | |
88 | Ssl::PeerConnector::prepareSocket() | |
89 | { | |
90 | const int fd = serverConnection()->fd; | |
91 | if (!Comm::IsConnOpen(serverConn) || fd_table[serverConn->fd].closing()) { | |
92 | connectionClosed("Ssl::PeerConnector::prepareSocket"); | |
93 | return false; | |
94 | } | |
95 | ||
96 | // watch for external connection closures | |
97 | typedef CommCbMemFunT<Ssl::PeerConnector, CommCloseCbParams> Dialer; | |
98 | closeHandler = JobCallback(9, 5, Dialer, this, Ssl::PeerConnector::commCloseHandler); | |
99 | comm_add_close_handler(fd, closeHandler); | |
100 | return true; | |
101 | } | |
102 | ||
1b091aec | 103 | SSL * |
a23223bf CT |
104 | Ssl::PeerConnector::initializeSsl() |
105 | { | |
96993ee0 | 106 | Security::ContextPtr sslContext(getSslContext()); |
a23223bf CT |
107 | assert(sslContext); |
108 | ||
1b091aec CT |
109 | const int fd = serverConnection()->fd; |
110 | ||
31855516 CT |
111 | SSL *ssl = Ssl::CreateClient(sslContext, fd, "server https start"); |
112 | if (!ssl) { | |
a23223bf CT |
113 | ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw()); |
114 | anErr->xerrno = errno; | |
115 | debugs(83, DBG_IMPORTANT, "Error allocating SSL handle: " << ERR_error_string(ERR_get_error(), NULL)); | |
76b64c80 | 116 | |
1b091aec CT |
117 | noteNegotiationDone(anErr); |
118 | bail(anErr); | |
119 | return NULL; | |
a23223bf CT |
120 | } |
121 | ||
122 | // If CertValidation Helper used do not lookup checklist for errors, | |
123 | // but keep a list of errors to send it to CertValidator | |
124 | if (!Ssl::TheConfig.ssl_crt_validator) { | |
125 | // Create the ACL check list now, while we have access to more info. | |
126 | // The list is used in ssl_verify_cb() and is freed in ssl_free(). | |
127 | if (acl_access *acl = ::Config.ssl_client.cert_error) { | |
128 | ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str); | |
129 | // check->fd(fd); XXX: need client FD here | |
130 | SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check); | |
131 | } | |
132 | } | |
1b091aec | 133 | return ssl; |
a23223bf CT |
134 | } |
135 | ||
8aec3e1b CT |
136 | void |
137 | Ssl::PeerConnector::setReadTimeout() | |
138 | { | |
139 | int timeToRead; | |
140 | if (negotiationTimeout) { | |
141 | const int timeUsed = squid_curtime - startTime; | |
142 | const int timeLeft = max(0, static_cast<int>(negotiationTimeout - timeUsed)); | |
143 | timeToRead = min(static_cast<int>(::Config.Timeout.read), timeLeft); | |
144 | } else | |
145 | timeToRead = ::Config.Timeout.read; | |
146 | AsyncCall::Pointer nil; | |
147 | commSetConnTimeout(serverConnection(), timeToRead, nil); | |
148 | } | |
149 | ||
a23223bf CT |
150 | void |
151 | Ssl::PeerConnector::negotiateSsl() | |
152 | { | |
153 | if (!Comm::IsConnOpen(serverConnection()) || fd_table[serverConnection()->fd].closing()) | |
154 | return; | |
155 | ||
156 | const int fd = serverConnection()->fd; | |
157 | SSL *ssl = fd_table[fd].ssl; | |
158 | const int result = SSL_connect(ssl); | |
159 | if (result <= 0) { | |
160 | handleNegotiateError(result); | |
161 | return; // we might be gone by now | |
162 | } | |
163 | ||
c91d4d4e CT |
164 | if (!sslFinalized()) |
165 | return; | |
166 | ||
167 | callBack(); | |
168 | } | |
169 | ||
170 | bool | |
171 | Ssl::PeerConnector::sslFinalized() | |
172 | { | |
1b091aec CT |
173 | if (Ssl::TheConfig.ssl_crt_validator && useCertValidator_) { |
174 | const int fd = serverConnection()->fd; | |
175 | SSL *ssl = fd_table[fd].ssl; | |
a23223bf | 176 | |
a23223bf CT |
177 | Ssl::CertValidationRequest validationRequest; |
178 | // WARNING: Currently we do not use any locking for any of the | |
179 | // members of the Ssl::CertValidationRequest class. In this code the | |
180 | // Ssl::CertValidationRequest object used only to pass data to | |
181 | // Ssl::CertValidationHelper::submit method. | |
182 | validationRequest.ssl = ssl; | |
5c51bffb | 183 | validationRequest.domainName = request->url.host(); |
a23223bf CT |
184 | if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors))) |
185 | // validationRequest disappears on return so no need to cbdataReference | |
186 | validationRequest.errors = errs; | |
187 | else | |
188 | validationRequest.errors = NULL; | |
189 | try { | |
190 | debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd."); | |
0e208dad CT |
191 | AsyncCall::Pointer call = asyncCall(83,5, "Ssl::PeerConnector::sslCrtvdHandleReply", Ssl::CertValidationHelper::CbDialer(this, &Ssl::PeerConnector::sslCrtvdHandleReply, nullptr)); |
192 | Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, call); | |
c91d4d4e | 193 | return false; |
a23223bf CT |
194 | } catch (const std::exception &e) { |
195 | debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " << | |
196 | "request for " << validationRequest.domainName << | |
197 | " certificate: " << e.what() << "; will now block to " << | |
198 | "validate that certificate."); | |
199 | // fall through to do blocking in-process generation. | |
200 | ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw()); | |
1b091aec CT |
201 | |
202 | noteNegotiationDone(anErr); | |
a23223bf | 203 | bail(anErr); |
a23223bf | 204 | serverConn->close(); |
c91d4d4e | 205 | return true; |
a23223bf CT |
206 | } |
207 | } | |
1b091aec CT |
208 | |
209 | noteNegotiationDone(NULL); | |
c91d4d4e | 210 | return true; |
a23223bf CT |
211 | } |
212 | ||
c91d4d4e | 213 | void switchToTunnel(HttpRequest *request, Comm::ConnectionPointer & clientConn, Comm::ConnectionPointer &srvConn); |
7f4e9b73 | 214 | |
31855516 | 215 | void |
1b091aec | 216 | Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone(allow_t answer, void *data) |
7f4e9b73 | 217 | { |
1b091aec | 218 | Ssl::PeekingPeerConnector *peerConnect = (Ssl::PeekingPeerConnector *) data; |
566f8310 AR |
219 | // Use job calls to add done() checks and other job logic/protections. |
220 | CallJobHere1(83, 7, CbcPointer<PeekingPeerConnector>(peerConnect), Ssl::PeekingPeerConnector, checkForPeekAndSpliceDone, answer); | |
221 | } | |
222 | ||
223 | void | |
224 | Ssl::PeekingPeerConnector::checkForPeekAndSpliceDone(allow_t answer) | |
225 | { | |
226 | const Ssl::BumpMode finalAction = (answer.code == ACCESS_ALLOWED) ? | |
2d5fa7fa SM |
227 | static_cast<Ssl::BumpMode>(answer.kind): |
228 | checkForPeekAndSpliceGuess(); | |
566f8310 | 229 | checkForPeekAndSpliceMatched(finalAction); |
7f4e9b73 CT |
230 | } |
231 | ||
a9c2dd2f | 232 | void |
1b091aec | 233 | Ssl::PeekingPeerConnector::checkForPeekAndSplice() |
31855516 | 234 | { |
5d65362c CT |
235 | // Mark Step3 of bumping |
236 | if (request->clientConnectionManager.valid()) { | |
237 | if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) { | |
238 | serverBump->step = Ssl::bumpStep3; | |
239 | } | |
240 | } | |
241 | ||
69f69080 CT |
242 | handleServerCertificate(); |
243 | ||
a9c2dd2f CT |
244 | ACLFilledChecklist *acl_checklist = new ACLFilledChecklist( |
245 | ::Config.accessList.ssl_bump, | |
246 | request.getRaw(), NULL); | |
640fe8fb CT |
247 | acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpNone)); |
248 | acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpPeek)); | |
249 | acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpStare)); | |
250 | acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpClientFirst)); | |
251 | acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpServerFirst)); | |
252 | SSL *ssl = fd_table[serverConn->fd].ssl; | |
253 | BIO *b = SSL_get_rbio(ssl); | |
254 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
255 | if (!srvBio->canSplice()) | |
256 | acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpSplice)); | |
257 | if (!srvBio->canBump()) | |
258 | acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpBump)); | |
1b091aec | 259 | acl_checklist->nonBlockingCheck(Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone, this); |
a9c2dd2f | 260 | } |
789dda8d | 261 | |
a9c2dd2f | 262 | void |
566f8310 | 263 | Ssl::PeekingPeerConnector::checkForPeekAndSpliceMatched(const Ssl::BumpMode action) |
a9c2dd2f CT |
264 | { |
265 | SSL *ssl = fd_table[serverConn->fd].ssl; | |
31855516 CT |
266 | BIO *b = SSL_get_rbio(ssl); |
267 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
8693472e | 268 | debugs(83,5, "Will check for peek and splice on FD " << serverConn->fd); |
7f4e9b73 | 269 | |
a9c2dd2f | 270 | Ssl::BumpMode finalAction = action; |
640fe8fb | 271 | Must(finalAction == Ssl::bumpSplice || finalAction == Ssl::bumpBump || finalAction == Ssl::bumpTerminate); |
a9c2dd2f | 272 | // Record final decision |
68bd0182 | 273 | if (request->clientConnectionManager.valid()) { |
a9c2dd2f | 274 | request->clientConnectionManager->sslBumpMode = finalAction; |
68bd0182 CT |
275 | request->clientConnectionManager->serverBump()->act.step3 = finalAction; |
276 | } | |
a9c2dd2f CT |
277 | |
278 | if (finalAction == Ssl::bumpTerminate) { | |
b54a7c5a CT |
279 | serverConn->close(); |
280 | clientConn->close(); | |
a9c2dd2f | 281 | } else if (finalAction != Ssl::bumpSplice) { |
31855516 CT |
282 | //Allow write, proceed with the connection |
283 | srvBio->holdWrite(false); | |
7b4984f7 | 284 | srvBio->recordInput(false); |
8693472e | 285 | debugs(83,5, "Retry the fwdNegotiateSSL on FD " << serverConn->fd); |
1b091aec | 286 | Ssl::PeerConnector::noteWantWrite(); |
93ead3fd | 287 | } else { |
c91d4d4e | 288 | splice = true; |
f53969cc | 289 | // Ssl Negotiation stops here. Last SSL checks for valid certificates |
c91d4d4e | 290 | // and if done, switch to tunnel mode |
1b091aec CT |
291 | if (sslFinalized()) { |
292 | debugs(83,5, "Abort NegotiateSSL on FD " << serverConn->fd << " and splice the connection"); | |
293 | } | |
31855516 CT |
294 | } |
295 | } | |
296 | ||
566f8310 AR |
297 | Ssl::BumpMode |
298 | Ssl::PeekingPeerConnector::checkForPeekAndSpliceGuess() const | |
299 | { | |
300 | if (const ConnStateData *csd = request->clientConnectionManager.valid()) { | |
301 | const Ssl::BumpMode currentMode = csd->sslBumpMode; | |
302 | if (currentMode == Ssl::bumpStare) { | |
303 | debugs(83,5, "default to bumping after staring"); | |
304 | return Ssl::bumpBump; | |
305 | } | |
306 | debugs(83,5, "default to splicing after " << currentMode); | |
307 | } else { | |
308 | debugs(83,3, "default to splicing due to missing info"); | |
309 | } | |
310 | ||
311 | return Ssl::bumpSplice; | |
312 | } | |
313 | ||
a23223bf | 314 | void |
0e208dad | 315 | Ssl::PeerConnector::sslCrtvdHandleReply(Ssl::CertValidationResponse::Pointer validationResponse) |
a23223bf | 316 | { |
0e208dad | 317 | Must(validationResponse != NULL); |
a23223bf | 318 | |
a23223bf CT |
319 | Ssl::CertErrors *errs = NULL; |
320 | Ssl::ErrorDetail *errDetails = NULL; | |
321 | bool validatorFailed = false; | |
322 | if (!Comm::IsConnOpen(serverConnection())) { | |
323 | return; | |
324 | } | |
325 | ||
0e208dad | 326 | debugs(83,5, request->url.host() << " cert validation result: " << validationResponse->resultCode); |
a23223bf | 327 | |
0e208dad CT |
328 | if (validationResponse->resultCode == ::Helper::Error) |
329 | errs = sslCrtvdCheckForErrors(*validationResponse, errDetails); | |
330 | else if (validationResponse->resultCode != ::Helper::Okay) | |
a23223bf CT |
331 | validatorFailed = true; |
332 | ||
333 | if (!errDetails && !validatorFailed) { | |
1b091aec CT |
334 | noteNegotiationDone(NULL); |
335 | callBack(); | |
a23223bf CT |
336 | return; |
337 | } | |
338 | ||
1b091aec CT |
339 | if (errs) { |
340 | if (certErrors) | |
341 | cbdataReferenceDone(certErrors); | |
342 | certErrors = cbdataReference(errs); | |
343 | } | |
344 | ||
a23223bf CT |
345 | ErrorState *anErr = NULL; |
346 | if (validatorFailed) { | |
347 | anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw()); | |
348 | } else { | |
a23223bf CT |
349 | anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw()); |
350 | anErr->detail = errDetails; | |
351 | /*anErr->xerrno= Should preserved*/ | |
352 | } | |
353 | ||
1b091aec | 354 | noteNegotiationDone(anErr); |
a23223bf | 355 | bail(anErr); |
a23223bf CT |
356 | serverConn->close(); |
357 | return; | |
358 | } | |
359 | ||
360 | /// Checks errors in the cert. validator response against sslproxy_cert_error. | |
361 | /// The first honored error, if any, is returned via errDetails parameter. | |
362 | /// The method returns all seen errors except SSL_ERROR_NONE as Ssl::CertErrors. | |
363 | Ssl::CertErrors * | |
364 | Ssl::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails) | |
365 | { | |
366 | Ssl::CertErrors *errs = NULL; | |
367 | ||
368 | ACLFilledChecklist *check = NULL; | |
369 | if (acl_access *acl = ::Config.ssl_client.cert_error) | |
370 | check = new ACLFilledChecklist(acl, request.getRaw(), dash_str); | |
371 | ||
372 | SSL *ssl = fd_table[serverConnection()->fd].ssl; | |
373 | typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI; | |
374 | for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) { | |
375 | debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason); | |
376 | ||
377 | assert(i->error_no != SSL_ERROR_NONE); | |
378 | ||
379 | if (!errDetails) { | |
380 | bool allowed = false; | |
381 | if (check) { | |
b4e6a8d4 | 382 | check->sslErrors = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get(), i->error_depth)); |
a23223bf CT |
383 | if (check->fastCheck() == ACCESS_ALLOWED) |
384 | allowed = true; | |
385 | } | |
386 | // else the Config.ssl_client.cert_error access list is not defined | |
387 | // and the first error will cause the error page | |
388 | ||
389 | if (allowed) { | |
390 | debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer"); | |
391 | } else { | |
392 | debugs(83, 5, "confirming SSL error " << i->error_no); | |
393 | X509 *brokenCert = i->cert.get(); | |
f97700a0 | 394 | Security::CertPointer peerCert(SSL_get_peer_certificate(ssl)); |
a23223bf CT |
395 | const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str(); |
396 | errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason); | |
397 | } | |
398 | if (check) { | |
399 | delete check->sslErrors; | |
400 | check->sslErrors = NULL; | |
401 | } | |
402 | } | |
403 | ||
404 | if (!errs) | |
b4e6a8d4 | 405 | errs = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get(), i->error_depth)); |
a23223bf | 406 | else |
b4e6a8d4 | 407 | errs->push_back_unique(Ssl::CertError(i->error_no, i->cert.get(), i->error_depth)); |
a23223bf CT |
408 | } |
409 | if (check) | |
410 | delete check; | |
411 | ||
412 | return errs; | |
413 | } | |
414 | ||
415 | /// A wrapper for Comm::SetSelect() notifications. | |
416 | void | |
417 | Ssl::PeerConnector::NegotiateSsl(int, void *data) | |
418 | { | |
419 | PeerConnector *pc = static_cast<PeerConnector*>(data); | |
420 | // Use job calls to add done() checks and other job logic/protections. | |
421 | CallJobHere(83, 7, pc, Ssl::PeerConnector, negotiateSsl); | |
422 | } | |
423 | ||
424 | void | |
425 | Ssl::PeerConnector::handleNegotiateError(const int ret) | |
426 | { | |
427 | const int fd = serverConnection()->fd; | |
428 | unsigned long ssl_lib_error = SSL_ERROR_NONE; | |
429 | SSL *ssl = fd_table[fd].ssl; | |
f5518dca | 430 | const int ssl_error = SSL_get_error(ssl, ret); |
a23223bf | 431 | |
e2849af8 | 432 | switch (ssl_error) { |
e2849af8 | 433 | case SSL_ERROR_WANT_READ: |
1b091aec | 434 | noteWantRead(); |
e2849af8 | 435 | return; |
a23223bf | 436 | |
e2849af8 | 437 | case SSL_ERROR_WANT_WRITE: |
1b091aec | 438 | noteWantWrite(); |
e2849af8 | 439 | return; |
a23223bf | 440 | |
e2849af8 A |
441 | case SSL_ERROR_SSL: |
442 | case SSL_ERROR_SYSCALL: | |
443 | ssl_lib_error = ERR_get_error(); | |
1b091aec CT |
444 | // proceed to the general error handling code |
445 | break; | |
446 | default: | |
447 | // no special error handling for all other errors | |
448 | break; | |
449 | } | |
450 | noteSslNegotiationError(ret, ssl_error, ssl_lib_error); | |
451 | } | |
a23223bf | 452 | |
1b091aec CT |
453 | void |
454 | Ssl::PeerConnector::noteWantRead() | |
455 | { | |
1b091aec | 456 | const int fd = serverConnection()->fd; |
55369ae6 AR |
457 | SSL *ssl = fd_table[fd].ssl; |
458 | BIO *b = SSL_get_rbio(ssl); | |
459 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
460 | if (srvBio->holdRead()) { | |
461 | if (srvBio->gotHello()) { | |
462 | if (checkForMissingCertificates()) | |
463 | return; // Wait to download certificates before proceed. | |
464 | ||
465 | srvBio->holdRead(false); | |
466 | // Schedule a negotiateSSl to allow openSSL parse received data | |
467 | Ssl::PeerConnector::NegotiateSsl(fd, this); | |
468 | return; | |
469 | } else if (srvBio->gotHelloFailed()) { | |
470 | srvBio->holdRead(false); | |
471 | debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd); | |
472 | // Schedule a negotiateSSl to allow openSSL parse received data | |
473 | Ssl::PeerConnector::NegotiateSsl(fd, this); | |
474 | return; | |
475 | } | |
476 | } | |
477 | ||
478 | setReadTimeout(); | |
1b091aec CT |
479 | Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0); |
480 | } | |
7f4e9b73 | 481 | |
1b091aec CT |
482 | void |
483 | Ssl::PeerConnector::noteWantWrite() | |
484 | { | |
485 | const int fd = serverConnection()->fd; | |
486 | Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0); | |
487 | return; | |
488 | } | |
a23223bf | 489 | |
1b091aec CT |
490 | void |
491 | Ssl::PeerConnector::noteSslNegotiationError(const int ret, const int ssl_error, const int ssl_lib_error) | |
492 | { | |
493 | #ifdef EPROTO | |
494 | int sysErrNo = EPROTO; | |
495 | #else | |
496 | int sysErrNo = EACCES; | |
497 | #endif | |
a23223bf | 498 | |
1b091aec CT |
499 | // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1 |
500 | if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0) | |
501 | sysErrNo = errno; | |
a23223bf | 502 | |
1b091aec CT |
503 | const int fd = serverConnection()->fd; |
504 | debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd << | |
505 | ": " << ERR_error_string(ssl_lib_error, NULL) << " (" << | |
506 | ssl_error << "/" << ret << "/" << errno << ")"); | |
a23223bf | 507 | |
1b091aec CT |
508 | ErrorState *anErr = NULL; |
509 | if (request != NULL) | |
510 | anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request.getRaw()); | |
511 | else | |
512 | anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, NULL); | |
a23223bf CT |
513 | anErr->xerrno = sysErrNo; |
514 | ||
1b091aec | 515 | SSL *ssl = fd_table[fd].ssl; |
a23223bf CT |
516 | Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail); |
517 | if (errFromFailure != NULL) { | |
518 | // The errFromFailure is attached to the ssl object | |
519 | // and will be released when ssl object destroyed. | |
520 | // Copy errFromFailure to a new Ssl::ErrorDetail object | |
521 | anErr->detail = new Ssl::ErrorDetail(*errFromFailure); | |
522 | } else { | |
523 | // server_cert can be NULL here | |
524 | X509 *server_cert = SSL_get_peer_certificate(ssl); | |
525 | anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert, NULL); | |
526 | X509_free(server_cert); | |
527 | } | |
528 | ||
529 | if (ssl_lib_error != SSL_ERROR_NONE) | |
530 | anErr->detail->setLibError(ssl_lib_error); | |
531 | ||
1b091aec CT |
532 | assert(certErrors == NULL); |
533 | // remember validation errors, if any | |
534 | if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors*>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors))) | |
535 | certErrors = cbdataReference(errs); | |
a23223bf | 536 | |
1b091aec | 537 | noteNegotiationDone(anErr); |
a23223bf CT |
538 | bail(anErr); |
539 | } | |
540 | ||
541 | void | |
542 | Ssl::PeerConnector::bail(ErrorState *error) | |
543 | { | |
544 | Must(error); // or the recepient will not know there was a problem | |
a23223bf CT |
545 | Must(callback != NULL); |
546 | CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer()); | |
547 | Must(dialer); | |
548 | dialer->answer().error = error; | |
549 | ||
550 | callBack(); | |
551 | // Our job is done. The callabck recepient will probably close the failed | |
552 | // peer connection and try another peer or go direct (if possible). We | |
553 | // can close the connection ourselves (our error notification would reach | |
554 | // the recepient before the fd-closure notification), but we would rather | |
555 | // minimize the number of fd-closure notifications and let the recepient | |
556 | // manage the TCP state of the connection. | |
557 | } | |
558 | ||
559 | void | |
560 | Ssl::PeerConnector::callBack() | |
561 | { | |
562 | AsyncCall::Pointer cb = callback; | |
563 | // Do this now so that if we throw below, swanSong() assert that we _tried_ | |
564 | // to call back holds. | |
565 | callback = NULL; // this should make done() true | |
566 | ||
567 | // remove close handler | |
568 | comm_remove_close_handler(serverConnection()->fd, closeHandler); | |
569 | ||
570 | CbDialer *dialer = dynamic_cast<CbDialer*>(cb->getDialer()); | |
571 | Must(dialer); | |
572 | dialer->answer().conn = serverConnection(); | |
573 | ScheduleCallHere(cb); | |
574 | } | |
575 | ||
a23223bf CT |
576 | void |
577 | Ssl::PeerConnector::swanSong() | |
578 | { | |
579 | // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any | |
580 | AsyncJob::swanSong(); | |
566f8310 AR |
581 | if (callback != NULL) { // paranoid: we have left the caller waiting |
582 | debugs(83, DBG_IMPORTANT, "BUG: Unexpected state while connecting to a cache_peer or origin server"); | |
583 | ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw()); | |
584 | bail(anErr); | |
585 | assert(!callback); | |
586 | return; | |
587 | } | |
a23223bf CT |
588 | } |
589 | ||
590 | const char * | |
591 | Ssl::PeerConnector::status() const | |
592 | { | |
593 | static MemBuf buf; | |
594 | buf.reset(); | |
595 | ||
596 | // TODO: redesign AsyncJob::status() API to avoid this | |
597 | // id and stop reason reporting duplication. | |
598 | buf.append(" [", 2); | |
599 | if (stopReason != NULL) { | |
07e6d76e AJ |
600 | buf.append("Stopped, reason:", 16); |
601 | buf.appendf("%s",stopReason); | |
a23223bf CT |
602 | } |
603 | if (serverConn != NULL) | |
07e6d76e AJ |
604 | buf.appendf(" FD %d", serverConn->fd); |
605 | buf.appendf(" %s%u]", id.Prefix, id.value); | |
a23223bf CT |
606 | buf.terminate(); |
607 | ||
608 | return buf.content(); | |
609 | } | |
610 | ||
6cae08c9 | 611 | /// CallDialer to allow use Downloader objects within PeerConnector class. |
55369ae6 AR |
612 | class PeerConnectorCertDownloaderDialer: public CallDialer, public Downloader::CbDialer |
613 | { | |
614 | public: | |
615 | typedef void (Ssl::PeerConnector::*Method)(SBuf &object, int status); | |
616 | ||
617 | PeerConnectorCertDownloaderDialer(Method method, Ssl::PeerConnector *pc): | |
618 | method_(method), | |
619 | peerConnector_(pc) {} | |
620 | ||
621 | /* CallDialer API */ | |
622 | virtual bool canDial(AsyncCall &call) { return peerConnector_.valid(); } | |
623 | void dial(AsyncCall &call) { ((&(*peerConnector_))->*method_)(object, status); } | |
624 | virtual void print(std::ostream &os) const { | |
625 | os << '(' << peerConnector_.get() << ", Http Status:" << status << ')'; | |
626 | } | |
627 | ||
6cae08c9 CT |
628 | Method method_; ///< The Ssl::PeerConnector method to dial |
629 | CbcPointer<Ssl::PeerConnector> peerConnector_; ///< The Ssl::PeerConnector object | |
55369ae6 AR |
630 | }; |
631 | ||
632 | void | |
633 | Ssl::PeerConnector::startCertDownloading(SBuf &url) | |
634 | { | |
635 | AsyncCall::Pointer certCallback = asyncCall(81, 4, | |
636 | "Ssl::PeerConnector::certDownloadingDone", | |
637 | PeerConnectorCertDownloaderDialer(&Ssl::PeerConnector::certDownloadingDone, this)); | |
638 | ||
4e526b93 | 639 | const Downloader *csd = dynamic_cast<const Downloader*>(request->clientConnectionManager.valid()); |
55369ae6 | 640 | MasterXaction *xaction = new MasterXaction; |
4e526b93 | 641 | Downloader *dl = new Downloader(url, xaction, certCallback, csd ? csd->nestedLevel() + 1 : 1); |
55369ae6 AR |
642 | AsyncJob::Start(dl); |
643 | } | |
644 | ||
645 | void | |
646 | Ssl::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus) | |
647 | { | |
4e526b93 | 648 | certsDownloads++; |
7b4984f7 | 649 | debugs(81, 5, "Certificate downloading status: " << downloadStatus << " certificate size: " << obj.length()); |
55369ae6 AR |
650 | |
651 | // Get ServerBio from SSL object | |
652 | const int fd = serverConnection()->fd; | |
653 | SSL *ssl = fd_table[fd].ssl; | |
654 | BIO *b = SSL_get_rbio(ssl); | |
655 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
656 | ||
7b4984f7 CT |
657 | // Parse Certificate. Assume that it is in DER format. Probably we |
658 | // should handle PEM or other formats too | |
55369ae6 AR |
659 | const unsigned char *raw = (const unsigned char*)obj.rawContent(); |
660 | if (X509 *cert = d2i_X509(NULL, &raw, obj.length())) { | |
661 | char buffer[1024]; | |
662 | debugs(81, 5, "Retrieved certificate: " << X509_NAME_oneline(X509_get_subject_name(cert), buffer, 1024)); | |
663 | const Ssl::X509_STACK_Pointer &certsList = srvBio->serverCertificates(); | |
664 | if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert, certsList)) { | |
665 | urlsOfMissingCerts.push(SBuf(issuerUri)); | |
666 | } | |
667 | Ssl::SSL_add_untrusted_cert(ssl, cert); | |
668 | } | |
669 | ||
670 | // Check if has uri to donwload and add it to urlsOfMissingCerts | |
4e526b93 | 671 | if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) { |
55369ae6 AR |
672 | startCertDownloading(urlsOfMissingCerts.front()); |
673 | urlsOfMissingCerts.pop(); | |
674 | return; | |
675 | } | |
676 | ||
677 | srvBio->holdRead(false); | |
678 | Ssl::PeerConnector::NegotiateSsl(serverConnection()->fd, this); | |
679 | } | |
680 | ||
681 | bool | |
682 | Ssl::PeerConnector::checkForMissingCertificates () | |
683 | { | |
4e526b93 CT |
684 | // Check for nested SSL certificates downloads. For example when the |
685 | // certificate located in an SSL site which requires to download a | |
686 | // a missing certificate (... from an SSL site which requires to ...) | |
687 | const Downloader *csd = dynamic_cast<const Downloader*>(request->clientConnectionManager.valid()); | |
688 | if (csd && csd->nestedLevel() >= MaxNestedDownloads) | |
689 | return false; | |
690 | ||
55369ae6 AR |
691 | const int fd = serverConnection()->fd; |
692 | SSL *ssl = fd_table[fd].ssl; | |
693 | BIO *b = SSL_get_rbio(ssl); | |
694 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
695 | const Ssl::X509_STACK_Pointer &certs = srvBio->serverCertificates(); | |
696 | ||
ffbfd5be | 697 | if (certs.get() && sk_X509_num(certs.get())) { |
55369ae6 AR |
698 | debugs(83, 5, "SSL server sent " << sk_X509_num(certs.get()) << " certificates"); |
699 | Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs); | |
700 | if (urlsOfMissingCerts.size()) { | |
701 | startCertDownloading(urlsOfMissingCerts.front()); | |
702 | urlsOfMissingCerts.pop(); | |
703 | return true; | |
704 | } | |
705 | } | |
706 | ||
707 | return false; | |
708 | } | |
709 | ||
96993ee0 | 710 | Security::ContextPtr |
1b091aec CT |
711 | Ssl::BlindPeerConnector::getSslContext() |
712 | { | |
713 | if (const CachePeer *peer = serverConnection()->getPeer()) { | |
714 | assert(peer->secure.encryptTransport); | |
96993ee0 | 715 | Security::ContextPtr sslContext(peer->sslContext); |
1b091aec CT |
716 | return sslContext; |
717 | } | |
a7300c7a | 718 | return ::Config.ssl_client.sslContext; |
1b091aec CT |
719 | } |
720 | ||
721 | SSL * | |
722 | Ssl::BlindPeerConnector::initializeSsl() | |
723 | { | |
724 | SSL *ssl = Ssl::PeerConnector::initializeSsl(); | |
725 | if (!ssl) | |
726 | return NULL; | |
727 | ||
a7300c7a CT |
728 | if (const CachePeer *peer = serverConnection()->getPeer()) { |
729 | assert(peer); | |
1b091aec | 730 | |
a7300c7a CT |
731 | // NP: domain may be a raw-IP but it is now always set |
732 | assert(!peer->secure.sslDomain.isEmpty()); | |
1b091aec | 733 | |
a7300c7a CT |
734 | // const loss is okay here, ssl_ex_index_server is only read and not assigned a destructor |
735 | SBuf *host = new SBuf(peer->secure.sslDomain); | |
736 | SSL_set_ex_data(ssl, ssl_ex_index_server, host); | |
1b091aec | 737 | |
a7300c7a CT |
738 | if (peer->sslSession) |
739 | SSL_set_session(ssl, peer->sslSession); | |
740 | } else { | |
41e803be CT |
741 | SBuf *hostName = new SBuf(request->url.host()); |
742 | SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostName); | |
a7300c7a | 743 | } |
1b091aec CT |
744 | |
745 | return ssl; | |
746 | } | |
747 | ||
748 | void | |
749 | Ssl::BlindPeerConnector::noteNegotiationDone(ErrorState *error) | |
750 | { | |
751 | if (error) { | |
752 | // XXX: forward.cc calls peerConnectSucceeded() after an OK TCP connect but | |
753 | // we call peerConnectFailed() if SSL failed afterwards. Is that OK? | |
754 | // It is not clear whether we should call peerConnectSucceeded/Failed() | |
755 | // based on TCP results, SSL results, or both. And the code is probably not | |
756 | // consistent in this aspect across tunnelling and forwarding modules. | |
757 | if (CachePeer *p = serverConnection()->getPeer()) | |
758 | peerConnectFailed(p); | |
759 | return; | |
760 | } | |
761 | ||
762 | const int fd = serverConnection()->fd; | |
763 | SSL *ssl = fd_table[fd].ssl; | |
764 | if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) { | |
765 | if (serverConnection()->getPeer()->sslSession) | |
766 | SSL_SESSION_free(serverConnection()->getPeer()->sslSession); | |
767 | ||
768 | serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl); | |
769 | } | |
770 | } | |
771 | ||
96993ee0 | 772 | Security::ContextPtr |
1b091aec CT |
773 | Ssl::PeekingPeerConnector::getSslContext() |
774 | { | |
775 | // XXX: locate a per-server context in Security:: instead | |
776 | return ::Config.ssl_client.sslContext; | |
777 | } | |
778 | ||
779 | SSL * | |
780 | Ssl::PeekingPeerConnector::initializeSsl() | |
781 | { | |
782 | SSL *ssl = Ssl::PeerConnector::initializeSsl(); | |
783 | if (!ssl) | |
784 | return NULL; | |
785 | ||
786 | if (ConnStateData *csd = request->clientConnectionManager.valid()) { | |
787 | ||
788 | // client connection is required in the case we need to splice | |
789 | // or terminate client and server connections | |
790 | assert(clientConn != NULL); | |
791 | SBuf *hostName = NULL; | |
792 | Ssl::ClientBio *cltBio = NULL; | |
793 | ||
794 | //Enable Status_request tls extension, required to bump some clients | |
795 | SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp); | |
796 | ||
797 | // In server-first bumping mode, clientSsl is NULL. | |
798 | if (SSL *clientSsl = fd_table[clientConn->fd].ssl) { | |
799 | BIO *b = SSL_get_rbio(clientSsl); | |
800 | cltBio = static_cast<Ssl::ClientBio *>(b->ptr); | |
801 | const Ssl::Bio::sslFeatures &features = cltBio->getFeatures(); | |
802 | if (!features.serverName.isEmpty()) | |
803 | hostName = new SBuf(features.serverName); | |
804 | } | |
805 | ||
806 | if (!hostName) { | |
807 | // While we are peeking at the certificate, we may not know the server | |
808 | // name that the client will request (after interception or CONNECT) | |
809 | // unless it was the CONNECT request with a user-typed address. | |
810 | const bool isConnectRequest = !csd->port->flags.isIntercepted(); | |
811 | if (!request->flags.sslPeek || isConnectRequest) | |
5c51bffb | 812 | hostName = new SBuf(request->url.host()); |
1b091aec CT |
813 | } |
814 | ||
815 | if (hostName) | |
816 | SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostName); | |
817 | ||
818 | Must(!csd->serverBump() || csd->serverBump()->step <= Ssl::bumpStep2); | |
819 | if (csd->sslBumpMode == Ssl::bumpPeek || csd->sslBumpMode == Ssl::bumpStare) { | |
820 | assert(cltBio); | |
821 | const Ssl::Bio::sslFeatures &features = cltBio->getFeatures(); | |
822 | if (features.sslVersion != -1) { | |
823 | features.applyToSSL(ssl, csd->sslBumpMode); | |
824 | // Should we allow it for all protocols? | |
825 | if (features.sslVersion >= 3) { | |
826 | BIO *b = SSL_get_rbio(ssl); | |
827 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
828 | // Inherite client features, like SSL version, SNI and other | |
829 | srvBio->setClientFeatures(features); | |
ffbfd5be | 830 | srvBio->recordInput(true); |
1b091aec CT |
831 | srvBio->mode(csd->sslBumpMode); |
832 | } | |
833 | } | |
834 | } else { | |
835 | // Set client SSL options | |
836 | SSL_set_options(ssl, ::Security::ProxyOutgoingConfig.parsedOptions); | |
837 | ||
838 | // Use SNI TLS extension only when we connect directly | |
839 | // to the origin server and we know the server host name. | |
c52a4693 AW |
840 | const char *sniServer = NULL; |
841 | const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host; | |
842 | if (!hostName || redirected) | |
843 | sniServer = !request->url.hostIsNumeric() ? request->url.host() : NULL; | |
844 | else | |
845 | sniServer = hostName->c_str(); | |
846 | ||
1b091aec CT |
847 | if (sniServer) |
848 | Ssl::setClientSNI(ssl, sniServer); | |
849 | } | |
850 | ||
851 | // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE | |
852 | X509 *peeked_cert; | |
853 | if (csd->serverBump() && | |
854 | (peeked_cert = csd->serverBump()->serverCert.get())) { | |
855 | CRYPTO_add(&(peeked_cert->references),1,CRYPTO_LOCK_X509); | |
856 | SSL_set_ex_data(ssl, ssl_ex_index_ssl_peeked_cert, peeked_cert); | |
857 | } | |
858 | } | |
859 | ||
860 | return ssl; | |
861 | } | |
862 | ||
863 | void | |
864 | Ssl::PeekingPeerConnector::noteNegotiationDone(ErrorState *error) | |
865 | { | |
866 | SSL *ssl = fd_table[serverConnection()->fd].ssl; | |
867 | ||
868 | // Check the list error with | |
869 | if (!request->clientConnectionManager.valid() || ! ssl) | |
870 | return; | |
871 | ||
872 | // remember the server certificate from the ErrorDetail object | |
873 | if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) { | |
874 | // remember validation errors, if any | |
875 | if (certErrors) { | |
876 | if (serverBump->sslErrors) | |
877 | cbdataReferenceDone(serverBump->sslErrors); | |
878 | serverBump->sslErrors = cbdataReference(certErrors); | |
879 | } | |
880 | ||
881 | if (!serverBump->serverCert.get()) { | |
882 | // remember the server certificate from the ErrorDetail object | |
883 | if (error && error->detail && error->detail->peerCert()) | |
884 | serverBump->serverCert.resetAndLock(error->detail->peerCert()); | |
885 | else { | |
886 | handleServerCertificate(); | |
887 | } | |
888 | } | |
889 | ||
890 | if (error) { | |
891 | // For intercepted connections, set the host name to the server | |
892 | // certificate CN. Otherwise, we just hope that CONNECT is using | |
893 | // a user-entered address (a host name or a user-entered IP). | |
894 | const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted(); | |
895 | if (request->flags.sslPeek && !isConnectRequest) { | |
896 | if (X509 *srvX509 = serverBump->serverCert.get()) { | |
897 | if (const char *name = Ssl::CommonHostName(srvX509)) { | |
851feda6 | 898 | request->url.host(name); |
1b091aec CT |
899 | debugs(83, 3, "reset request host: " << name); |
900 | } | |
901 | } | |
902 | } | |
903 | } | |
904 | } | |
905 | ||
ce74a2eb CT |
906 | if (!error) { |
907 | serverCertificateVerified(); | |
908 | if (splice) | |
909 | switchToTunnel(request.getRaw(), clientConn, serverConn); | |
910 | } | |
1b091aec CT |
911 | } |
912 | ||
913 | void | |
914 | Ssl::PeekingPeerConnector::noteWantWrite() | |
915 | { | |
916 | const int fd = serverConnection()->fd; | |
917 | SSL *ssl = fd_table[fd].ssl; | |
918 | BIO *b = SSL_get_rbio(ssl); | |
919 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
920 | ||
921 | if ((srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) { | |
922 | debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd); | |
923 | checkForPeekAndSplice(); | |
924 | return; | |
925 | } | |
926 | ||
927 | Ssl::PeerConnector::noteWantWrite(); | |
928 | } | |
929 | ||
930 | void | |
931 | Ssl::PeekingPeerConnector::noteSslNegotiationError(const int result, const int ssl_error, const int ssl_lib_error) | |
932 | { | |
933 | const int fd = serverConnection()->fd; | |
934 | SSL *ssl = fd_table[fd].ssl; | |
935 | BIO *b = SSL_get_rbio(ssl); | |
936 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
937 | ||
938 | // In Peek mode, the ClientHello message sent to the server. If the | |
939 | // server resuming a previous (spliced) SSL session with the client, | |
940 | // then probably we are here because local SSL object does not know | |
941 | // anything about the session being resumed. | |
942 | // | |
943 | if (srvBio->bumpMode() == Ssl::bumpPeek && (resumingSession = srvBio->resumingSession())) { | |
944 | // we currently splice all resumed sessions unconditionally | |
945 | if (const bool spliceResumed = true) { | |
946 | bypassCertValidator(); | |
566f8310 | 947 | checkForPeekAndSpliceMatched(Ssl::bumpSplice); |
1b091aec CT |
948 | return; |
949 | } // else fall through to find a matching ssl_bump action (with limited info) | |
950 | } | |
951 | ||
952 | // If we are in peek-and-splice mode and still we did not write to | |
953 | // server yet, try to see if we should splice. | |
954 | // In this case the connection can be saved. | |
955 | // If the checklist decision is do not splice a new error will | |
956 | // occur in the next SSL_connect call, and we will fail again. | |
957 | // Abort on certificate validation errors to avoid splicing and | |
958 | // thus hiding them. | |
959 | // Abort if no certificate found probably because of malformed or | |
960 | // unsupported server Hello message (TODO: make configurable). | |
961 | if (!SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) && | |
962 | (srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) { | |
f97700a0 | 963 | Security::CertPointer serverCert(SSL_get_peer_certificate(ssl)); |
1b091aec CT |
964 | if (serverCert.get()) { |
965 | debugs(81, 3, "Error (" << ERR_error_string(ssl_lib_error, NULL) << ") but, hold write on SSL connection on FD " << fd); | |
966 | checkForPeekAndSplice(); | |
967 | return; | |
968 | } | |
969 | } | |
970 | ||
971 | // else call parent noteNegotiationError to produce an error page | |
972 | Ssl::PeerConnector::noteSslNegotiationError(result, ssl_error, ssl_lib_error); | |
973 | } | |
974 | ||
975 | void | |
976 | Ssl::PeekingPeerConnector::handleServerCertificate() | |
977 | { | |
978 | if (serverCertificateHandled) | |
979 | return; | |
980 | ||
981 | if (ConnStateData *csd = request->clientConnectionManager.valid()) { | |
982 | const int fd = serverConnection()->fd; | |
983 | SSL *ssl = fd_table[fd].ssl; | |
f97700a0 | 984 | Security::CertPointer serverCert(SSL_get_peer_certificate(ssl)); |
1b091aec CT |
985 | if (!serverCert.get()) |
986 | return; | |
987 | ||
988 | serverCertificateHandled = true; | |
989 | ||
1b091aec CT |
990 | // remember the server certificate for later use |
991 | if (Ssl::ServerBump *serverBump = csd->serverBump()) { | |
992 | serverBump->serverCert.reset(serverCert.release()); | |
993 | } | |
994 | } | |
995 | } | |
7fe542fe | 996 | |
ce74a2eb CT |
997 | void |
998 | Ssl::PeekingPeerConnector::serverCertificateVerified() | |
999 | { | |
1000 | if (ConnStateData *csd = request->clientConnectionManager.valid()) { | |
f97700a0 | 1001 | Security::CertPointer serverCert; |
ce74a2eb CT |
1002 | if(Ssl::ServerBump *serverBump = csd->serverBump()) |
1003 | serverCert.resetAndLock(serverBump->serverCert.get()); | |
1004 | else { | |
1005 | const int fd = serverConnection()->fd; | |
1006 | SSL *ssl = fd_table[fd].ssl; | |
1007 | serverCert.reset(SSL_get_peer_certificate(ssl)); | |
1008 | } | |
1009 | if (serverCert.get()) { | |
1010 | csd->resetSslCommonName(Ssl::CommonHostName(serverCert.get())); | |
1011 | debugs(83, 5, "HTTPS server CN: " << csd->sslCommonName() << | |
1012 | " bumped: " << *serverConnection()); | |
1013 | } | |
1014 | } | |
1015 | } | |
1016 |