Io.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 "base/IoManip.h"
13 #include "fde.h"
14 #include "security/Io.h"
15 #include "ssl/gadgets.h"
16 
17 namespace Security {
18 
19 template <typename Fun>
21 static void PrepForIo();
22 
23 typedef SessionPointer::element_type *ConnectionPointer;
24 
25 } // namespace Security
26 
27 void
28 Security::IoResult::print(std::ostream &os) const
29 {
30  const char *strCat = "unknown";
31  switch (category) {
32  case ioSuccess:
33  strCat = "success";
34  break;
35  case ioWantRead:
36  strCat = "want-read";
37  break;
38  case ioWantWrite:
39  strCat = "want-write";
40  break;
41  case ioError:
42  strCat = "error";
43  break;
44  }
45  os << strCat;
46 
47  if (errorDescription)
48  os << ", " << errorDescription;
49 
50  if (important)
51  os << ", important";
52 }
53 
54 // TODO: Replace high-level ERR_get_error() calls with ForgetErrors() calls or
55 // exceptions carrying ReportAndForgetErrors() reports.
56 void
58 {
59 #if USE_OPENSSL
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 << "ERROR: Squid 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:110
#define DBG_CRITICAL
Definition: Stream.h:40
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition: forward.h:82
std::ostream & ForceAlert(std::ostream &s)
Definition: debug.cc:1396
@ SQUID_TLS_ERR_CONNECT
failure to establish a connection with a TLS server
Definition: forward.h:220
a summary a TLS I/O operation outcome
Definition: Io.h:19
SessionPointer::element_type * ConnectionPointer
Definition: Io.cc:23
Category category
primary outcome classification
Definition: Io.h:40
void ForgetErrors()
clear any errors that a TLS library has accumulated in its global storage
Definition: Io.cc:57
#define assert(EX)
Definition: assert.h:19
static IoResult Handshake(Comm::Connection &, ErrorCode, Fun)
SBuf ErrorDetail
Definition: Esi.h:29
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
IoResult Connect(Comm::Connection &transport)
establish a TLS connection over the specified from-Squid transport connection
Definition: Io.cc:212
void ForgetErrors()
Clear any errors accumulated by OpenSSL in its global storage.
Definition: gadgets.cc:17
@ SQUID_TLS_ERR_ACCEPT
failure to accept a connection from a TLS client
Definition: forward.h:219
#define DBG_IMPORTANT
Definition: Stream.h:41
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
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
int ssl_ex_index_ssl_error_detail
void print(std::ostream &os) const
Definition: Io.cc:28

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors