]>
Commit | Line | Data |
---|---|---|
a23223bf | 1 | /* |
ef57eb7b | 2 | * Copyright (C) 1996-2016 The Squid Software Foundation and contributors |
a23223bf | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
a23223bf CT |
7 | */ |
8 | ||
32f1ca3f | 9 | /* DEBUG: section 83 TLS Server/Peer negotiation */ |
bbc27441 | 10 | |
a23223bf CT |
11 | #include "squid.h" |
12 | #include "acl/FilledChecklist.h" | |
a23223bf | 13 | #include "comm/Loops.h" |
55369ae6 | 14 | #include "Downloader.h" |
a23223bf CT |
15 | #include "errorpage.h" |
16 | #include "fde.h" | |
54fb1cbf | 17 | #include "http/Stream.h" |
a23223bf | 18 | #include "HttpRequest.h" |
36698640 | 19 | #include "security/NegotiationHistory.h" |
a72b6e88 | 20 | #include "security/PeerConnector.h" |
7f4e9b73 | 21 | #include "SquidConfig.h" |
a72b6e88 | 22 | #if USE_OPENSSL |
31855516 | 23 | #include "ssl/bio.h" |
a23223bf CT |
24 | #include "ssl/cert_validate_message.h" |
25 | #include "ssl/Config.h" | |
a23223bf | 26 | #include "ssl/helper.h" |
a72b6e88 | 27 | #endif |
a23223bf | 28 | |
a72b6e88 | 29 | CBDATA_NAMESPACED_CLASS_INIT(Security, PeerConnector); |
a23223bf | 30 | |
a72b6e88 AJ |
31 | Security::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const AccessLogEntryPointer &alp, const time_t timeout) : |
32 | AsyncJob("Security::PeerConnector"), | |
f53969cc | 33 | serverConn(aServerConn), |
d4ddb3e6 | 34 | al(alp), |
f53969cc SM |
35 | callback(aCallback), |
36 | negotiationTimeout(timeout), | |
37 | startTime(squid_curtime), | |
54fb1cbf | 38 | useCertValidator_(true), |
4e526b93 | 39 | certsDownloads(0) |
a23223bf | 40 | { |
a72b6e88 | 41 | debugs(83, 5, "Security::PeerConnector constructed, this=" << (void*)this); |
a23223bf CT |
42 | // if this throws, the caller's cb dialer is not our CbDialer |
43 | Must(dynamic_cast<CbDialer*>(callback->getDialer())); | |
44 | } | |
45 | ||
a72b6e88 | 46 | Security::PeerConnector::~PeerConnector() |
a23223bf | 47 | { |
a72b6e88 | 48 | debugs(83, 5, "Security::PeerConnector destructed, this=" << (void*)this); |
a23223bf CT |
49 | } |
50 | ||
a72b6e88 | 51 | bool Security::PeerConnector::doneAll() const |
a23223bf CT |
52 | { |
53 | return (!callback || callback->canceled()) && AsyncJob::doneAll(); | |
54 | } | |
55 | ||
56 | /// Preps connection and SSL state. Calls negotiate(). | |
57 | void | |
a72b6e88 | 58 | Security::PeerConnector::start() |
a23223bf CT |
59 | { |
60 | AsyncJob::start(); | |
61 | ||
eba8d9bb AJ |
62 | Security::SessionPointer tmp; |
63 | if (prepareSocket() && initializeTls(tmp)) | |
a23223bf | 64 | negotiateSsl(); |
ae3ac744 AJ |
65 | else |
66 | mustStop("Security::PeerConnector TLS socket initialize failed"); | |
a23223bf CT |
67 | } |
68 | ||
69 | void | |
a72b6e88 | 70 | Security::PeerConnector::commCloseHandler(const CommCloseCbParams ¶ms) |
a23223bf | 71 | { |
a72b6e88 AJ |
72 | debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data); |
73 | connectionClosed("Security::PeerConnector::commCloseHandler"); | |
a23223bf CT |
74 | } |
75 | ||
76 | void | |
a72b6e88 | 77 | Security::PeerConnector::connectionClosed(const char *reason) |
a23223bf CT |
78 | { |
79 | mustStop(reason); | |
80 | callback = NULL; | |
81 | } | |
82 | ||
83 | bool | |
a72b6e88 | 84 | Security::PeerConnector::prepareSocket() |
a23223bf CT |
85 | { |
86 | const int fd = serverConnection()->fd; | |
87 | if (!Comm::IsConnOpen(serverConn) || fd_table[serverConn->fd].closing()) { | |
a72b6e88 | 88 | connectionClosed("Security::PeerConnector::prepareSocket"); |
a23223bf CT |
89 | return false; |
90 | } | |
91 | ||
92 | // watch for external connection closures | |
a72b6e88 AJ |
93 | typedef CommCbMemFunT<Security::PeerConnector, CommCloseCbParams> Dialer; |
94 | closeHandler = JobCallback(9, 5, Dialer, this, Security::PeerConnector::commCloseHandler); | |
a23223bf CT |
95 | comm_add_close_handler(fd, closeHandler); |
96 | return true; | |
97 | } | |
98 | ||
eba8d9bb | 99 | bool |
a72b6e88 | 100 | Security::PeerConnector::initializeTls(Security::SessionPointer &serverSession) |
a23223bf | 101 | { |
a72b6e88 | 102 | #if USE_OPENSSL |
96993ee0 | 103 | Security::ContextPtr sslContext(getSslContext()); |
a23223bf CT |
104 | assert(sslContext); |
105 | ||
157c5ace | 106 | if (!Ssl::CreateClient(sslContext, serverConnection(), "server https start")) { |
a23223bf CT |
107 | ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw()); |
108 | anErr->xerrno = errno; | |
109 | debugs(83, DBG_IMPORTANT, "Error allocating SSL handle: " << ERR_error_string(ERR_get_error(), NULL)); | |
1b091aec CT |
110 | noteNegotiationDone(anErr); |
111 | bail(anErr); | |
eba8d9bb | 112 | return false; |
a23223bf CT |
113 | } |
114 | ||
157c5ace | 115 | // A TLS/SSL session has now been created for the connection and stored in fd_table |
eba8d9bb | 116 | serverSession = fd_table[serverConnection()->fd].ssl; |
157c5ace | 117 | |
a23223bf CT |
118 | // If CertValidation Helper used do not lookup checklist for errors, |
119 | // but keep a list of errors to send it to CertValidator | |
120 | if (!Ssl::TheConfig.ssl_crt_validator) { | |
121 | // Create the ACL check list now, while we have access to more info. | |
122 | // The list is used in ssl_verify_cb() and is freed in ssl_free(). | |
123 | if (acl_access *acl = ::Config.ssl_client.cert_error) { | |
124 | ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str); | |
d4ddb3e6 | 125 | check->al = al; |
a23223bf | 126 | // check->fd(fd); XXX: need client FD here |
eba8d9bb | 127 | SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check); |
a23223bf CT |
128 | } |
129 | } | |
a72b6e88 | 130 | |
eba8d9bb | 131 | return true; |
a72b6e88 AJ |
132 | #else |
133 | return false; | |
134 | #endif | |
a23223bf CT |
135 | } |
136 | ||
8aec3e1b | 137 | void |
a72b6e88 | 138 | Security::PeerConnector::setReadTimeout() |
8aec3e1b CT |
139 | { |
140 | int timeToRead; | |
141 | if (negotiationTimeout) { | |
142 | const int timeUsed = squid_curtime - startTime; | |
143 | const int timeLeft = max(0, static_cast<int>(negotiationTimeout - timeUsed)); | |
144 | timeToRead = min(static_cast<int>(::Config.Timeout.read), timeLeft); | |
145 | } else | |
146 | timeToRead = ::Config.Timeout.read; | |
147 | AsyncCall::Pointer nil; | |
148 | commSetConnTimeout(serverConnection(), timeToRead, nil); | |
149 | } | |
150 | ||
36698640 | 151 | void |
a72b6e88 | 152 | Security::PeerConnector::recordNegotiationDetails() |
36698640 | 153 | { |
a72b6e88 | 154 | #if USE_OPENSSL |
36698640 CT |
155 | const int fd = serverConnection()->fd; |
156 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); | |
157 | ||
158 | // retrieve TLS server negotiated information if any | |
159 | serverConnection()->tlsNegotiations()->retrieveNegotiatedInfo(ssl); | |
160 | // retrieve TLS parsed extra info | |
161 | BIO *b = SSL_get_rbio(ssl); | |
162 | Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(b->ptr); | |
163 | if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails()) | |
164 | serverConnection()->tlsNegotiations()->retrieveParsedInfo(details); | |
a72b6e88 | 165 | #endif |
36698640 CT |
166 | } |
167 | ||
a23223bf | 168 | void |
a72b6e88 | 169 | Security::PeerConnector::negotiateSsl() |
a23223bf CT |
170 | { |
171 | if (!Comm::IsConnOpen(serverConnection()) || fd_table[serverConnection()->fd].closing()) | |
172 | return; | |
173 | ||
a72b6e88 | 174 | #if USE_OPENSSL |
a23223bf | 175 | const int fd = serverConnection()->fd; |
33cc0629 | 176 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); |
a23223bf | 177 | const int result = SSL_connect(ssl); |
a72b6e88 AJ |
178 | #else |
179 | const int result = -1; | |
180 | #endif | |
a23223bf CT |
181 | if (result <= 0) { |
182 | handleNegotiateError(result); | |
183 | return; // we might be gone by now | |
184 | } | |
185 | ||
36698640 CT |
186 | recordNegotiationDetails(); |
187 | ||
c91d4d4e CT |
188 | if (!sslFinalized()) |
189 | return; | |
190 | ||
191 | callBack(); | |
192 | } | |
193 | ||
194 | bool | |
a72b6e88 | 195 | Security::PeerConnector::sslFinalized() |
c91d4d4e | 196 | { |
a72b6e88 | 197 | #if USE_OPENSSL |
1b091aec CT |
198 | if (Ssl::TheConfig.ssl_crt_validator && useCertValidator_) { |
199 | const int fd = serverConnection()->fd; | |
33cc0629 | 200 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); |
a23223bf | 201 | |
a23223bf CT |
202 | Ssl::CertValidationRequest validationRequest; |
203 | // WARNING: Currently we do not use any locking for any of the | |
204 | // members of the Ssl::CertValidationRequest class. In this code the | |
205 | // Ssl::CertValidationRequest object used only to pass data to | |
206 | // Ssl::CertValidationHelper::submit method. | |
207 | validationRequest.ssl = ssl; | |
5c51bffb | 208 | validationRequest.domainName = request->url.host(); |
a23223bf CT |
209 | if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors))) |
210 | // validationRequest disappears on return so no need to cbdataReference | |
211 | validationRequest.errors = errs; | |
212 | else | |
213 | validationRequest.errors = NULL; | |
214 | try { | |
215 | debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd."); | |
a72b6e88 | 216 | AsyncCall::Pointer call = asyncCall(83,5, "Security::PeerConnector::sslCrtvdHandleReply", Ssl::CertValidationHelper::CbDialer(this, &Security::PeerConnector::sslCrtvdHandleReply, nullptr)); |
0e208dad | 217 | Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, call); |
c91d4d4e | 218 | return false; |
a23223bf CT |
219 | } catch (const std::exception &e) { |
220 | debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " << | |
221 | "request for " << validationRequest.domainName << | |
222 | " certificate: " << e.what() << "; will now block to " << | |
223 | "validate that certificate."); | |
224 | // fall through to do blocking in-process generation. | |
225 | ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw()); | |
1b091aec CT |
226 | |
227 | noteNegotiationDone(anErr); | |
a23223bf | 228 | bail(anErr); |
a23223bf | 229 | serverConn->close(); |
c91d4d4e | 230 | return true; |
a23223bf CT |
231 | } |
232 | } | |
a72b6e88 | 233 | #endif |
1b091aec CT |
234 | |
235 | noteNegotiationDone(NULL); | |
c91d4d4e | 236 | return true; |
a23223bf CT |
237 | } |
238 | ||
a72b6e88 | 239 | #if USE_OPENSSL |
a23223bf | 240 | void |
a72b6e88 | 241 | Security::PeerConnector::sslCrtvdHandleReply(Ssl::CertValidationResponse::Pointer validationResponse) |
a23223bf | 242 | { |
0e208dad | 243 | Must(validationResponse != NULL); |
a23223bf | 244 | |
a23223bf CT |
245 | Ssl::ErrorDetail *errDetails = NULL; |
246 | bool validatorFailed = false; | |
247 | if (!Comm::IsConnOpen(serverConnection())) { | |
248 | return; | |
249 | } | |
250 | ||
0e208dad | 251 | debugs(83,5, request->url.host() << " cert validation result: " << validationResponse->resultCode); |
a23223bf | 252 | |
088f0761 CT |
253 | if (validationResponse->resultCode == ::Helper::Error) { |
254 | if (Ssl::CertErrors *errs = sslCrtvdCheckForErrors(*validationResponse, errDetails)) { | |
255 | Security::SessionPtr ssl = fd_table[serverConnection()->fd].ssl.get(); | |
256 | Ssl::CertErrors *oldErrs = static_cast<Ssl::CertErrors*>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)); | |
257 | SSL_set_ex_data(ssl, ssl_ex_index_ssl_errors, (void *)errs); | |
258 | delete oldErrs; | |
259 | } | |
260 | } else if (validationResponse->resultCode != ::Helper::Okay) | |
a23223bf CT |
261 | validatorFailed = true; |
262 | ||
263 | if (!errDetails && !validatorFailed) { | |
1b091aec CT |
264 | noteNegotiationDone(NULL); |
265 | callBack(); | |
a23223bf CT |
266 | return; |
267 | } | |
268 | ||
269 | ErrorState *anErr = NULL; | |
270 | if (validatorFailed) { | |
271 | anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw()); | |
272 | } else { | |
a23223bf CT |
273 | anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request.getRaw()); |
274 | anErr->detail = errDetails; | |
275 | /*anErr->xerrno= Should preserved*/ | |
276 | } | |
277 | ||
1b091aec | 278 | noteNegotiationDone(anErr); |
a23223bf | 279 | bail(anErr); |
a23223bf CT |
280 | serverConn->close(); |
281 | return; | |
282 | } | |
a72b6e88 | 283 | #endif |
a23223bf | 284 | |
a72b6e88 | 285 | #if USE_OPENSSL |
a23223bf CT |
286 | /// Checks errors in the cert. validator response against sslproxy_cert_error. |
287 | /// The first honored error, if any, is returned via errDetails parameter. | |
288 | /// The method returns all seen errors except SSL_ERROR_NONE as Ssl::CertErrors. | |
289 | Ssl::CertErrors * | |
a72b6e88 | 290 | Security::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails) |
a23223bf CT |
291 | { |
292 | Ssl::CertErrors *errs = NULL; | |
293 | ||
294 | ACLFilledChecklist *check = NULL; | |
d4ddb3e6 | 295 | if (acl_access *acl = ::Config.ssl_client.cert_error) { |
a23223bf | 296 | check = new ACLFilledChecklist(acl, request.getRaw(), dash_str); |
d4ddb3e6 CT |
297 | check->al = al; |
298 | } | |
a23223bf | 299 | |
33cc0629 | 300 | Security::SessionPtr ssl = fd_table[serverConnection()->fd].ssl.get(); |
a23223bf CT |
301 | typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI; |
302 | for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) { | |
303 | debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason); | |
304 | ||
305 | assert(i->error_no != SSL_ERROR_NONE); | |
306 | ||
307 | if (!errDetails) { | |
308 | bool allowed = false; | |
309 | if (check) { | |
b4e6a8d4 | 310 | check->sslErrors = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get(), i->error_depth)); |
a23223bf CT |
311 | if (check->fastCheck() == ACCESS_ALLOWED) |
312 | allowed = true; | |
313 | } | |
314 | // else the Config.ssl_client.cert_error access list is not defined | |
315 | // and the first error will cause the error page | |
316 | ||
317 | if (allowed) { | |
318 | debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer"); | |
319 | } else { | |
320 | debugs(83, 5, "confirming SSL error " << i->error_no); | |
321 | X509 *brokenCert = i->cert.get(); | |
f97700a0 | 322 | Security::CertPointer peerCert(SSL_get_peer_certificate(ssl)); |
a23223bf CT |
323 | const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str(); |
324 | errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason); | |
325 | } | |
326 | if (check) { | |
327 | delete check->sslErrors; | |
328 | check->sslErrors = NULL; | |
329 | } | |
330 | } | |
331 | ||
332 | if (!errs) | |
b4e6a8d4 | 333 | errs = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get(), i->error_depth)); |
a23223bf | 334 | else |
b4e6a8d4 | 335 | errs->push_back_unique(Ssl::CertError(i->error_no, i->cert.get(), i->error_depth)); |
a23223bf CT |
336 | } |
337 | if (check) | |
338 | delete check; | |
339 | ||
340 | return errs; | |
341 | } | |
a72b6e88 | 342 | #endif |
a23223bf CT |
343 | |
344 | /// A wrapper for Comm::SetSelect() notifications. | |
345 | void | |
a72b6e88 | 346 | Security::PeerConnector::NegotiateSsl(int, void *data) |
a23223bf CT |
347 | { |
348 | PeerConnector *pc = static_cast<PeerConnector*>(data); | |
349 | // Use job calls to add done() checks and other job logic/protections. | |
a72b6e88 | 350 | CallJobHere(83, 7, pc, Security::PeerConnector, negotiateSsl); |
a23223bf CT |
351 | } |
352 | ||
353 | void | |
a72b6e88 | 354 | Security::PeerConnector::handleNegotiateError(const int ret) |
a23223bf | 355 | { |
a72b6e88 | 356 | #if USE_OPENSSL |
a23223bf CT |
357 | const int fd = serverConnection()->fd; |
358 | unsigned long ssl_lib_error = SSL_ERROR_NONE; | |
33cc0629 | 359 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); |
f5518dca | 360 | const int ssl_error = SSL_get_error(ssl, ret); |
a23223bf | 361 | |
e2849af8 | 362 | switch (ssl_error) { |
e2849af8 | 363 | case SSL_ERROR_WANT_READ: |
1b091aec | 364 | noteWantRead(); |
e2849af8 | 365 | return; |
a23223bf | 366 | |
e2849af8 | 367 | case SSL_ERROR_WANT_WRITE: |
1b091aec | 368 | noteWantWrite(); |
e2849af8 | 369 | return; |
a23223bf | 370 | |
e2849af8 A |
371 | case SSL_ERROR_SSL: |
372 | case SSL_ERROR_SYSCALL: | |
373 | ssl_lib_error = ERR_get_error(); | |
1b091aec CT |
374 | // proceed to the general error handling code |
375 | break; | |
376 | default: | |
377 | // no special error handling for all other errors | |
378 | break; | |
379 | } | |
36698640 | 380 | |
d9219c2b | 381 | // Log connection details, if any |
36698640 | 382 | recordNegotiationDetails(); |
1b091aec | 383 | noteSslNegotiationError(ret, ssl_error, ssl_lib_error); |
a72b6e88 | 384 | #endif |
1b091aec | 385 | } |
a23223bf | 386 | |
1b091aec | 387 | void |
a72b6e88 | 388 | Security::PeerConnector::noteWantRead() |
1b091aec | 389 | { |
1b091aec | 390 | const int fd = serverConnection()->fd; |
212e5aee | 391 | #if USE_OPENSSL |
54fb1cbf | 392 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); |
55369ae6 AR |
393 | BIO *b = SSL_get_rbio(ssl); |
394 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
395 | if (srvBio->holdRead()) { | |
396 | if (srvBio->gotHello()) { | |
397 | if (checkForMissingCertificates()) | |
398 | return; // Wait to download certificates before proceed. | |
399 | ||
400 | srvBio->holdRead(false); | |
168d2b30 | 401 | // schedule a negotiateSSl to allow openSSL parse received data |
212e5aee | 402 | Security::PeerConnector::NegotiateSsl(fd, this); |
55369ae6 AR |
403 | return; |
404 | } else if (srvBio->gotHelloFailed()) { | |
405 | srvBio->holdRead(false); | |
406 | debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd); | |
168d2b30 | 407 | // schedule a negotiateSSl to allow openSSL parse received data |
212e5aee | 408 | Security::PeerConnector::NegotiateSsl(fd, this); |
55369ae6 AR |
409 | return; |
410 | } | |
411 | } | |
212e5aee | 412 | #endif |
55369ae6 | 413 | setReadTimeout(); |
1b091aec CT |
414 | Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0); |
415 | } | |
7f4e9b73 | 416 | |
1b091aec | 417 | void |
a72b6e88 | 418 | Security::PeerConnector::noteWantWrite() |
1b091aec CT |
419 | { |
420 | const int fd = serverConnection()->fd; | |
421 | Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0); | |
422 | return; | |
423 | } | |
a23223bf | 424 | |
1b091aec | 425 | void |
a72b6e88 | 426 | Security::PeerConnector::noteSslNegotiationError(const int ret, const int ssl_error, const int ssl_lib_error) |
1b091aec | 427 | { |
a72b6e88 AJ |
428 | #if USE_OPENSSL // not used unless OpenSSL enabled. |
429 | #if defined(EPROTO) | |
1b091aec CT |
430 | int sysErrNo = EPROTO; |
431 | #else | |
432 | int sysErrNo = EACCES; | |
433 | #endif | |
a23223bf | 434 | |
1b091aec CT |
435 | // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1 |
436 | if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0) | |
437 | sysErrNo = errno; | |
a23223bf | 438 | |
1b091aec CT |
439 | const int fd = serverConnection()->fd; |
440 | debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd << | |
441 | ": " << ERR_error_string(ssl_lib_error, NULL) << " (" << | |
442 | ssl_error << "/" << ret << "/" << errno << ")"); | |
a23223bf | 443 | |
1b091aec CT |
444 | ErrorState *anErr = NULL; |
445 | if (request != NULL) | |
446 | anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request.getRaw()); | |
447 | else | |
448 | anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, NULL); | |
a23223bf CT |
449 | anErr->xerrno = sysErrNo; |
450 | ||
33cc0629 | 451 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); |
a23223bf CT |
452 | Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail); |
453 | if (errFromFailure != NULL) { | |
454 | // The errFromFailure is attached to the ssl object | |
455 | // and will be released when ssl object destroyed. | |
456 | // Copy errFromFailure to a new Ssl::ErrorDetail object | |
457 | anErr->detail = new Ssl::ErrorDetail(*errFromFailure); | |
458 | } else { | |
459 | // server_cert can be NULL here | |
460 | X509 *server_cert = SSL_get_peer_certificate(ssl); | |
461 | anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert, NULL); | |
462 | X509_free(server_cert); | |
463 | } | |
464 | ||
465 | if (ssl_lib_error != SSL_ERROR_NONE) | |
466 | anErr->detail->setLibError(ssl_lib_error); | |
467 | ||
1b091aec | 468 | noteNegotiationDone(anErr); |
a23223bf | 469 | bail(anErr); |
a72b6e88 | 470 | #endif |
a23223bf CT |
471 | } |
472 | ||
473 | void | |
a72b6e88 | 474 | Security::PeerConnector::bail(ErrorState *error) |
a23223bf CT |
475 | { |
476 | Must(error); // or the recepient will not know there was a problem | |
a23223bf CT |
477 | Must(callback != NULL); |
478 | CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer()); | |
479 | Must(dialer); | |
480 | dialer->answer().error = error; | |
481 | ||
482 | callBack(); | |
483 | // Our job is done. The callabck recepient will probably close the failed | |
484 | // peer connection and try another peer or go direct (if possible). We | |
485 | // can close the connection ourselves (our error notification would reach | |
486 | // the recepient before the fd-closure notification), but we would rather | |
487 | // minimize the number of fd-closure notifications and let the recepient | |
488 | // manage the TCP state of the connection. | |
489 | } | |
490 | ||
491 | void | |
a72b6e88 | 492 | Security::PeerConnector::callBack() |
a23223bf CT |
493 | { |
494 | AsyncCall::Pointer cb = callback; | |
495 | // Do this now so that if we throw below, swanSong() assert that we _tried_ | |
496 | // to call back holds. | |
497 | callback = NULL; // this should make done() true | |
498 | ||
499 | // remove close handler | |
500 | comm_remove_close_handler(serverConnection()->fd, closeHandler); | |
501 | ||
502 | CbDialer *dialer = dynamic_cast<CbDialer*>(cb->getDialer()); | |
503 | Must(dialer); | |
504 | dialer->answer().conn = serverConnection(); | |
505 | ScheduleCallHere(cb); | |
506 | } | |
507 | ||
a23223bf | 508 | void |
a72b6e88 | 509 | Security::PeerConnector::swanSong() |
a23223bf CT |
510 | { |
511 | // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any | |
512 | AsyncJob::swanSong(); | |
566f8310 AR |
513 | if (callback != NULL) { // paranoid: we have left the caller waiting |
514 | debugs(83, DBG_IMPORTANT, "BUG: Unexpected state while connecting to a cache_peer or origin server"); | |
515 | ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw()); | |
516 | bail(anErr); | |
517 | assert(!callback); | |
518 | return; | |
519 | } | |
a23223bf CT |
520 | } |
521 | ||
522 | const char * | |
a72b6e88 | 523 | Security::PeerConnector::status() const |
a23223bf CT |
524 | { |
525 | static MemBuf buf; | |
526 | buf.reset(); | |
527 | ||
528 | // TODO: redesign AsyncJob::status() API to avoid this | |
529 | // id and stop reason reporting duplication. | |
530 | buf.append(" [", 2); | |
531 | if (stopReason != NULL) { | |
07e6d76e AJ |
532 | buf.append("Stopped, reason:", 16); |
533 | buf.appendf("%s",stopReason); | |
a23223bf CT |
534 | } |
535 | if (serverConn != NULL) | |
07e6d76e | 536 | buf.appendf(" FD %d", serverConn->fd); |
f2e41480 | 537 | buf.appendf(" %s%u]", id.prefix(), id.value); |
a23223bf CT |
538 | buf.terminate(); |
539 | ||
540 | return buf.content(); | |
541 | } | |
542 | ||
212e5aee | 543 | #if USE_OPENSSL |
6cae08c9 | 544 | /// CallDialer to allow use Downloader objects within PeerConnector class. |
4cab96c5 | 545 | class PeerConnectorCertDownloaderDialer: public Downloader::CbDialer |
55369ae6 AR |
546 | { |
547 | public: | |
212e5aee | 548 | typedef void (Security::PeerConnector::*Method)(SBuf &object, int status); |
55369ae6 | 549 | |
212e5aee | 550 | PeerConnectorCertDownloaderDialer(Method method, Security::PeerConnector *pc): |
55369ae6 AR |
551 | method_(method), |
552 | peerConnector_(pc) {} | |
553 | ||
554 | /* CallDialer API */ | |
555 | virtual bool canDial(AsyncCall &call) { return peerConnector_.valid(); } | |
4cab96c5 | 556 | virtual void dial(AsyncCall &call) { ((&(*peerConnector_))->*method_)(object, status); } |
212e5aee CT |
557 | Method method_; ///< The Security::PeerConnector method to dial |
558 | CbcPointer<Security::PeerConnector> peerConnector_; ///< The Security::PeerConnector object | |
55369ae6 AR |
559 | }; |
560 | ||
561 | void | |
212e5aee | 562 | Security::PeerConnector::startCertDownloading(SBuf &url) |
55369ae6 AR |
563 | { |
564 | AsyncCall::Pointer certCallback = asyncCall(81, 4, | |
3945c91d SM |
565 | "Security::PeerConnector::certDownloadingDone", |
566 | PeerConnectorCertDownloaderDialer(&Security::PeerConnector::certDownloadingDone, this)); | |
55369ae6 | 567 | |
cda7024f CT |
568 | const Downloader *csd = dynamic_cast<const Downloader*>(request->downloader.valid()); |
569 | Downloader *dl = new Downloader(url, certCallback, csd ? csd->nestedLevel() + 1 : 1); | |
55369ae6 AR |
570 | AsyncJob::Start(dl); |
571 | } | |
572 | ||
573 | void | |
212e5aee | 574 | Security::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus) |
55369ae6 | 575 | { |
4b5ea8a6 | 576 | ++certsDownloads; |
7b4984f7 | 577 | debugs(81, 5, "Certificate downloading status: " << downloadStatus << " certificate size: " << obj.length()); |
55369ae6 | 578 | |
168d2b30 | 579 | // get ServerBio from SSL object |
55369ae6 | 580 | const int fd = serverConnection()->fd; |
54fb1cbf | 581 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); |
55369ae6 AR |
582 | BIO *b = SSL_get_rbio(ssl); |
583 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
584 | ||
25d0ea14 | 585 | // Parse Certificate. Assume that it is in DER format. |
c31381d0 CT |
586 | // According to RFC 4325: |
587 | // The server must provide a DER encoded certificate or a collection | |
588 | // collection of certificates in a "certs-only" CMS message. | |
589 | // The applications MUST accept DER encoded certificates and SHOULD | |
590 | // be able to accept collection of certificates. | |
591 | // TODO: support collection of certificates | |
55369ae6 AR |
592 | const unsigned char *raw = (const unsigned char*)obj.rawContent(); |
593 | if (X509 *cert = d2i_X509(NULL, &raw, obj.length())) { | |
594 | char buffer[1024]; | |
595 | debugs(81, 5, "Retrieved certificate: " << X509_NAME_oneline(X509_get_subject_name(cert), buffer, 1024)); | |
a34d1d2d | 596 | const Security::CertList &certsList = srvBio->serverCertificatesIfAny(); |
55369ae6 AR |
597 | if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert, certsList)) { |
598 | urlsOfMissingCerts.push(SBuf(issuerUri)); | |
599 | } | |
600 | Ssl::SSL_add_untrusted_cert(ssl, cert); | |
601 | } | |
602 | ||
4b5ea8a6 CT |
603 | // Check if there are URIs to download from and if yes start downloading |
604 | // the first in queue. | |
4e526b93 | 605 | if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) { |
55369ae6 AR |
606 | startCertDownloading(urlsOfMissingCerts.front()); |
607 | urlsOfMissingCerts.pop(); | |
608 | return; | |
609 | } | |
610 | ||
611 | srvBio->holdRead(false); | |
212e5aee | 612 | Security::PeerConnector::NegotiateSsl(serverConnection()->fd, this); |
55369ae6 AR |
613 | } |
614 | ||
615 | bool | |
212e5aee | 616 | Security::PeerConnector::checkForMissingCertificates() |
55369ae6 | 617 | { |
4e526b93 CT |
618 | // Check for nested SSL certificates downloads. For example when the |
619 | // certificate located in an SSL site which requires to download a | |
168d2b30 | 620 | // a missing certificate (... from an SSL site which requires to ...). |
cda7024f | 621 | |
4b5ea8a6 | 622 | const Downloader *csd = request->downloader.get(); |
4e526b93 CT |
623 | if (csd && csd->nestedLevel() >= MaxNestedDownloads) |
624 | return false; | |
625 | ||
55369ae6 | 626 | const int fd = serverConnection()->fd; |
54fb1cbf | 627 | Security::SessionPtr ssl = fd_table[fd].ssl.get(); |
55369ae6 AR |
628 | BIO *b = SSL_get_rbio(ssl); |
629 | Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr); | |
a34d1d2d | 630 | const Security::CertList &certs = srvBio->serverCertificatesIfAny(); |
55369ae6 | 631 | |
a34d1d2d CT |
632 | if (certs.size()) { |
633 | debugs(83, 5, "SSL server sent " << certs.size() << " certificates"); | |
55369ae6 AR |
634 | Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs); |
635 | if (urlsOfMissingCerts.size()) { | |
636 | startCertDownloading(urlsOfMissingCerts.front()); | |
637 | urlsOfMissingCerts.pop(); | |
638 | return true; | |
639 | } | |
640 | } | |
641 | ||
642 | return false; | |
643 | } | |
212e5aee | 644 | #endif //USE_OPENSSL |
3945c91d | 645 |