]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/PeekingPeerConnector.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / ssl / PeekingPeerConnector.cc
CommitLineData
32f1ca3f 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
32f1ca3f
AJ
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"
83b053a0 18#include "security/ErrorDetail.h"
32f1ca3f
AJ
19#include "security/NegotiationHistory.h"
20#include "SquidConfig.h"
21#include "ssl/bio.h"
22#include "ssl/PeekingPeerConnector.h"
23#include "ssl/ServerBump.h"
24
25CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector);
26
1c2b4465 27void switchToTunnel(HttpRequest *request, const Comm::ConnectionPointer &clientConn, const Comm::ConnectionPointer &srvConn, const SBuf &preReadServerData);
32f1ca3f
AJ
28
29void
329c128c 30Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone(Acl::Answer answer, void *data)
32f1ca3f
AJ
31{
32 Ssl::PeekingPeerConnector *peerConnect = (Ssl::PeekingPeerConnector *) data;
33 // Use job calls to add done() checks and other job logic/protections.
34 CallJobHere1(83, 7, CbcPointer<PeekingPeerConnector>(peerConnect), Ssl::PeekingPeerConnector, checkForPeekAndSpliceDone, answer);
35}
36
37void
329c128c 38Ssl::PeekingPeerConnector::checkForPeekAndSpliceDone(Acl::Answer answer)
32f1ca3f 39{
06bf5384 40 const Ssl::BumpMode finalAction = answer.allowed() ?
32f1ca3f
AJ
41 static_cast<Ssl::BumpMode>(answer.kind):
42 checkForPeekAndSpliceGuess();
43 checkForPeekAndSpliceMatched(finalAction);
44}
45
46void
47Ssl::PeekingPeerConnector::checkForPeekAndSplice()
48{
49 // Mark Step3 of bumping
50 if (request->clientConnectionManager.valid()) {
51 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
090f1d3c 52 serverBump->step = XactionStep::tlsBump3;
32f1ca3f
AJ
53 }
54 }
55
56 handleServerCertificate();
57
58 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
59 ::Config.accessList.ssl_bump,
60 request.getRaw(), NULL);
d4ddb3e6 61 acl_checklist->al = al;
329c128c 62 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpNone));
63 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpPeek));
64 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpStare));
65 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpClientFirst));
66 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpServerFirst));
ad23e748
AJ
67 Security::SessionPointer session(fd_table[serverConn->fd].ssl);
68 BIO *b = SSL_get_rbio(session.get());
093deea9 69 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
32f1ca3f 70 if (!srvBio->canSplice())
329c128c 71 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpSplice));
32f1ca3f 72 if (!srvBio->canBump())
329c128c 73 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpBump));
cb365059 74 acl_checklist->syncAle(request.getRaw(), nullptr);
32f1ca3f
AJ
75 acl_checklist->nonBlockingCheck(Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone, this);
76}
77
78void
79Ssl::PeekingPeerConnector::checkForPeekAndSpliceMatched(const Ssl::BumpMode action)
80{
ad23e748
AJ
81 Security::SessionPointer session(fd_table[serverConn->fd].ssl);
82 BIO *b = SSL_get_rbio(session.get());
093deea9 83 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
32f1ca3f
AJ
84 debugs(83,5, "Will check for peek and splice on FD " << serverConn->fd);
85
86 Ssl::BumpMode finalAction = action;
87 Must(finalAction == Ssl::bumpSplice || finalAction == Ssl::bumpBump || finalAction == Ssl::bumpTerminate);
88 // Record final decision
89 if (request->clientConnectionManager.valid()) {
90 request->clientConnectionManager->sslBumpMode = finalAction;
91 request->clientConnectionManager->serverBump()->act.step3 = finalAction;
92 }
bf352fb2 93 al->ssl.bumpMode = finalAction;
32f1ca3f
AJ
94
95 if (finalAction == Ssl::bumpTerminate) {
25b0ce45 96 bail(new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scForbidden, request.getRaw(), al));
32f1ca3f 97 clientConn->close();
25b0ce45 98 clientConn = nullptr;
32f1ca3f
AJ
99 } else if (finalAction != Ssl::bumpSplice) {
100 //Allow write, proceed with the connection
101 srvBio->holdWrite(false);
102 srvBio->recordInput(false);
103 debugs(83,5, "Retry the fwdNegotiateSSL on FD " << serverConn->fd);
a72b6e88 104 Security::PeerConnector::noteWantWrite();
32f1ca3f
AJ
105 } else {
106 splice = true;
107 // Ssl Negotiation stops here. Last SSL checks for valid certificates
108 // and if done, switch to tunnel mode
109 if (sslFinalized()) {
110 debugs(83,5, "Abort NegotiateSSL on FD " << serverConn->fd << " and splice the connection");
56753478 111 callBack();
32f1ca3f
AJ
112 }
113 }
114}
115
116Ssl::BumpMode
117Ssl::PeekingPeerConnector::checkForPeekAndSpliceGuess() const
118{
119 if (const ConnStateData *csd = request->clientConnectionManager.valid()) {
120 const Ssl::BumpMode currentMode = csd->sslBumpMode;
121 if (currentMode == Ssl::bumpStare) {
122 debugs(83,5, "default to bumping after staring");
123 return Ssl::bumpBump;
124 }
125 debugs(83,5, "default to splicing after " << currentMode);
126 } else {
127 debugs(83,3, "default to splicing due to missing info");
128 }
129
130 return Ssl::bumpSplice;
131}
132
b23f5f9c
AJ
133Security::ContextPointer
134Ssl::PeekingPeerConnector::getTlsContext()
32f1ca3f 135{
b23f5f9c 136 return ::Config.ssl_client.sslContext;
32f1ca3f
AJ
137}
138
eba8d9bb 139bool
0166128b 140Ssl::PeekingPeerConnector::initialize(Security::SessionPointer &serverSession)
32f1ca3f 141{
0166128b 142 if (!Security::PeerConnector::initialize(serverSession))
eba8d9bb 143 return false;
32f1ca3f 144
25b0ce45
CT
145 // client connection supplies TLS client details and is also used if we
146 // need to splice or terminate the client and server connections
147 if (!Comm::IsConnOpen(clientConn))
148 return false;
149
32f1ca3f
AJ
150 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
151
32f1ca3f 152 SBuf *hostName = NULL;
32f1ca3f 153
eba8d9bb
AJ
154 //Enable Status_request TLS extension, required to bump some clients
155 SSL_set_tlsext_status_type(serverSession.get(), TLSEXT_STATUSTYPE_ocsp);
32f1ca3f 156
3cae14a6 157 const Security::TlsDetails::Pointer details = csd->tlsParser.details;
49a4d72f 158 if (details && !details->serverName.isEmpty())
3cae14a6 159 hostName = new SBuf(details->serverName);
32f1ca3f
AJ
160
161 if (!hostName) {
162 // While we are peeking at the certificate, we may not know the server
163 // name that the client will request (after interception or CONNECT)
164 // unless it was the CONNECT request with a user-typed address.
165 const bool isConnectRequest = !csd->port->flags.isIntercepted();
166 if (!request->flags.sslPeek || isConnectRequest)
167 hostName = new SBuf(request->url.host());
168 }
169
170 if (hostName)
eba8d9bb 171 SSL_set_ex_data(serverSession.get(), ssl_ex_index_server, (void*)hostName);
32f1ca3f 172
090f1d3c 173 Must(!csd->serverBump() || csd->serverBump()->at(XactionStep::tlsBump1, XactionStep::tlsBump2));
32f1ca3f 174 if (csd->sslBumpMode == Ssl::bumpPeek || csd->sslBumpMode == Ssl::bumpStare) {
eba8d9bb
AJ
175 auto clientSession = fd_table[clientConn->fd].ssl.get();
176 Must(clientSession);
177 BIO *bc = SSL_get_rbio(clientSession);
093deea9 178 Ssl::ClientBio *cltBio = static_cast<Ssl::ClientBio *>(BIO_get_data(bc));
3cae14a6 179 Must(cltBio);
6744c1a8 180 if (details && details->tlsVersion.protocol != AnyP::PROTO_NONE)
eba8d9bb 181 applyTlsDetailsToSSL(serverSession.get(), details, csd->sslBumpMode);
6744c1a8
CT
182
183 BIO *b = SSL_get_rbio(serverSession.get());
184 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
185 Must(srvBio);
186 // inherit client features such as TLS version and SNI
187 srvBio->setClientFeatures(details, cltBio->rBufData());
188 srvBio->recordInput(true);
189 srvBio->mode(csd->sslBumpMode);
32f1ca3f
AJ
190 } else {
191 // Set client SSL options
b491f761 192 ::Security::ProxyOutgoingConfig.updateSessionOptions(serverSession);
32f1ca3f 193
32f1ca3f 194 const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host;
428819f3
RB
195 const char *sniServer = (!hostName || redirected) ?
196 request->url.host() :
197 hostName->c_str();
32f1ca3f 198 if (sniServer)
428819f3 199 setClientSNI(serverSession.get(), sniServer);
32f1ca3f
AJ
200 }
201
088f0761 202 if (Ssl::ServerBump *serverBump = csd->serverBump()) {
8f917129 203 serverBump->attachServerSession(serverSession);
088f0761
CT
204 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
205 if (X509 *peeked_cert = serverBump->serverCert.get()) {
6f2b8700 206 X509_up_ref(peeked_cert);
eba8d9bb 207 SSL_set_ex_data(serverSession.get(), ssl_ex_index_ssl_peeked_cert, peeked_cert);
088f0761 208 }
32f1ca3f
AJ
209 }
210 }
211
eba8d9bb 212 return true;
32f1ca3f
AJ
213}
214
215void
216Ssl::PeekingPeerConnector::noteNegotiationDone(ErrorState *error)
217{
32f1ca3f 218 // Check the list error with
ad23e748 219 if (!request->clientConnectionManager.valid() || !fd_table[serverConnection()->fd].ssl)
32f1ca3f
AJ
220 return;
221
222 // remember the server certificate from the ErrorDetail object
223 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
32f1ca3f
AJ
224 if (!serverBump->serverCert.get()) {
225 // remember the server certificate from the ErrorDetail object
83b053a0
CT
226 const auto errDetail = dynamic_cast<Security::ErrorDetail *>(error ? error->detail.getRaw() : nullptr);
227 if (errDetail && errDetail->peerCert())
228 serverBump->serverCert.resetAndLock(errDetail->peerCert());
32f1ca3f
AJ
229 else {
230 handleServerCertificate();
231 }
232 }
233
234 if (error) {
235 // For intercepted connections, set the host name to the server
236 // certificate CN. Otherwise, we just hope that CONNECT is using
237 // a user-entered address (a host name or a user-entered IP).
238 const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
239 if (request->flags.sslPeek && !isConnectRequest) {
240 if (X509 *srvX509 = serverBump->serverCert.get()) {
241 if (const char *name = Ssl::CommonHostName(srvX509)) {
242 request->url.host(name);
243 debugs(83, 3, "reset request host: " << name);
244 }
245 }
246 }
247 }
248 }
249
32f1ca3f
AJ
250 if (!error) {
251 serverCertificateVerified();
252 if (splice) {
25b0ce45
CT
253 if (!Comm::IsConnOpen(clientConn)) {
254 bail(new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw(), al));
255 throw TextException("from-client connection gone", Here());
256 }
1c2b4465 257 startTunneling();
32f1ca3f
AJ
258 }
259 }
260}
261
1c2b4465
CT
262void
263Ssl::PeekingPeerConnector::startTunneling()
264{
265 // switchToTunnel() drains any already buffered from-server data (rBufData)
266 fd_table[serverConn->fd].useDefaultIo();
267 // tunnelStartShoveling() drains any buffered from-client data (inBuf)
268 fd_table[clientConn->fd].useDefaultIo();
269
270 // TODO: Encapsulate this frequently repeated logic into a method.
271 const auto session = fd_table[serverConn->fd].ssl;
272 auto b = SSL_get_rbio(session.get());
273 auto srvBio = static_cast<Ssl::ServerBio*>(BIO_get_data(b));
274
275 switchToTunnel(request.getRaw(), clientConn, serverConn, srvBio->rBufData());
276 tunnelInsteadOfNegotiating();
277}
278
32f1ca3f
AJ
279void
280Ssl::PeekingPeerConnector::noteWantWrite()
281{
282 const int fd = serverConnection()->fd;
ad23e748
AJ
283 Security::SessionPointer session(fd_table[fd].ssl);
284 BIO *b = SSL_get_rbio(session.get());
093deea9 285 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
32f1ca3f
AJ
286
287 if ((srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
bc8e1f18 288 debugs(81, 3, "hold write on SSL connection on FD " << fd);
32f1ca3f
AJ
289 checkForPeekAndSplice();
290 return;
291 }
292
a72b6e88 293 Security::PeerConnector::noteWantWrite();
32f1ca3f
AJ
294}
295
296void
83b053a0 297Ssl::PeekingPeerConnector::noteNegotiationError(const Security::ErrorDetailPointer &errorDetail)
32f1ca3f
AJ
298{
299 const int fd = serverConnection()->fd;
ad23e748
AJ
300 Security::SessionPointer session(fd_table[fd].ssl);
301 BIO *b = SSL_get_rbio(session.get());
093deea9 302 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
32f1ca3f 303
cd29a421
CT
304 if (srvBio->bumpMode() == Ssl::bumpPeek) {
305 auto bypassValidator = false;
306 if (srvBio->encryptedCertificates()) {
307 // it is pointless to peek at encrypted certificates
308 //
309 // we currently splice all sessions with encrypted certificates
310 // if (const auto spliceEncryptedCertificates = true) {
311 bypassValidator = true;
312 // } // else fall through to find a matching ssl_bump action (with limited info)
313 } else if (srvBio->resumingSession()) {
314 // In peek mode, the ClientHello message is forwarded to the server.
315 // If the server is resuming a previous (spliced) SSL session with
316 // the client, then probably we are here because our local SSL
317 // object does not know anything about the session being resumed.
318 //
319 // we currently splice all resumed sessions
320 // if (const auto spliceResumed = true) {
321 bypassValidator = true;
322 // } // else fall through to find a matching ssl_bump action (with limited info)
323 }
324
325 if (bypassValidator) {
326 bypassCertValidator();
327 checkForPeekAndSpliceMatched(Ssl::bumpSplice);
328 return;
329 }
32f1ca3f
AJ
330 }
331
332 // If we are in peek-and-splice mode and still we did not write to
333 // server yet, try to see if we should splice.
334 // In this case the connection can be saved.
335 // If the checklist decision is do not splice a new error will
336 // occur in the next SSL_connect call, and we will fail again.
337 // Abort on certificate validation errors to avoid splicing and
338 // thus hiding them.
339 // Abort if no certificate found probably because of malformed or
340 // unsupported server Hello message (TODO: make configurable).
d2f0c106
CT
341 // TODO: Add/use a positive "successfully validated server cert" signal
342 // instead of relying on the "![presumably_]validation_error && serverCert"
343 // signal combo.
ad23e748 344 if (!SSL_get_ex_data(session.get(), ssl_ex_index_ssl_error_detail) &&
32f1ca3f 345 (srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
ad23e748
AJ
346 Security::CertPointer serverCert(SSL_get_peer_certificate(session.get()));
347 if (serverCert) {
83b053a0 348 debugs(81, 3, "hold TLS write on FD " << fd << " despite " << errorDetail);
32f1ca3f
AJ
349 checkForPeekAndSplice();
350 return;
351 }
352 }
353
354 // else call parent noteNegotiationError to produce an error page
83b053a0 355 Security::PeerConnector::noteNegotiationError(errorDetail);
32f1ca3f
AJ
356}
357
358void
359Ssl::PeekingPeerConnector::handleServerCertificate()
360{
361 if (serverCertificateHandled)
362 return;
363
364 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
365 const int fd = serverConnection()->fd;
ad23e748
AJ
366 Security::SessionPointer session(fd_table[fd].ssl);
367 Security::CertPointer serverCert(SSL_get_peer_certificate(session.get()));
368 if (!serverCert)
32f1ca3f
AJ
369 return;
370
371 serverCertificateHandled = true;
372
373 // remember the server certificate for later use
374 if (Ssl::ServerBump *serverBump = csd->serverBump()) {
35b3559c 375 serverBump->serverCert = std::move(serverCert);
32f1ca3f
AJ
376 }
377 }
378}
379
380void
381Ssl::PeekingPeerConnector::serverCertificateVerified()
382{
383 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
384 Security::CertPointer serverCert;
385 if(Ssl::ServerBump *serverBump = csd->serverBump())
014a9017 386 serverCert.resetAndLock(serverBump->serverCert.get());
32f1ca3f
AJ
387 else {
388 const int fd = serverConnection()->fd;
ad23e748
AJ
389 Security::SessionPointer session(fd_table[fd].ssl);
390 serverCert.resetWithoutLocking(SSL_get_peer_certificate(session.get()));
32f1ca3f 391 }
ad23e748 392 if (serverCert) {
32f1ca3f
AJ
393 csd->resetSslCommonName(Ssl::CommonHostName(serverCert.get()));
394 debugs(83, 5, "HTTPS server CN: " << csd->sslCommonName() <<
395 " bumped: " << *serverConnection());
396 }
397 }
398}
399
56753478
CT
400void
401Ssl::PeekingPeerConnector::tunnelInsteadOfNegotiating()
402{
403 Must(callback != NULL);
404 CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
405 Must(dialer);
406 dialer->answer().tunneled = true;
407 debugs(83, 5, "The SSL negotiation with server aborted");
408}
409