2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 83 TLS I/O */
13 #include "security/Io.h"
17 template <typename Fun
>
18 static IoResult
Handshake(Comm::Connection
&, ErrorCode
, Fun
);
19 static void PrepForIo();
21 typedef SessionPointer::element_type
*ConnectionPointer
;
23 } // namespace Security
25 // TODO: Replace high-level ERR_get_error() calls with a new std::ostream
26 // ReportErrors manipulator inside debugs(), followed by a ForgetErrors() call.
28 Security::ForgetErrors()
31 unsigned int reported
= 0; // efficiently marks ForgetErrors() call boundary
32 while (const auto errorToForget
= ERR_get_error())
33 debugs(83, 7, '#' << (++reported
) << ": " << asHex(errorToForget
));
37 /// the steps necessary to perform before the upcoming TLS I/O
38 /// to correctly interpret/detail the outcome of that I/O
42 // flush earlier errors that some call forgot to extract, so that we will
43 // only get the error(s) specific to the upcoming I/O operation
46 // as the last step, reset errno to know when the I/O operation set it
50 /// Calls the given TLS handshake function and analysis its outcome.
51 /// Handles alert logging and being called without adequate TLS library support.
52 template <typename Fun
>
53 static Security::IoResult
54 Security::Handshake(Comm::Connection
&transport
, const ErrorCode topError
, Fun ioCall
)
56 assert(transport
.isOpen());
57 const auto fd
= transport
.fd
;
58 auto connection
= fd_table
[fd
].ssl
.get();
61 const auto callResult
= ioCall(connection
);
62 const auto xerrno
= errno
;
64 debugs(83, 5, callResult
<< '/' << xerrno
<< " for TLS connection " <<
65 static_cast<void*>(connection
) << " over " << transport
);
69 return IoResult(IoResult::ioSuccess
);
71 const auto ioError
= SSL_get_error(connection
, callResult
);
73 // quickly handle common, non-erroneous outcomes
76 case SSL_ERROR_WANT_READ
:
77 return IoResult(IoResult::ioWantRead
);
79 case SSL_ERROR_WANT_WRITE
:
80 return IoResult(IoResult::ioWantWrite
);
83 ; // fall through to handle the problem
86 // now we know that we are dealing with a real problem; detail it
87 const ErrorDetail::Pointer errorDetail
=
88 new ErrorDetail(topError
, ioError
, xerrno
);
90 IoResult
ioResult(errorDetail
);
92 // collect debugging-related details
94 case SSL_ERROR_SYSCALL
:
95 if (callResult
== 0) {
96 ioResult
.errorDescription
= "peer aborted";
98 ioResult
.errorDescription
= "system call failure";
99 ioResult
.important
= (xerrno
== ECONNRESET
);
103 case SSL_ERROR_ZERO_RETURN
:
104 // peer sent a "close notify" alert, closing TLS connection for writing
105 ioResult
.errorDescription
= "peer closed";
106 ioResult
.important
= true;
110 // an ever-increasing number of possible cases but usually SSL_ERROR_SSL
111 ioResult
.errorDescription
= "failure";
112 ioResult
.important
= true;
118 if (callResult
== GNUTLS_E_SUCCESS
) {
119 // TODO: Avoid gnutls_*() calls if debugging is off.
120 const auto desc
= gnutls_session_get_desc(connection
);
121 debugs(83, 2, "TLS session info: " << desc
);
123 return IoResult(IoResult::ioSuccess
);
126 // Debug the TLS connection state so far.
127 // TODO: Avoid gnutls_*() calls if debugging is off.
128 const auto descIn
= gnutls_handshake_get_last_in(connection
);
129 debugs(83, 2, "handshake IN: " << gnutls_handshake_description_get_name(descIn
));
130 const auto descOut
= gnutls_handshake_get_last_out(connection
);
131 debugs(83, 2, "handshake OUT: " << gnutls_handshake_description_get_name(descOut
));
133 if (callResult
== GNUTLS_E_WARNING_ALERT_RECEIVED
) {
134 const auto alert
= gnutls_alert_get(connection
);
135 debugs(83, DBG_IMPORTANT
, "WARNING: TLS alert: " << gnutls_alert_get_name(alert
));
136 // fall through to retry
139 if (!gnutls_error_is_fatal(callResult
)) {
140 const auto reading
= gnutls_record_get_direction(connection
) == 0;
141 return IoResult(reading
? IoResult::ioWantRead
: IoResult::ioWantWrite
);
144 // now we know that we are dealing with a real problem; detail it
145 const ErrorDetail::Pointer errorDetail
=
146 new ErrorDetail(topError
, callResult
, xerrno
);
148 IoResult
ioResult(errorDetail
);
149 ioResult
.errorDescription
= "failure";
153 // TLS I/O code path should never be reachable without a TLS/SSL library.
154 debugs(1, DBG_CRITICAL
, ForceAlert
<< "BUG: " <<
155 "Unexpected TLS I/O in Squid built without a TLS/SSL library");
156 assert(false); // we want a stack trace which fatal() does not produce
157 return IoResult(nullptr); // not reachable
161 // TODO: After dropping OpenSSL v1.1.0 support, this and Security::Connect() can
162 // be simplified further by using SSL_do_handshake() and eliminating lambdas.
164 Security::Accept(Comm::Connection
&transport
)
166 return Handshake(transport
, SQUID_TLS_ERR_ACCEPT
, [] (ConnectionPointer tlsConn
) {
168 return SSL_accept(tlsConn
);
170 return gnutls_handshake(tlsConn
);
172 return sizeof(tlsConn
); // the value is unused; should be unreachable
177 /// establish a TLS connection over the specified from-Squid transport connection
179 Security::Connect(Comm::Connection
&transport
)
181 return Handshake(transport
, SQUID_TLS_ERR_CONNECT
, [] (ConnectionPointer tlsConn
) {
183 return SSL_connect(tlsConn
);
185 return gnutls_handshake(tlsConn
);
187 return sizeof(tlsConn
); // the value is unused; should be unreachable