Squid Web Cache master
Loading...
Searching...
No Matches
Io.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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>
21
22typedef SessionPointer::element_type *ConnectionPointer;
23
24} // namespace Security
25
27void
29{
30 const char *strCat = nullptr;
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 = errorDescription;
43 break;
44 }
45 os << (strCat ? strCat : "unknown");
46}
47
48void
49Security::IoResult::printGist(std::ostream &os) const
50{
51 printDescription(os);
52 if (important)
53 os << ", important";
54 // no errorDetail in this summary output
55}
56
57void
59{
60 printDescription(os);
61 if (errorDetail)
62 os << Debug::Extra << "error detail: " << errorDetail;
63 // this->important flag may affect caller debugs() level, but the flag
64 // itself is not reported to the admin explicitly
65}
66
67// TODO: Replace high-level ERR_get_error() calls with ForgetErrors() calls or
68// exceptions carrying ReportAndForgetErrors() reports.
69void
71{
72#if USE_OPENSSL
74#endif
75}
76
77void
79{
80 // flush earlier errors that some call forgot to extract, so that we will
81 // only get the error(s) specific to the upcoming I/O operation
83
84 // as the last step, reset errno to know when the I/O operation set it
85 errno = 0;
86}
87
90template <typename Fun>
92Security::Handshake(Comm::Connection &transport, const ErrorCode topError, Fun ioCall)
93{
94 assert(transport.isOpen());
95 const auto fd = transport.fd;
96 auto connection = fd_table[fd].ssl.get();
97
98 PrepForIo();
99 const auto callResult = ioCall(connection);
100 const auto xerrno = errno;
101
102 debugs(83, 5, callResult << '/' << xerrno << " for TLS connection " <<
103 static_cast<void*>(connection) << " over " << transport);
104
105#if USE_OPENSSL
106 if (callResult > 0)
108
109 const auto ioError = SSL_get_error(connection, callResult);
110
111 // quickly handle common, non-erroneous outcomes
112 switch (ioError) {
113
114 case SSL_ERROR_WANT_READ:
116
117 case SSL_ERROR_WANT_WRITE:
119
120 default:
121 ; // fall through to handle the problem
122 }
123
124 // now we know that we are dealing with a real problem; detail it
125 ErrorDetail::Pointer errorDetail;
126 if (const auto oldDetail = SSL_get_ex_data(connection, ssl_ex_index_ssl_error_detail)) {
127 errorDetail = *static_cast<ErrorDetail::Pointer*>(oldDetail);
128 } else {
129 errorDetail = new ErrorDetail(topError, ioError, xerrno);
130 if (const auto serverCert = SSL_get_peer_certificate(connection))
131 errorDetail->setPeerCertificate(CertPointer(serverCert));
132 }
133 IoResult ioResult(errorDetail);
134
135 // collect debugging-related details
136 switch (ioError) {
137 case SSL_ERROR_SYSCALL:
138 if (callResult == 0) {
139 ioResult.errorDescription = "peer aborted";
140 } else {
141 ioResult.errorDescription = "system call failure";
142 ioResult.important = (xerrno == ECONNRESET);
143 }
144 break;
145
146 case SSL_ERROR_ZERO_RETURN:
147 // peer sent a "close notify" alert, closing TLS connection for writing
148 ioResult.errorDescription = "peer closed";
149 ioResult.important = true;
150 break;
151
152 default:
153 // an ever-increasing number of possible cases but usually SSL_ERROR_SSL
154 ioResult.errorDescription = "failure";
155 ioResult.important = true;
156 }
157
158 return ioResult;
159
160#elif HAVE_LIBGNUTLS
161 if (callResult == GNUTLS_E_SUCCESS) {
162 // TODO: Avoid gnutls_*() calls if debugging is off.
163 const auto desc = gnutls_session_get_desc(connection);
164 debugs(83, 2, "TLS session info: " << desc);
165 gnutls_free(desc);
167 }
168
169 // Debug the TLS connection state so far.
170 // TODO: Avoid gnutls_*() calls if debugging is off.
171 const auto descIn = gnutls_handshake_get_last_in(connection);
172 debugs(83, 2, "handshake IN: " << gnutls_handshake_description_get_name(descIn));
173 const auto descOut = gnutls_handshake_get_last_out(connection);
174 debugs(83, 2, "handshake OUT: " << gnutls_handshake_description_get_name(descOut));
175
176 if (callResult == GNUTLS_E_WARNING_ALERT_RECEIVED) {
177 const auto alert = gnutls_alert_get(connection);
178 debugs(83, DBG_IMPORTANT, "WARNING: TLS alert: " << gnutls_alert_get_name(alert));
179 // fall through to retry
180 }
181
182 if (!gnutls_error_is_fatal(callResult)) {
183 const auto reading = gnutls_record_get_direction(connection) == 0;
184 return IoResult(reading ? IoResult::ioWantRead : IoResult::ioWantWrite);
185 }
186
187 // now we know that we are dealing with a real problem; detail it
188 const ErrorDetail::Pointer errorDetail =
189 new ErrorDetail(topError, callResult, xerrno);
190
191 IoResult ioResult(errorDetail);
192 ioResult.errorDescription = "failure";
193 return ioResult;
194
195#else
196 (void)topError;
197 // TLS I/O code path should never be reachable without a TLS/SSL library.
198 debugs(1, DBG_CRITICAL, ForceAlert << "ERROR: Squid BUG: " <<
199 "Unexpected TLS I/O in Squid built without a TLS/SSL library");
200 assert(false); // we want a stack trace which fatal() does not produce
201 return IoResult(nullptr); // not reachable
202#endif
203}
204
205// TODO: After dropping OpenSSL v1.1.0 support, this and Security::Connect() can
206// be simplified further by using SSL_do_handshake() and eliminating lambdas.
209{
210 return Handshake(transport, SQUID_TLS_ERR_ACCEPT, [] (ConnectionPointer tlsConn) {
211#if USE_OPENSSL
212 return SSL_accept(tlsConn);
213#elif HAVE_LIBGNUTLS
214 return gnutls_handshake(tlsConn);
215#else
216 return sizeof(tlsConn); // the value is unused; should be unreachable
217#endif
218 });
219}
220
224{
225 return Handshake(transport, SQUID_TLS_ERR_CONNECT, [] (ConnectionPointer tlsConn) {
226#if USE_OPENSSL
227 return SSL_connect(tlsConn);
228#elif HAVE_LIBGNUTLS
229 return gnutls_handshake(tlsConn);
230#else
231 return sizeof(tlsConn); // the value is unused; should be unreachable
232#endif
233 });
234}
235
#define assert(EX)
Definition assert.h:17
bool isOpen() const
Definition Connection.h:101
static std::ostream & Extra(std::ostream &)
Definition debug.cc:1316
interface for supplying additional information about a transaction failure
Definition Detail.h:21
void setPeerCertificate(const CertPointer &)
TLS Handshake Protocol frame from RFC 5246 Section 7.4.
Definition Handshake.cc:67
a summary a TLS I/O operation outcome
Definition Io.h:19
void printDescription(std::ostream &) const
common part of printGist() and printWithExtras()
Definition Io.cc:28
Category category
primary outcome classification
Definition Io.h:45
void printWithExtras(std::ostream &) const
Definition Io.cc:58
const char * errorDescription
a brief description of an error
Definition Io.h:48
void printGist(std::ostream &) const
reports brief summary (on one line) suitable for low-level debugging
Definition Io.cc:49
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define fd_table
Definition fde.h:189
int ssl_ex_index_ssl_error_detail
Network/connection security abstraction layer.
Definition Connection.h:34
void PrepForIo()
Definition Io.cc:78
IoResult Accept(Comm::Connection &transport)
accept a TLS connection over the specified to-Squid transport connection
Definition Io.cc:208
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition forward.h:134
IoResult Connect(Comm::Connection &transport)
establish a TLS connection over the specified from-Squid transport connection
Definition Io.cc:223
SessionPointer::element_type * ConnectionPointer
Definition Io.cc:22
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
Definition forward.h:88
void ForgetErrors()
clear any errors that a TLS library has accumulated in its global storage
Definition Io.cc:70
void ForgetErrors()
Clear any errors accumulated by OpenSSL in its global storage.
Definition gadgets.cc:65
@ SQUID_TLS_ERR_CONNECT
failure to establish a connection with a TLS server
Definition forward.h:234
@ SQUID_TLS_ERR_ACCEPT
failure to accept a connection from a TLS client
Definition forward.h:233
std::ostream & ForceAlert(std::ostream &s)
Definition debug.cc:1411