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