/*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
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;
}
bool
Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
{
-#if USE_OPENSSL
Security::ContextPointer ctx(getTlsContext());
- assert(ctx);
+ debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
- if (!Ssl::CreateClient(ctx, serverConnection(), "server https start")) {
+ if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
const auto xerrno = errno;
- const auto ssl_error = ERR_get_error();
+ if (!ctx) {
+ debugs(83, DBG_IMPORTANT, "Error initializing TLS connection: No security context.");
+ } // else CreateClientSession() did the appropriate debugs() already
ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw());
anErr->xerrno = xerrno;
- debugs(83, DBG_IMPORTANT, "Error allocating TLS handle: " << Security::ErrorString(ssl_error));
noteNegotiationDone(anErr);
bail(anErr);
return false;
// 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,
// but keep a list of errors to send it to CertValidator
if (!Ssl::TheConfig.ssl_crt_validator) {
SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
}
}
+#endif
return true;
-#else
- return false;
-#endif
}
void
#if USE_OPENSSL
// retrieve TLS parsed extra info
BIO *b = SSL_get_rbio(session.get());
- Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(b->ptr);
+ Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails())
serverConnection()->tlsNegotiations()->retrieveParsedInfo(details);
#endif
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
+ auto session = fd_table[fd].ssl.get();
+ const int result = gnutls_handshake(session);
+ debugs(83, 5, "gnutls_handshake session=" << (void*)session << ", result=" << result);
+
+ if (result == GNUTLS_E_SUCCESS) {
+ char *desc = gnutls_session_get_desc(session);
+ debugs(83, 2, serverConnection() << " TLS Session info: " << desc);
+ gnutls_free(desc);
+ }
+
+ if (result != GNUTLS_E_SUCCESS) {
+ // debug the TLS session state so far
+ auto descIn = gnutls_handshake_get_last_in(session);
+ debugs(83, 2, "handshake IN: " << gnutls_handshake_description_get_name(descIn));
+ auto descOut = gnutls_handshake_get_last_out(session);
+ debugs(83, 2, "handshake OUT: " << gnutls_handshake_description_get_name(descOut));
#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;
}
+#elif USE_GNUTLS
+ 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)
+ noteWantRead();
+ else
+ noteWantWrite();
+ 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);
-#endif
}
void
Security::PeerConnector::noteWantRead()
{
const int fd = serverConnection()->fd;
+ debugs(83, 5, serverConnection());
#if USE_OPENSSL
Security::SessionPointer session(fd_table[fd].ssl);
BIO *b = SSL_get_rbio(session.get());
- Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+ Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
if (srvBio->holdRead()) {
if (srvBio->gotHello()) {
if (checkForMissingCertificates())
Security::PeerConnector::noteWantWrite()
{
const int fd = serverConnection()->fd;
+ debugs(83, 5, serverConnection());
Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
return;
}
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
+ int xerr = errno;
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 << ")");
+ ssl_error << "/" << ret << "/" << xerr << ")");
- ErrorState *anErr = NULL;
- if (request != NULL)
- anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request.getRaw());
- else
- anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, NULL);
+ ErrorState *anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request);
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
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.
PeerConnectorCertDownloaderDialer(&Security::PeerConnector::certDownloadingDone, this));
const Downloader *csd = (request ? dynamic_cast<const Downloader*>(request->downloader.valid()) : nullptr);
- Downloader *dl = new Downloader(url, certCallback, csd ? csd->nestedLevel() + 1 : 1);
+ Downloader *dl = new Downloader(url, certCallback, XactionInitiator::initCertFetcher, csd ? csd->nestedLevel() + 1 : 1);
AsyncJob::Start(dl);
}
const int fd = serverConnection()->fd;
Security::SessionPointer session(fd_table[fd].ssl);
BIO *b = SSL_get_rbio(session.get());
- Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+ Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
// Parse Certificate. Assume that it is in DER format.
// According to RFC 4325:
const int fd = serverConnection()->fd;
Security::SessionPointer session(fd_table[fd].ssl);
BIO *b = SSL_get_rbio(session.get());
- Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+ Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
const Security::CertList &certs = srvBio->serverCertificatesIfAny();
if (certs.size()) {