++name: SQUID_ERR_SSL_HANDSHAKE
++detail: "%ssl_error_descr: %ssl_lib_error"
++descr: "Handshake with SSL server failed"
++
name: SQUID_X509_V_ERR_DOMAIN_MISMATCH
detail: "%ssl_error_descr: %ssl_subject"
descr: "Certificate does not match domainname"
void
FwdState::negotiateSSL(int fd)
{
++ unsigned long ssl_lib_error = SSL_ERROR_NONE;
SSL *ssl = fd_table[fd].ssl;
int ret;
if ((ret = SSL_connect(ssl)) <= 0) {
int ssl_error = SSL_get_error(ssl, ret);
++#ifdef EPROTO
++ int sysErrNo = EPROTO;
++#else
++ int sysErrNo = EACCES;
++#endif
switch (ssl_error) {
Comm::SetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
return;
-- default:
++ case SSL_ERROR_SSL:
++ case SSL_ERROR_SYSCALL:
++ ssl_lib_error = ERR_get_error();
debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd <<
-- ": " << ERR_error_string(ERR_get_error(), NULL) << " (" << ssl_error <<
++ ": " << ERR_error_string(ssl_lib_error, NULL) << " (" << ssl_error <<
"/" << ret << "/" << errno << ")");
-- ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
--#ifdef EPROTO
-- anErr->xerrno = EPROTO;
--#else
++ // 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;
-- anErr->xerrno = EACCES;
--#endif
++ // falling through to complete error handling
++
++ default:
++ ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
++ anErr->xerrno = sysErrNo;
Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail);
if (errFromFailure != NULL) {
// Copy errFromFailure to a new Ssl::ErrorDetail object
anErr->detail = new Ssl::ErrorDetail(*errFromFailure);
}
++ else {
++ // clientCert can be be NULL
++ X509 *client_cert = SSL_get_peer_certificate(ssl);
++ anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, client_cert);
++ if (client_cert)
++ X509_free(client_cert);
++ }
++
++ if (ssl_lib_error != SSL_ERROR_NONE)
++ anErr->detail->setLibError(ssl_lib_error);
fail(anErr);
const char *name;
};
--static const char *SslErrorDetailDefaultStr = "SSL certificate validation error (%err_name): %ssl_subject";
++static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)";
//Use std::map to optimize search
typedef std::map<Ssl::ssl_error_t, const SslErrorEntry *> SslErrors;
SslErrors TheSslErrors;
static SslErrorEntry TheSslErrorArray[] = {
++ {SQUID_ERR_SSL_HANDSHAKE,
++ "SQUID_ERR_SSL_HANDSHAKE"},
{SQUID_X509_V_ERR_DOMAIN_MISMATCH,
"SQUID_X509_V_ERR_DOMAIN_MISMATCH"},
{X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
{"ssl_notafter", &Ssl::ErrorDetail::notafter},
{"err_name", &Ssl::ErrorDetail::err_code},
{"ssl_error_descr", &Ssl::ErrorDetail::err_descr},
++ {"ssl_lib_error", &Ssl::ErrorDetail::err_lib_error},
{NULL,NULL}
};
return "[Not available]";
}
++const char *Ssl::ErrorDetail::err_lib_error() const
++{
++ if (lib_error_no != SSL_ERROR_NONE)
++ return ERR_error_string(lib_error_no, NULL);
++ else
++ return "[No Error]";
++}
++
/**
* It converts the code to a string value. Currently the following
* formating codes are supported:
-- * %err_name: The name of the SSL error
++ * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*)
* %ssl_error_descr: A short description of the SSL error
* %ssl_cn: The comma-separated list of common and alternate names
* %ssl_subject: The certificate subject
* %ssl_ca_name: The certificate issuer name
* %ssl_notbefore: The certificate "not before" field
* %ssl_notafter: The certificate "not after" field
++ * %ssl_lib_error: human-readable low-level error string by ERR_error_string(3SSL)
\retval the length of the code (the number of characters will be replaced by value)
*/
int Ssl::ErrorDetail::convert(const char *code, const char **value) const
CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
peer_cert.reset(cert);
*/
--Ssl::ErrorDetail::ErrorDetail( Ssl::ssl_error_t err_no, X509 *cert): error_no (err_no)
++Ssl::ErrorDetail::ErrorDetail( Ssl::ssl_error_t err_no, X509 *cert): error_no (err_no), lib_error_no(SSL_ERROR_NONE)
{
-- peer_cert.reset(X509_dup(cert));
++ if (cert)
++ peer_cert.reset(X509_dup(cert));
++
detailEntry.error_no = SSL_ERROR_NONE;
}
}
detailEntry = anErrDetail.detailEntry;
++
++ lib_error_no = anErrDetail.lib_error_no;
}
void useRequest(HttpRequest *aRequest) { if (aRequest != NULL) request = aRequest;}
/// The error name to embed in squid error pages
const char *errorName() const {return err_code();}
++ ///Sets the low-level error returned by OpenSSL ERR_get_error()
++ void setLibError(unsigned long lib_err_no) {lib_error_no = lib_err_no;}
private:
typedef const char * (ErrorDetail::*fmt_action_t)() const;
const char *notafter() const;
const char *err_code() const;
const char *err_descr() const;
++ const char *err_lib_error() const;
int convert(const char *code, const char **value) const;
void buildDetail() const;
mutable String errDetailStr; ///< Caches the error detail message
ssl_error_t error_no; ///< The error code
++ unsigned long lib_error_no; ///< low-level error returned by OpenSSL ERR_get_error(3SSL)
X509_Pointer peer_cert; ///< A pointer to the peer certificate
mutable ErrorDetailEntry detailEntry;
HttpRequest::Pointer request;
*/
// Custom SSL errors; assumes all official errors are positive
++#define SQUID_ERR_SSL_HANDSHAKE -2
#define SQUID_X509_V_ERR_DOMAIN_MISMATCH -1
// All SSL errors range: from smallest (negative) custom to largest SSL error
--#define SQUID_SSL_ERROR_MIN SQUID_X509_V_ERR_DOMAIN_MISMATCH
++#define SQUID_SSL_ERROR_MIN SQUID_ERR_SSL_HANDSHAKE
#define SQUID_SSL_ERROR_MAX INT_MAX
namespace Ssl