Security::PeerConnector::start()
{
AsyncJob::start();
+ debugs(83, 5, "this=" << (void*)this);
Security::SessionPointer tmp;
if (prepareSocket() && initialize(tmp))
void
Security::PeerConnector::connectionClosed(const char *reason)
{
+ debugs(83, 5, reason << " socket closed/closing. this=" << (void*)this);
mustStop(reason);
callback = NULL;
}
bool
Security::PeerConnector::prepareSocket()
{
- const int fd = serverConnection()->fd;
- if (!Comm::IsConnOpen(serverConn) || fd_table[serverConn->fd].closing()) {
+ debugs(83, 5, serverConnection() << ", this=" << (void*)this);
+ if (!Comm::IsConnOpen(serverConnection()) || fd_table[serverConnection()->fd].closing()) {
connectionClosed("Security::PeerConnector::prepareSocket");
return false;
}
+ debugs(83, 5, serverConnection());
+
// watch for external connection closures
typedef CommCbMemFunT<Security::PeerConnector, CommCloseCbParams> Dialer;
closeHandler = JobCallback(9, 5, Dialer, this, Security::PeerConnector::commCloseHandler);
- comm_add_close_handler(fd, closeHandler);
+ comm_add_close_handler(serverConnection()->fd, closeHandler);
return true;
}
Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
{
Security::ContextPointer ctx(getTlsContext());
+ debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
const auto xerrno = errno;
// A TLS/SSL session has now been created for the connection and stored in fd_table
serverSession = fd_table[serverConnection()->fd].ssl;
+ debugs(83, 5, serverConnection() << ", session=" << (void*)serverSession.get());
#if USE_OPENSSL
// If CertValidation Helper used do not lookup checklist for errors,
return;
#if USE_OPENSSL
- const int result = SSL_connect(fd_table[fd].ssl.get());
+ auto session = fd_table[fd].ssl.get();
+ debugs(83, 5, "SSL_connect session=" << (void*)session);
+ const int result = SSL_connect(session);
+ if (result <= 0) {
#elif USE_GNUTLS
- const int result = gnutls_handshake(fd_table[fd].ssl.get());
+ auto session = fd_table[fd].ssl.get();
+ debugs(83, 5, "gnutls_handshake session=" << (void*)session);
+ const int result = gnutls_handshake(session);
+ if (result != GNUTLS_E_SUCCESS) {
+ debugs(83, 5, "gnutls_handshake session=" << (void*)session << ", result=" << result);
#else
- const int result = -1;
+ if (const int result = -1) {
#endif
- if (result <= 0) {
handleNegotiateError(result);
return; // we might be gone by now
}
Security::SessionPointer session(fd_table[fd].ssl);
Ssl::CertValidationRequest validationRequest;
- // WARNING: Currently we do not use any locking for any of the
- // members of the Ssl::CertValidationRequest class. In this code the
+ // WARNING: Currently we do not use any locking for 'errors' member
+ // of the Ssl::CertValidationRequest class. In this code the
// Ssl::CertValidationRequest object used only to pass data to
// Ssl::CertValidationHelper::submit method.
- validationRequest.ssl = session.get();
+ validationRequest.ssl = session;
if (SBuf *dName = (SBuf *)SSL_get_ex_data(session.get(), ssl_ex_index_server))
validationRequest.domainName = dName->c_str();
if (Security::CertErrors *errs = static_cast<Security::CertErrors *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors)))
void
Security::PeerConnector::handleNegotiateError(const int ret)
{
-#if USE_OPENSSL
const int fd = serverConnection()->fd;
- unsigned long ssl_lib_error = SSL_ERROR_NONE;
- Security::SessionPointer session(fd_table[fd].ssl);
+ const Security::SessionPointer session(fd_table[fd].ssl);
+ unsigned long ssl_lib_error = ret;
+
+#if USE_OPENSSL
const int ssl_error = SSL_get_error(session.get(), ret);
switch (ssl_error) {
break;
default:
// no special error handling for all other errors
+ ssl_lib_error = SSL_ERROR_NONE;
break;
}
- // Log connection details, if any
- recordNegotiationDetails();
- noteNegotiationError(ret, ssl_error, ssl_lib_error);
-
#elif USE_GNUTLS
- // TODO: handle specific errors individually like above
- if (gnutls_error_is_fatal(ret) == 0) {
- noteWantRead();
+ const int ssl_error = ret;
+
+ switch (ret) {
+ case GNUTLS_E_WARNING_ALERT_RECEIVED: {
+ auto alert = gnutls_alert_get(session.get());
+ debugs(83, DBG_IMPORTANT, "TLS ALERT: " << gnutls_alert_get_name(alert));
+ }
+ // drop through to next case
+
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_INTERRUPTED:
+ if (gnutls_record_get_direction(session.get()) == 0)
+ noteWantWrite();
+ else
+ noteWantRead();
return;
+
+ default:
+ // no special error handling for all other errors
+ break;
}
+
+#else
+ // this avoids unused variable compiler warnings.
+ Must(!session);
+ const int ssl_error = ret;
#endif
+
+ // Log connection details, if any
+ recordNegotiationDetails();
+ noteNegotiationError(ret, ssl_error, ssl_lib_error);
}
void
void
Security::PeerConnector::noteNegotiationError(const int ret, const int ssl_error, const int ssl_lib_error)
{
-#if USE_OPENSSL // not used unless OpenSSL enabled.
#if defined(EPROTO)
int sysErrNo = EPROTO;
#else
int sysErrNo = EACCES;
#endif
+#if USE_OPENSSL
// store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0)
sysErrNo = errno;
+#endif
const int fd = serverConnection()->fd;
- debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd <<
+ debugs(83, DBG_IMPORTANT, "ERROR: negotiating TLS on FD " << fd <<
": " << Security::ErrorString(ssl_lib_error) << " (" <<
ssl_error << "/" << ret << "/" << errno << ")");
anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, NULL);
anErr->xerrno = sysErrNo;
+#if USE_OPENSSL
Security::SessionPointer session(fd_table[fd].ssl);
Ssl::ErrorDetail *errFromFailure = static_cast<Ssl::ErrorDetail *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_error_detail));
if (errFromFailure != NULL) {
if (ssl_lib_error != SSL_ERROR_NONE)
anErr->detail->setLibError(ssl_lib_error);
+#endif
noteNegotiationDone(anErr);
bail(anErr);
-#endif
}
void
// the recepient before the fd-closure notification), but we would rather
// minimize the number of fd-closure notifications and let the recepient
// manage the TCP state of the connection.
+
+#if USE_GNUTLS
+ // but we do need to release the bad TLS related details in fd_table
+ // ... or GnuTLS will SEGFAULT.
+ const int fd = serverConnection()->fd;
+ Security::SessionClose(fd_table[fd].ssl, fd);
+#endif
}
void
Security::PeerConnector::callBack()
{
+ debugs(83, 5, "TLS setup ended for " << serverConnection());
+
AsyncCall::Pointer cb = callback;
// Do this now so that if we throw below, swanSong() assert that we _tried_
// to call back holds.
int i = gnutls_record_recv(session, buf, len);
#endif
if (i > 0) {
+ debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
(void)VALGRIND_MAKE_MEM_DEFINED(buf, i);
}
int i = gnutls_record_send(session, buf, len);
#endif
+ if (i > 0) {
+ debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
+ }
return i;
}
#endif
const char *errAction = "with no TLS/SSL library";
int errCode = 0;
#if USE_OPENSSL
- Security::SessionPointer session(SSL_new(ctx.get()));
+ Security::SessionPointer session(SSL_new(ctx.get()), [](SSL *p) {
+ debugs(83, 5, "SSL_free session=" << (void*)p);
+ SSL_free(p);
+ });
+ debugs(83, 5, "SSL_new session=" << (void*)session.get());
if (!session) {
errCode = ERR_get_error();
errAction = "failed to allocate handle";
#elif USE_GNUTLS
gnutls_session_t tmp;
errCode = gnutls_init(&tmp, static_cast<unsigned int>(type));
- Security::SessionPointer session(tmp);
+ Security::SessionPointer session(tmp, [](gnutls_session_t p) {
+ debugs(83, 5, "gnutls_deinit session=" << (void*)p);
+ gnutls_deinit(p); }
+ });
+ debugs(83, 5, "gnutls_init session=" << (void*)session.get());
if (errCode != GNUTLS_E_SUCCESS) {
session.reset();
errAction = "failed to initialize session";
if (errCode == GNUTLS_E_SUCCESS) {
#endif
+ debugs(83, 5, "link FD " << fd << " to TLS session=" << (void*)session.get());
fd_table[fd].ssl = session;
fd_table[fd].read_method = &tls_read_method;
fd_table[fd].write_method = &tls_write_method;
}
void
-Security::SessionClose(const Security::SessionPointer &s)
+Security::SessionClose(const Security::SessionPointer &s, const int fdOnError)
{
debugs(83, 5, "session=" << (void*)s.get());
- if (s) {
+ if (s && fdOnError == -1) {
#if USE_OPENSSL
SSL_shutdown(s.get());
#elif USE_GNUTLS
gnutls_bye(s.get(), GNUTLS_SHUT_RDWR);
#endif
}
+
+#if USE_GNUTLS
+ // XXX: should probably be done for OpenSSL too, but that needs testing.
+ if (fdOnError != -1) {
+ debugs(83, 5, "unlink FD " << fdOnError << " from TLS session=" << (void*)fd_table[fdOnError].ssl.get());
+ fd_table[fdOnError].ssl.reset();
+ fd_table[fdOnError].read_method = &default_read_method;
+ fd_table[fdOnError].write_method = &default_write_method;
+ fd_note(fdOnError, "TLS error");
+ }
+#endif
}
bool
bool CreateServerSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx);
#if USE_OPENSSL
-CtoCpp1(SSL_free, SSL *);
-#if defined(CRYPTO_LOCK_SSL) // OpenSSL 1.0
-inline int SSL_up_ref(SSL *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_SSL); return 0;}
-#endif
-typedef Security::LockingPointer<SSL, Security::SSL_free_cpp, HardFun<int, SSL *, SSL_up_ref> > SessionPointer;
+typedef std::shared_ptr<SSL> SessionPointer;
typedef std::unique_ptr<SSL_SESSION, HardFun<void, SSL_SESSION*, &SSL_SESSION_free>> SessionStatePointer;
#elif USE_GNUTLS
-// Locks can be implemented attaching locks counter to gnutls_session_t
-// objects using the gnutls_session_set_ptr()/gnutls_session_get_ptr ()
-// library functions
-CtoCpp1(gnutls_deinit, gnutls_session_t);
-typedef Security::LockingPointer<struct gnutls_session_int, gnutls_deinit_cpp> SessionPointer;
+typedef std::shared_ptr<struct gnutls_session_int> SessionPointer;
// wrapper function to get around gnutls_free being a typedef
inline void squid_gnutls_free(void *d) {gnutls_free(d);}
typedef std::unique_ptr<gnutls_datum_t, HardFun<void, void*, &Security::squid_gnutls_free>> SessionStatePointer;
#else
-// use void* so we can check against NULL
-CtoCpp1(xfree, void *);
-typedef Security::LockingPointer<void, xfree_cpp> SessionPointer;
+typedef std::shared_ptr<void> SessionPointer;
typedef std::unique_ptr<int> SessionStatePointer;
#endif
-/// close an active TLS session
-void SessionClose(const Security::SessionPointer &);
+/// close an active TLS session.
+/// set fdOnError to the connection FD when the session is being closed
+/// due to an encryption error, otherwise omit.
+void SessionClose(const Security::SessionPointer &, int fdOnError = -1);
/// whether the session is a resumed one
bool SessionIsResumed(const Security::SessionPointer &);