]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/PeekingPeerConnector.cc
Rename allow_t to Acl::Answer (#425)
[thirdparty/squid.git] / src / ssl / PeekingPeerConnector.cc
1 /*
2 * Copyright (C) 1996-2019 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 "http/Stream.h"
17 #include "HttpRequest.h"
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
24 CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector);
25
26 void switchToTunnel(HttpRequest *request, Comm::ConnectionPointer & clientConn, Comm::ConnectionPointer &srvConn);
27
28 void
29 Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone(Acl::Answer 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
36 void
37 Ssl::PeekingPeerConnector::checkForPeekAndSpliceDone(Acl::Answer answer)
38 {
39 const Ssl::BumpMode finalAction = answer.allowed() ?
40 static_cast<Ssl::BumpMode>(answer.kind):
41 checkForPeekAndSpliceGuess();
42 checkForPeekAndSpliceMatched(finalAction);
43 }
44
45 void
46 Ssl::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);
60 acl_checklist->al = al;
61 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpNone));
62 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpPeek));
63 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpStare));
64 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpClientFirst));
65 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpServerFirst));
66 Security::SessionPointer session(fd_table[serverConn->fd].ssl);
67 BIO *b = SSL_get_rbio(session.get());
68 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
69 if (!srvBio->canSplice())
70 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpSplice));
71 if (!srvBio->canBump())
72 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpBump));
73 acl_checklist->syncAle(request.getRaw(), nullptr);
74 acl_checklist->nonBlockingCheck(Ssl::PeekingPeerConnector::cbCheckForPeekAndSpliceDone, this);
75 }
76
77 void
78 Ssl::PeekingPeerConnector::checkForPeekAndSpliceMatched(const Ssl::BumpMode action)
79 {
80 Security::SessionPointer session(fd_table[serverConn->fd].ssl);
81 BIO *b = SSL_get_rbio(session.get());
82 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
83 debugs(83,5, "Will check for peek and splice on FD " << serverConn->fd);
84
85 Ssl::BumpMode finalAction = action;
86 Must(finalAction == Ssl::bumpSplice || finalAction == Ssl::bumpBump || finalAction == Ssl::bumpTerminate);
87 // Record final decision
88 if (request->clientConnectionManager.valid()) {
89 request->clientConnectionManager->sslBumpMode = finalAction;
90 request->clientConnectionManager->serverBump()->act.step3 = finalAction;
91 }
92 al->ssl.bumpMode = finalAction;
93
94 if (finalAction == Ssl::bumpTerminate) {
95 serverConn->close();
96 clientConn->close();
97 } else if (finalAction != Ssl::bumpSplice) {
98 //Allow write, proceed with the connection
99 srvBio->holdWrite(false);
100 srvBio->recordInput(false);
101 debugs(83,5, "Retry the fwdNegotiateSSL on FD " << serverConn->fd);
102 Security::PeerConnector::noteWantWrite();
103 } else {
104 splice = true;
105 // Ssl Negotiation stops here. Last SSL checks for valid certificates
106 // and if done, switch to tunnel mode
107 if (sslFinalized()) {
108 debugs(83,5, "Abort NegotiateSSL on FD " << serverConn->fd << " and splice the connection");
109 callBack();
110 }
111 }
112 }
113
114 Ssl::BumpMode
115 Ssl::PeekingPeerConnector::checkForPeekAndSpliceGuess() const
116 {
117 if (const ConnStateData *csd = request->clientConnectionManager.valid()) {
118 const Ssl::BumpMode currentMode = csd->sslBumpMode;
119 if (currentMode == Ssl::bumpStare) {
120 debugs(83,5, "default to bumping after staring");
121 return Ssl::bumpBump;
122 }
123 debugs(83,5, "default to splicing after " << currentMode);
124 } else {
125 debugs(83,3, "default to splicing due to missing info");
126 }
127
128 return Ssl::bumpSplice;
129 }
130
131 Security::ContextPointer
132 Ssl::PeekingPeerConnector::getTlsContext()
133 {
134 return ::Config.ssl_client.sslContext;
135 }
136
137 bool
138 Ssl::PeekingPeerConnector::initialize(Security::SessionPointer &serverSession)
139 {
140 if (!Security::PeerConnector::initialize(serverSession))
141 return false;
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;
149
150 //Enable Status_request TLS extension, required to bump some clients
151 SSL_set_tlsext_status_type(serverSession.get(), TLSEXT_STATUSTYPE_ocsp);
152
153 const Security::TlsDetails::Pointer details = csd->tlsParser.details;
154 if (details && !details->serverName.isEmpty())
155 hostName = new SBuf(details->serverName);
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(serverSession.get(), 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) {
171 auto clientSession = fd_table[clientConn->fd].ssl.get();
172 Must(clientSession);
173 BIO *bc = SSL_get_rbio(clientSession);
174 Ssl::ClientBio *cltBio = static_cast<Ssl::ClientBio *>(BIO_get_data(bc));
175 Must(cltBio);
176 if (details && details->tlsVersion.protocol != AnyP::PROTO_NONE)
177 applyTlsDetailsToSSL(serverSession.get(), details, csd->sslBumpMode);
178
179 BIO *b = SSL_get_rbio(serverSession.get());
180 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
181 Must(srvBio);
182 // inherit client features such as TLS version and SNI
183 srvBio->setClientFeatures(details, cltBio->rBufData());
184 srvBio->recordInput(true);
185 srvBio->mode(csd->sslBumpMode);
186 } else {
187 // Set client SSL options
188 ::Security::ProxyOutgoingConfig.updateSessionOptions(serverSession);
189
190 const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host;
191 const char *sniServer = (!hostName || redirected) ?
192 request->url.host() :
193 hostName->c_str();
194 if (sniServer)
195 setClientSNI(serverSession.get(), sniServer);
196 }
197
198 if (Ssl::ServerBump *serverBump = csd->serverBump()) {
199 serverBump->attachServerSession(serverSession);
200 // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
201 if (X509 *peeked_cert = serverBump->serverCert.get()) {
202 X509_up_ref(peeked_cert);
203 SSL_set_ex_data(serverSession.get(), ssl_ex_index_ssl_peeked_cert, peeked_cert);
204 }
205 }
206 }
207
208 return true;
209 }
210
211 void
212 Ssl::PeekingPeerConnector::noteNegotiationDone(ErrorState *error)
213 {
214 // Check the list error with
215 if (!request->clientConnectionManager.valid() || !fd_table[serverConnection()->fd].ssl)
216 return;
217
218 // remember the server certificate from the ErrorDetail object
219 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
220 if (!serverBump->serverCert.get()) {
221 // remember the server certificate from the ErrorDetail object
222 if (error && error->detail && error->detail->peerCert())
223 serverBump->serverCert.resetAndLock(error->detail->peerCert());
224 else {
225 handleServerCertificate();
226 }
227 }
228
229 if (error) {
230 // For intercepted connections, set the host name to the server
231 // certificate CN. Otherwise, we just hope that CONNECT is using
232 // a user-entered address (a host name or a user-entered IP).
233 const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
234 if (request->flags.sslPeek && !isConnectRequest) {
235 if (X509 *srvX509 = serverBump->serverCert.get()) {
236 if (const char *name = Ssl::CommonHostName(srvX509)) {
237 request->url.host(name);
238 debugs(83, 3, "reset request host: " << name);
239 }
240 }
241 }
242 }
243 }
244
245 if (!error) {
246 serverCertificateVerified();
247 if (splice) {
248 switchToTunnel(request.getRaw(), clientConn, serverConn);
249 tunnelInsteadOfNegotiating();
250 }
251 }
252 }
253
254 void
255 Ssl::PeekingPeerConnector::noteWantWrite()
256 {
257 const int fd = serverConnection()->fd;
258 Security::SessionPointer session(fd_table[fd].ssl);
259 BIO *b = SSL_get_rbio(session.get());
260 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
261
262 if ((srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
263 debugs(81, 3, "hold write on SSL connection on FD " << fd);
264 checkForPeekAndSplice();
265 return;
266 }
267
268 Security::PeerConnector::noteWantWrite();
269 }
270
271 void
272 Ssl::PeekingPeerConnector::noteNegotiationError(const int result, const int ssl_error, const int ssl_lib_error)
273 {
274 const int fd = serverConnection()->fd;
275 Security::SessionPointer session(fd_table[fd].ssl);
276 BIO *b = SSL_get_rbio(session.get());
277 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
278
279 // In Peek mode, the ClientHello message sent to the server. If the
280 // server resuming a previous (spliced) SSL session with the client,
281 // then probably we are here because local SSL object does not know
282 // anything about the session being resumed.
283 //
284 if (srvBio->bumpMode() == Ssl::bumpPeek && (resumingSession = srvBio->resumingSession())) {
285 // we currently splice all resumed sessions unconditionally
286 // if (const bool spliceResumed = true) {
287 bypassCertValidator();
288 checkForPeekAndSpliceMatched(Ssl::bumpSplice);
289 return;
290 // } // else fall through to find a matching ssl_bump action (with limited info)
291 }
292
293 // If we are in peek-and-splice mode and still we did not write to
294 // server yet, try to see if we should splice.
295 // In this case the connection can be saved.
296 // If the checklist decision is do not splice a new error will
297 // occur in the next SSL_connect call, and we will fail again.
298 // Abort on certificate validation errors to avoid splicing and
299 // thus hiding them.
300 // Abort if no certificate found probably because of malformed or
301 // unsupported server Hello message (TODO: make configurable).
302 if (!SSL_get_ex_data(session.get(), ssl_ex_index_ssl_error_detail) &&
303 (srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
304 Security::CertPointer serverCert(SSL_get_peer_certificate(session.get()));
305 if (serverCert) {
306 debugs(81, 3, "Error (" << Security::ErrorString(ssl_lib_error) << ") but, hold write on SSL connection on FD " << fd);
307 checkForPeekAndSplice();
308 return;
309 }
310 }
311
312 // else call parent noteNegotiationError to produce an error page
313 Security::PeerConnector::noteNegotiationError(result, ssl_error, ssl_lib_error);
314 }
315
316 void
317 Ssl::PeekingPeerConnector::handleServerCertificate()
318 {
319 if (serverCertificateHandled)
320 return;
321
322 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
323 const int fd = serverConnection()->fd;
324 Security::SessionPointer session(fd_table[fd].ssl);
325 Security::CertPointer serverCert(SSL_get_peer_certificate(session.get()));
326 if (!serverCert)
327 return;
328
329 serverCertificateHandled = true;
330
331 // remember the server certificate for later use
332 if (Ssl::ServerBump *serverBump = csd->serverBump()) {
333 serverBump->serverCert = std::move(serverCert);
334 }
335 }
336 }
337
338 void
339 Ssl::PeekingPeerConnector::serverCertificateVerified()
340 {
341 if (ConnStateData *csd = request->clientConnectionManager.valid()) {
342 Security::CertPointer serverCert;
343 if(Ssl::ServerBump *serverBump = csd->serverBump())
344 serverCert.resetAndLock(serverBump->serverCert.get());
345 else {
346 const int fd = serverConnection()->fd;
347 Security::SessionPointer session(fd_table[fd].ssl);
348 serverCert.resetWithoutLocking(SSL_get_peer_certificate(session.get()));
349 }
350 if (serverCert) {
351 csd->resetSslCommonName(Ssl::CommonHostName(serverCert.get()));
352 debugs(83, 5, "HTTPS server CN: " << csd->sslCommonName() <<
353 " bumped: " << *serverConnection());
354 }
355 }
356 }
357
358 void
359 Ssl::PeekingPeerConnector::tunnelInsteadOfNegotiating()
360 {
361 Must(callback != NULL);
362 CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
363 Must(dialer);
364 dialer->answer().tunneled = true;
365 debugs(83, 5, "The SSL negotiation with server aborted");
366 }
367