PeekingPeerConnector.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2018 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"
19 #include "SquidConfig.h"
20 #include "ssl/bio.h"
22 #include "ssl/ServerBump.h"
23 
24 CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector);
25 
27 
28 void
30 {
32  // Use job calls to add done() checks and other job logic/protections.
34 }
35 
36 void
38 {
39  const Ssl::BumpMode finalAction = answer.allowed() ?
40  static_cast<Ssl::BumpMode>(answer.kind):
41  checkForPeekAndSpliceGuess();
42  checkForPeekAndSpliceMatched(finalAction);
43 }
44 
45 void
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;
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())
71  if (!srvBio->canBump())
73  acl_checklist->syncAle(request.getRaw(), nullptr);
75 }
76 
77 void
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);
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 
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 
133 {
135 }
136 
137 bool
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  SSL_set_options(serverSession.get(), ::Security::ProxyOutgoingConfig.parsedOptions);
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
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
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 
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
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
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
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 
int ssl_ex_index_ssl_peeked_cert
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:238
void tunnelInsteadOfNegotiating()
Inform caller class that the SSL negotiation aborted.
#define fd_table
Definition: fde.h:157
#define assert(EX)
Definition: assert.h:17
void const char HLPCB * callback
Definition: stub_helper.cc:16
static void cbCheckForPeekAndSpliceDone(allow_t answer, void *data)
A wrapper function for checkForPeekAndSpliceDone for use with acl.
int ssl_ex_index_server
const SBuf & rBufData()
The buffered input data.
Definition: bio.h:60
virtual void noteNegotiationDone(ErrorState *error)
Definition: Acl.h:113
Definition: SBuf.h:86
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void error(char *format,...)
void banAction(const allow_t &action)
add action to the list of banned actions
Definition: Checklist.cc:402
Security::ParsedOptions parsedOptions
parsed value of sslOptions
Definition: PeerOptions.h:82
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
Security::ContextPointer sslContext
Definition: SquidConfig.h:514
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
void switchToTunnel(HttpRequest *request, Comm::ConnectionPointer &clientConn, Comm::ConnectionPointer &srvConn)
Definition: tunnel.cc:1334
X509 * peerCert()
the peer certificate
Definition: ErrorDetail.h:57
A PeerConnector for HTTP origin servers. Capable of SslBumping.
struct SquidConfig::@123 ssl_client
virtual void noteWantWrite()
virtual Security::ContextPointer getTlsContext()
void const char HLPCB void * data
Definition: stub_helper.cc:16
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
int ssl_ex_index_ssl_error_detail
const char * CommonHostName(X509 *x509)
Definition: gadgets.cc:913
virtual bool initialize(Security::SessionPointer &)
PeerOptions ProxyOutgoingConfig
configuration options for DIRECT server access
Definition: PeerOptions.cc:22
int kind
which custom access list verb matched
Definition: Acl.h:153
Ssl::BumpMode checkForPeekAndSpliceGuess() const
Guesses the final bumping decision when no ssl_bump rules match.
std::shared_ptr< SSL_CTX > ContextPointer
Definition: Context.h:28
bool allowed() const
Definition: Acl.h:141
void checkForPeekAndSpliceDone(allow_t answer)
Callback function for ssl_bump acl check in step3 SSL bump step.
virtual Security::EncryptorAnswer & answer()=0
gives PeerConnector access to the in-dialer answer
#define CallJobHere1(debugSection, debugLevel, job, Class, method, arg1)
Definition: AsyncJobCalls.h:62
bool tunneled
whether we spliced the connections instead of negotiating encryption
bool action(int fd, size_t metasize, const char *fn, const char *url, const SquidMetaList &meta)
Definition: purge.cc:311
CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector)
void applyTlsDetailsToSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, Ssl::BumpMode bumpMode)
Definition: bio.cc:686
char * url
Definition: tcp-banger2.c:114
void Comm::ConnectionPointer & clientConn
Definition: stub_tunnel.cc:19
void setClientSNI(SSL *ssl, const char *fqdn)
Definition: support.cc:914
virtual void noteNegotiationError(const int result, const int ssl_error, const int ssl_lib_error)
Callback dialer API to allow PeerConnector to set the answer.
Definition: PeerConnector.h:71
virtual bool initialize(Security::SessionPointer &)
void * BIO_get_data(BIO *table)
Definition: bio.h:209
BumpMode
Definition: support.h:130
void checkForPeekAndSpliceMatched(const Ssl::BumpMode finalMode)
Handles the final bumping decision.
virtual void noteNegotiationError(const int result, const int ssl_error, const int ssl_lib_error)
Ssl::ErrorDetail * detail
Definition: errorpage.h:171
const char * ErrorString(const ErrorCode code)
Definition: forward.h:120
class SquidConfig Config
Definition: SquidConfig.cc:12
#define NULL
Definition: types.h:166
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const
assigns uninitialized adapted_request and url ALE components
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:41

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors