Io.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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
17namespace Security {
18
19template <typename Fun>
21static void PrepForIo();
22
23typedef SessionPointer::element_type *ConnectionPointer;
24
25} // namespace Security
26
27void
28Security::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
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.
56void
58{
59#if USE_OPENSSL
61#endif
62}
63
66static 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
72
73 // as the last step, reset errno to know when the I/O operation set it
74 errno = 0;
75}
76
79template <typename Fun>
81Security::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
#define assert(EX)
Definition: assert.h:17
bool isOpen() const
Definition: Connection.h:101
void setPeerCertificate(const CertPointer &)
Definition: ErrorDetail.cc:490
a summary a TLS I/O operation outcome
Definition: Io.h:19
Category category
primary outcome classification
Definition: Io.h:40
bool important
whether the error was serious/unusual
Definition: Io.h:44
void print(std::ostream &os) const
Definition: Io.cc:28
const char * errorDescription
a brief description of an error
Definition: Io.h:43
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
#define fd_table
Definition: fde.h:189
int ssl_ex_index_ssl_error_detail
SBuf ErrorDetail
Definition: Esi.h:29
Network/connection security abstraction layer.
Definition: Connection.h:34
static IoResult Handshake(Comm::Connection &, ErrorCode, Fun)
IoResult Accept(Comm::Connection &transport)
accept a TLS connection over the specified to-Squid transport connection
Definition: Io.cc:197
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:132
IoResult Connect(Comm::Connection &transport)
establish a TLS connection over the specified from-Squid transport connection
Definition: Io.cc:212
SessionPointer::element_type * ConnectionPointer
Definition: Io.cc:23
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition: forward.h:86
static void PrepForIo()
Definition: Io.cc:67
void ForgetErrors()
clear any errors that a TLS library has accumulated in its global storage
Definition: Io.cc:57
void ForgetErrors()
Clear any errors accumulated by OpenSSL in its global storage.
Definition: gadgets.cc:17
@ SQUID_TLS_ERR_CONNECT
failure to establish a connection with a TLS server
Definition: forward.h:230
@ SQUID_TLS_ERR_ACCEPT
failure to accept a connection from a TLS client
Definition: forward.h:229
std::ostream & ForceAlert(std::ostream &s)
Definition: debug.cc:1408

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors