Io.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 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 TLS I/O */
10 
11 #include "squid.h"
12 #include "fde.h"
13 #include "security/Io.h"
14 
15 namespace Security {
16 
17 template <typename Fun>
19 static void PrepForIo();
20 
21 typedef SessionPointer::element_type *ConnectionPointer;
22 
23 } // namespace Security
24 
25 void
26 Security::IoResult::print(std::ostream &os) const
27 {
28  const char *strCat = "unknown";
29  switch (category) {
30  case ioSuccess:
31  strCat = "success";
32  break;
33  case ioWantRead:
34  strCat = "want-read";
35  break;
36  case ioWantWrite:
37  strCat = "want-write";
38  break;
39  case ioError:
40  strCat = "error";
41  break;
42  }
43  os << strCat;
44 
45  if (errorDescription)
46  os << ", " << errorDescription;
47 
48  if (important)
49  os << ", important";
50 }
51 
52 // TODO: Replace high-level ERR_get_error() calls with a new std::ostream
53 // ReportErrors manipulator inside debugs(), followed by a ForgetErrors() call.
54 void
56 {
57 #if USE_OPENSSL
58  unsigned int reported = 0; // efficiently marks ForgetErrors() call boundary
59  while (const auto errorToForget = ERR_get_error())
60  debugs(83, 7, '#' << (++reported) << ": " << asHex(errorToForget));
61 #endif
62 }
63 
66 static void
68 {
69  // flush earlier errors that some call forgot to extract, so that we will
70  // only get the error(s) specific to the upcoming I/O operation
71  ForgetErrors();
72 
73  // as the last step, reset errno to know when the I/O operation set it
74  errno = 0;
75 }
76 
79 template <typename Fun>
80 static Security::IoResult
81 Security::Handshake(Comm::Connection &transport, const ErrorCode topError, Fun ioCall)
82 {
83  assert(transport.isOpen());
84  const auto fd = transport.fd;
85  auto connection = fd_table[fd].ssl.get();
86 
87  PrepForIo();
88  const auto callResult = ioCall(connection);
89  const auto xerrno = errno;
90 
91  debugs(83, 5, callResult << '/' << xerrno << " for TLS connection " <<
92  static_cast<void*>(connection) << " over " << transport);
93 
94 #if USE_OPENSSL
95  if (callResult > 0)
97 
98  const auto ioError = SSL_get_error(connection, callResult);
99 
100  // quickly handle common, non-erroneous outcomes
101  switch (ioError) {
102 
103  case SSL_ERROR_WANT_READ:
105 
106  case SSL_ERROR_WANT_WRITE:
108 
109  default:
110  ; // fall through to handle the problem
111  }
112 
113  // now we know that we are dealing with a real problem; detail it
114  ErrorDetail::Pointer errorDetail;
115  if (const auto oldDetail = SSL_get_ex_data(connection, ssl_ex_index_ssl_error_detail)) {
116  errorDetail = *static_cast<ErrorDetail::Pointer*>(oldDetail);
117  } else {
118  errorDetail = new ErrorDetail(topError, ioError, xerrno);
119  if (const auto serverCert = SSL_get_peer_certificate(connection))
120  errorDetail->setPeerCertificate(CertPointer(serverCert));
121  }
122  IoResult ioResult(errorDetail);
123 
124  // collect debugging-related details
125  switch (ioError) {
126  case SSL_ERROR_SYSCALL:
127  if (callResult == 0) {
128  ioResult.errorDescription = "peer aborted";
129  } else {
130  ioResult.errorDescription = "system call failure";
131  ioResult.important = (xerrno == ECONNRESET);
132  }
133  break;
134 
135  case SSL_ERROR_ZERO_RETURN:
136  // peer sent a "close notify" alert, closing TLS connection for writing
137  ioResult.errorDescription = "peer closed";
138  ioResult.important = true;
139  break;
140 
141  default:
142  // an ever-increasing number of possible cases but usually SSL_ERROR_SSL
143  ioResult.errorDescription = "failure";
144  ioResult.important = true;
145  }
146 
147  return ioResult;
148 
149 #elif USE_GNUTLS
150  if (callResult == GNUTLS_E_SUCCESS) {
151  // TODO: Avoid gnutls_*() calls if debugging is off.
152  const auto desc = gnutls_session_get_desc(connection);
153  debugs(83, 2, "TLS session info: " << desc);
154  gnutls_free(desc);
156  }
157 
158  // Debug the TLS connection state so far.
159  // TODO: Avoid gnutls_*() calls if debugging is off.
160  const auto descIn = gnutls_handshake_get_last_in(connection);
161  debugs(83, 2, "handshake IN: " << gnutls_handshake_description_get_name(descIn));
162  const auto descOut = gnutls_handshake_get_last_out(connection);
163  debugs(83, 2, "handshake OUT: " << gnutls_handshake_description_get_name(descOut));
164 
165  if (callResult == GNUTLS_E_WARNING_ALERT_RECEIVED) {
166  const auto alert = gnutls_alert_get(connection);
167  debugs(83, DBG_IMPORTANT, "WARNING: TLS alert: " << gnutls_alert_get_name(alert));
168  // fall through to retry
169  }
170 
171  if (!gnutls_error_is_fatal(callResult)) {
172  const auto reading = gnutls_record_get_direction(connection) == 0;
174  }
175 
176  // now we know that we are dealing with a real problem; detail it
177  const ErrorDetail::Pointer errorDetail =
178  new ErrorDetail(topError, callResult, xerrno);
179 
180  IoResult ioResult(errorDetail);
181  ioResult.errorDescription = "failure";
182  return ioResult;
183 
184 #else
185  (void)topError;
186  // TLS I/O code path should never be reachable without a TLS/SSL library.
187  debugs(1, DBG_CRITICAL, ForceAlert << "BUG: " <<
188  "Unexpected TLS I/O in Squid built without a TLS/SSL library");
189  assert(false); // we want a stack trace which fatal() does not produce
190  return IoResult(nullptr); // not reachable
191 #endif
192 }
193 
194 // TODO: After dropping OpenSSL v1.1.0 support, this and Security::Connect() can
195 // be simplified further by using SSL_do_handshake() and eliminating lambdas.
198 {
199  return Handshake(transport, SQUID_TLS_ERR_ACCEPT, [] (ConnectionPointer tlsConn) {
200 #if USE_OPENSSL
201  return SSL_accept(tlsConn);
202 #elif USE_GNUTLS
203  return gnutls_handshake(tlsConn);
204 #else
205  return sizeof(tlsConn); // the value is unused; should be unreachable
206 #endif
207  });
208 }
209 
213 {
214  return Handshake(transport, SQUID_TLS_ERR_CONNECT, [] (ConnectionPointer tlsConn) {
215 #if USE_OPENSSL
216  return SSL_connect(tlsConn);
217 #elif USE_GNUTLS
218  return gnutls_handshake(tlsConn);
219 #else
220  return sizeof(tlsConn); // the value is unused; should be unreachable
221 #endif
222  });
223 }
224 
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:102
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition: forward.h:74
a summary a TLS I/O operation outcome
Definition: Io.h:19
#define DBG_CRITICAL
Definition: Debug.h:40
#define DBG_IMPORTANT
Definition: Debug.h:41
SessionPointer::element_type * ConnectionPointer
Definition: Io.cc:21
@ SQUID_TLS_ERR_CONNECT
failure to establish a connection with a TLS server
Definition: forward.h:212
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
Category category
primary outcome classification
Definition: Io.h:40
void setPeerCertificate(const CertPointer &)
Definition: ErrorDetail.cc:487
void ForgetErrors()
clear any errors that a TLS library has accumulated in its global storage
Definition: Io.cc:55
#define assert(EX)
Definition: assert.h:19
static IoResult Handshake(Comm::Connection &, ErrorCode, Fun)
SBuf ErrorDetail
Definition: Esi.h:29
@ SQUID_TLS_ERR_ACCEPT
failure to accept a connection from a TLS client
Definition: forward.h:211
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition: Debug.h:277
static void PrepForIo()
Definition: Io.cc:67
#define fd_table
Definition: fde.h:189
const char * errorDescription
a brief description of an error
Definition: Io.h:43
std::ostream & ForceAlert(std::ostream &s)
Definition: debug.cc:717
IoResult Connect(Comm::Connection &transport)
establish a TLS connection over the specified from-Squid transport connection
Definition: Io.cc:212
IoResult Accept(Comm::Connection &transport)
accept a TLS connection over the specified to-Squid transport connection
Definition: Io.cc:197
bool isOpen() const
Definition: Connection.h:99
bool important
whether the error was serious/unusual
Definition: Io.h:44
Network/connection security abstraction layer.
Definition: Connection.h:34
int ssl_ex_index_ssl_error_detail
void print(std::ostream &os) const
Definition: Io.cc:26

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors