]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/PeekingPeerConnector.cc
Removed HandshakeParser::parseError and hid/renamed its parseDone.
[thirdparty/squid.git] / src / ssl / PeekingPeerConnector.cc
CommitLineData
32f1ca3f
AJ
1/*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 83 SSL-Bump Server/Peer negotiation */
10
11#include "squid.h"
12#include "acl/FilledChecklist.h"
13#include "client_side.h"
14#include "errorpage.h"
15#include "fde.h"
0ba8b2ad 16#include "http/Stream.h"
64d0dd9f 17#include "HttpRequest.h"
32f1ca3f
AJ
18#include "security/NegotiationHistory.h"
19#include "SquidConfig.h"
20#include "ssl/bio.h"
21#include "ssl/PeekingPeerConnector.h"
22#include "ssl/ServerBump.h"
23
24CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector);
25
26void switchToTunnel(HttpRequest *request, Comm::ConnectionPointer & clientConn, Comm::ConnectionPointer &srvConn);
27
28void
29Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone(allow_t answer, void *data)
30{
31 Ssl::PeekingPeerConnector *peerConnect = (Ssl::PeekingPeerConnector *) data;
32 // Use job calls to add done() checks and other job logic/protections.
33 CallJobHere1(83, 7, CbcPointer<PeekingPeerConnector>(peerConnect), Ssl::PeekingPeerConnector, checkForPeekAndSpliceDone, answer);
34}
35
36void
37Ssl::PeekingPeerConnector::checkForPeekAndSpliceDone(allow_t answer)
38{
39 const Ssl::BumpMode finalAction = (answer.code == ACCESS_ALLOWED) ?
40 static_cast<Ssl::BumpMode>(answer.kind):
41 checkForPeekAndSpliceGuess();
42 checkForPeekAndSpliceMatched(finalAction);
43}
44
45void
46Ssl::PeekingPeerConnector::checkForPeekAndSplice()
47{
48 // Mark Step3 of bumping
49 if (request->clientConnectionManager.valid()) {
50 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
51 serverBump->step = Ssl::bumpStep3;
52 }
53 }
54
55 handleServerCertificate();
56
57 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
58 ::Config.accessList.ssl_bump,
59 request.getRaw(), NULL);
d4ddb3e6 60 acl_checklist->al = al;
32f1ca3f
AJ
61 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpNone));
62 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpPeek));
63 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpStare));
64 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpClientFirst));
65 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpServerFirst));
66 Security::SessionPtr ssl = fd_table[serverConn->fd].ssl.get();
67 BIO *b = SSL_get_rbio(ssl);
68 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
69 if (!srvBio->canSplice())
70 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpSplice));
71 if (!srvBio->canBump())
72 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpBump));
73 acl_checklist->nonBlockingCheck(Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone, this);
74}
75
76void
77Ssl::PeekingPeerConnector::checkForPeekAndSpliceMatched(const Ssl::BumpMode action)
78{
79 Security::SessionPtr ssl = fd_table[serverConn->fd].ssl.get();
80 BIO *b = SSL_get_rbio(ssl);
81 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
82 debugs(83,5, "Will check for peek and splice on FD " << serverConn->fd);
83
84 Ssl::BumpMode finalAction = action;
85 Must(finalAction == Ssl::bumpSplice || finalAction == Ssl::bumpBump || finalAction == Ssl::bumpTerminate);
86 // Record final decision
87 if (request->clientConnectionManager.valid()) {
88 request->clientConnectionManager->sslBumpMode = finalAction;
89 request->clientConnectionManager->serverBump()->act.step3 = finalAction;
90 }
91
92 if (finalAction == Ssl::bumpTerminate) {
93 serverConn->close();
94 clientConn->close();
95 } else if (finalAction != Ssl::bumpSplice) {
96 //Allow write, proceed with the connection
97 srvBio->holdWrite(false);
98 srvBio->recordInput(false);
99 debugs(83,5, "Retry the fwdNegotiateSSL on FD " << serverConn->fd);
100 Ssl::PeerConnector::noteWantWrite();
101 } else {
102 splice = true;
103 // Ssl Negotiation stops here. Last SSL checks for valid certificates
104 // and if done, switch to tunnel mode
105 if (sslFinalized()) {
106 debugs(83,5, "Abort NegotiateSSL on FD " << serverConn->fd << " and splice the connection");
56753478 107 callBack();
32f1ca3f
AJ
108 }
109 }
110}
111
112Ssl::BumpMode
113Ssl::PeekingPeerConnector::checkForPeekAndSpliceGuess() const
114{
115 if (const ConnStateData *csd = request->clientConnectionManager.valid()) {
116 const Ssl::BumpMode currentMode = csd->sslBumpMode;
117 if (currentMode == Ssl::bumpStare) {
118 debugs(83,5, "default to bumping after staring");
119 return Ssl::bumpBump;
120 }
121 debugs(83,5, "default to splicing after " << currentMode);
122 } else {
123 debugs(83,3, "default to splicing due to missing info");
124 }
125
126 return Ssl::bumpSplice;
127}
128
129Security::ContextPtr
130Ssl::PeekingPeerConnector::getSslContext()
131{
132 // XXX: locate a per-server context in Security:: instead
133 return ::Config.ssl_client.sslContext;
134}
135
136Security::SessionPtr
137Ssl::PeekingPeerConnector::initializeSsl()
138{
139 auto ssl = Ssl::PeerConnector::initializeSsl();
140 if (!ssl)
141 return nullptr;
142
143 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
144
145 // client connection is required in the case we need to splice
146 // or terminate client and server connections
147 assert(clientConn != NULL);
148 SBuf *hostName = NULL;
32f1ca3f
AJ
149
150 //Enable Status_request tls extension, required to bump some clients
151 SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp);
152
3cae14a6
CT
153 const Security::TlsDetails::Pointer details = csd->tlsParser.details;
154 if (details != NULL && !details->serverName.isEmpty())
155 hostName = new SBuf(details->serverName);
32f1ca3f
AJ
156
157 if (!hostName) {
158 // While we are peeking at the certificate, we may not know the server
159 // name that the client will request (after interception or CONNECT)
160 // unless it was the CONNECT request with a user-typed address.
161 const bool isConnectRequest = !csd->port->flags.isIntercepted();
162 if (!request->flags.sslPeek || isConnectRequest)
163 hostName = new SBuf(request->url.host());
164 }
165
166 if (hostName)
167 SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostName);
168
169 Must(!csd->serverBump() || csd->serverBump()->step <= Ssl::bumpStep2);
170 if (csd->sslBumpMode == Ssl::bumpPeek || csd->sslBumpMode == Ssl::bumpStare) {
3cae14a6
CT
171 auto clientSsl = fd_table[clientConn->fd].ssl.get();
172 Must(clientSsl);
173 BIO *bc = SSL_get_rbio(clientSsl);
174 Ssl::ClientBio *cltBio = static_cast<Ssl::ClientBio *>(bc->ptr);
175 Must(cltBio);
176 //const Security::TlsDetails::Pointer &details = csd->tlsParser.details;
21530947
CT
177 if (details->tlsVersion != -1) {
178 applyTlsDetailsToSSL(ssl, details, csd->sslBumpMode);
32f1ca3f 179 // Should we allow it for all protocols?
21530947 180 if (details->tlsVersion >= 3) {
32f1ca3f
AJ
181 BIO *b = SSL_get_rbio(ssl);
182 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
183 // Inherite client features, like SSL version, SNI and other
21530947 184 srvBio->setClientFeatures(details, cltBio->rBufData());
32f1ca3f
AJ
185 srvBio->recordInput(true);
186 srvBio->mode(csd->sslBumpMode);
187 }
188 }
189 } else {
190 // Set client SSL options
191 SSL_set_options(ssl, ::Security::ProxyOutgoingConfig.parsedOptions);
192
193 // Use SNI TLS extension only when we connect directly
194 // to the origin server and we know the server host name.
195 const char *sniServer = NULL;
196 const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host;
197 if (!hostName || redirected)
198 sniServer = !request->url.hostIsNumeric() ? request->url.host() : NULL;
199 else
200 sniServer = hostName->c_str();
201
202 if (sniServer)
203 Ssl::setClientSNI(ssl, sniServer);
204 }
205
088f0761
CT
206 if (Ssl::ServerBump *serverBump = csd->serverBump()) {
207 serverBump->attachServerSSL(ssl);
208 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
209 if (X509 *peeked_cert = serverBump->serverCert.get()) {
210 CRYPTO_add(&(peeked_cert->references),1,CRYPTO_LOCK_X509);
211 SSL_set_ex_data(ssl, ssl_ex_index_ssl_peeked_cert, peeked_cert);
212 }
32f1ca3f
AJ
213 }
214 }
215
216 return ssl;
217}
218
219void
220Ssl::PeekingPeerConnector::noteNegotiationDone(ErrorState *error)
221{
222 Security::SessionPtr ssl = fd_table[serverConnection()->fd].ssl.get();
223
224 // Check the list error with
225 if (!request->clientConnectionManager.valid() || ! ssl)
226 return;
227
228 // remember the server certificate from the ErrorDetail object
229 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
32f1ca3f
AJ
230 if (!serverBump->serverCert.get()) {
231 // remember the server certificate from the ErrorDetail object
232 if (error && error->detail && error->detail->peerCert())
233 serverBump->serverCert.resetAndLock(error->detail->peerCert());
234 else {
235 handleServerCertificate();
236 }
237 }
238
239 if (error) {
240 // For intercepted connections, set the host name to the server
241 // certificate CN. Otherwise, we just hope that CONNECT is using
242 // a user-entered address (a host name or a user-entered IP).
243 const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
244 if (request->flags.sslPeek && !isConnectRequest) {
245 if (X509 *srvX509 = serverBump->serverCert.get()) {
246 if (const char *name = Ssl::CommonHostName(srvX509)) {
247 request->url.host(name);
248 debugs(83, 3, "reset request host: " << name);
249 }
250 }
251 }
252 }
253 }
254
32f1ca3f
AJ
255 if (!error) {
256 serverCertificateVerified();
257 if (splice) {
32f1ca3f 258 switchToTunnel(request.getRaw(), clientConn, serverConn);
56753478 259 tunnelInsteadOfNegotiating();
32f1ca3f
AJ
260 }
261 }
262}
263
264void
265Ssl::PeekingPeerConnector::noteWantWrite()
266{
267 const int fd = serverConnection()->fd;
268 Security::SessionPtr ssl = fd_table[fd].ssl.get();
269 BIO *b = SSL_get_rbio(ssl);
270 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
271
272 if ((srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
32f1ca3f
AJ
273 checkForPeekAndSplice();
274 return;
275 }
276
277 Ssl::PeerConnector::noteWantWrite();
278}
279
280void
281Ssl::PeekingPeerConnector::noteSslNegotiationError(const int result, const int ssl_error, const int ssl_lib_error)
282{
283 const int fd = serverConnection()->fd;
284 Security::SessionPtr ssl = fd_table[fd].ssl.get();
285 BIO *b = SSL_get_rbio(ssl);
286 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
287
288 // In Peek mode, the ClientHello message sent to the server. If the
289 // server resuming a previous (spliced) SSL session with the client,
290 // then probably we are here because local SSL object does not know
291 // anything about the session being resumed.
292 //
293 if (srvBio->bumpMode() == Ssl::bumpPeek && (resumingSession = srvBio->resumingSession())) {
294 // we currently splice all resumed sessions unconditionally
295 if (const bool spliceResumed = true) {
296 bypassCertValidator();
297 checkForPeekAndSpliceMatched(Ssl::bumpSplice);
298 return;
299 } // else fall through to find a matching ssl_bump action (with limited info)
300 }
301
302 // If we are in peek-and-splice mode and still we did not write to
303 // server yet, try to see if we should splice.
304 // In this case the connection can be saved.
305 // If the checklist decision is do not splice a new error will
306 // occur in the next SSL_connect call, and we will fail again.
307 // Abort on certificate validation errors to avoid splicing and
308 // thus hiding them.
309 // Abort if no certificate found probably because of malformed or
310 // unsupported server Hello message (TODO: make configurable).
311 if (!SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) &&
312 (srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
313 Security::CertPointer serverCert(SSL_get_peer_certificate(ssl));
314 if (serverCert.get()) {
315 debugs(81, 3, "Error (" << ERR_error_string(ssl_lib_error, NULL) << ") but, hold write on SSL connection on FD " << fd);
316 checkForPeekAndSplice();
317 return;
318 }
319 }
320
321 // else call parent noteNegotiationError to produce an error page
322 Ssl::PeerConnector::noteSslNegotiationError(result, ssl_error, ssl_lib_error);
323}
324
325void
326Ssl::PeekingPeerConnector::handleServerCertificate()
327{
328 if (serverCertificateHandled)
329 return;
330
331 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
332 const int fd = serverConnection()->fd;
333 Security::SessionPtr ssl = fd_table[fd].ssl.get();
334 Security::CertPointer serverCert(SSL_get_peer_certificate(ssl));
335 if (!serverCert.get())
336 return;
337
338 serverCertificateHandled = true;
339
340 // remember the server certificate for later use
341 if (Ssl::ServerBump *serverBump = csd->serverBump()) {
342 serverBump->serverCert.reset(serverCert.release());
343 }
344 }
345}
346
347void
348Ssl::PeekingPeerConnector::serverCertificateVerified()
349{
350 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
351 Security::CertPointer serverCert;
352 if(Ssl::ServerBump *serverBump = csd->serverBump())
353 serverCert.resetAndLock(serverBump->serverCert.get());
354 else {
355 const int fd = serverConnection()->fd;
356 Security::SessionPtr ssl = fd_table[fd].ssl.get();
357 serverCert.reset(SSL_get_peer_certificate(ssl));
358 }
359 if (serverCert.get()) {
360 csd->resetSslCommonName(Ssl::CommonHostName(serverCert.get()));
361 debugs(83, 5, "HTTPS server CN: " << csd->sslCommonName() <<
362 " bumped: " << *serverConnection());
363 }
364 }
365}
366
56753478
CT
367void
368Ssl::PeekingPeerConnector::tunnelInsteadOfNegotiating()
369{
370 Must(callback != NULL);
371 CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
372 Must(dialer);
373 dialer->answer().tunneled = true;
374 debugs(83, 5, "The SSL negotiation with server aborted");
375}
376