Hi all,
This simple patch provide %D details for all SSL errors documented at
http://www.openssl.org/docs/apps/verify.html
This patch also adds a std::map based structure to optimize the ssl
error description retrieval.
Regards,
Christos
This simple patch provide %D details for all errors documented at
http://www.openssl.org/docs/apps/verify.html
Also this patch add a std::map structure to optimize the ssl error description
retrieval.
This is a Measurement Factory project
=== modified file 'src/ssl/ErrorDetail.cc'
--- src/ssl/ErrorDetail.cc 2011-03-17 00:12:57 +0000
+++ src/ssl/ErrorDetail.cc 2011-03-24 10:27:00 +0000
@@ -1,112 +1,253 @@
#include "squid.h"
#include "ssl/ErrorDetail.h"
+#if HAVE_MAP
+#include <map>
+#endif
struct SslErrorDetailEntry {
Ssl::ssl_error_t value;
const char *name;
- const char *detail;
+ const char *detail; ///< for error page %D macro expansion; may contain macros
+ const char *descr; ///< short error description (for use in debug messages or error pages)
};
static const char *SslErrorDetailDefaultStr = "SSL certificate validation error (%err_name): %ssl_subject";
-// TODO: optimize by replacing with std::map or similar
-static SslErrorDetailEntry TheSslDetailMap[] = {
- { SQUID_X509_V_ERR_DOMAIN_MISMATCH,
- "SQUID_X509_V_ERR_DOMAIN_MISMATCH",
- "%err_name: The hostname you are connecting to (%H), does not match any of the Certificate valid names: %ssl_cn"},
- { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
- "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
- "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name" },
- { X509_V_ERR_CERT_NOT_YET_VALID,
- "X509_V_ERR_CERT_NOT_YET_VALID",
- "%err_name: SSL Certficate is not valid before: %ssl_notbefore" },
- { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
- "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
- "%err_name: SSL Certificate has invalid start date (the 'not before' field): %ssl_subject" },
- { X509_V_ERR_CERT_HAS_EXPIRED,
- "X509_V_ERR_CERT_HAS_EXPIRED",
- "%err_name: SSL Certificate expired on %ssl_notafter" },
- { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
- "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
- "%err_name: SSL Certificate has invalid expiration date (the 'not after' field): %ssl_subject" },
+//Use std::map to optimize search
+std::map<Ssl::ssl_error_t, SslErrorDetailEntry *> TheSslDetailMap;
+
+static SslErrorDetailEntry TheSslDetailArray[] = {
+ {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
+ "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
+ "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name",
+ "Unable to get issuer certificate"},
+ {X509_V_ERR_UNABLE_TO_GET_CRL,
+ "X509_V_ERR_UNABLE_TO_GET_CRL",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Unable to get certificate CRL"},
+ {X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
+ "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Unable to decrypt certificate's signature"},
+ {X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
+ "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Unable to decrypt CRL's signature"},
+ {X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
+ "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
+ "%err_name: Unable to decode issuer (CA) public key: %ssl_ca_name",
+ "Unable to decode issuer public key"},
+ {X509_V_ERR_CERT_SIGNATURE_FAILURE,
+ "X509_V_ERR_CERT_SIGNATURE_FAILURE",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Certificate signature failure"},
+ {X509_V_ERR_CRL_SIGNATURE_FAILURE,
+ "X509_V_ERR_CRL_SIGNATURE_FAILURE",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "CRL signature failure"},
+ {X509_V_ERR_CERT_NOT_YET_VALID,
+ "X509_V_ERR_CERT_NOT_YET_VALID",
+ "%err_name: SSL Certficate is not valid before: %ssl_notbefore",
+ "Certificate is not yet valid"},
+ {X509_V_ERR_CERT_HAS_EXPIRED,
+ "X509_V_ERR_CERT_HAS_EXPIRED",
+ "%err_name: SSL Certificate expired on: %ssl_notafter",
+ "Certificate has expired"},
+ {X509_V_ERR_CRL_NOT_YET_VALID,
+ "X509_V_ERR_CRL_NOT_YET_VALID",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "CRL is not yet valid"},
+ {X509_V_ERR_CRL_HAS_EXPIRED,
+ "X509_V_ERR_CRL_HAS_EXPIRED",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "CRL has expired"},
+ {X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
+ "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
+ "%err_name: SSL Certificate has invalid start date (the 'not before' field): %ssl_subject",
+ "Format error in certificate's notBefore field"},
+ {X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
+ "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
+ "%err_name: SSL Certificate has invalid expiration date (the 'not after' field): %ssl_subject",
+ "Format error in certificate's notAfter field"},
+ {X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
+ "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Format error in CRL's lastUpdate field"},
+ {X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
+ "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Format error in CRL's nextUpdate field"},
+ {X509_V_ERR_OUT_OF_MEM,
+ "X509_V_ERR_OUT_OF_MEM",
+ "%err_name: %ssl_error_descr",
+ "Out of memory"},
{X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
"X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",
- "%err_name: Self-signed SSL Certificate: %ssl_subject"},
- { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
- "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
- "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name" },
- { SSL_ERROR_NONE, "SSL_ERROR_NONE", "%err_name: No error" },
- {SSL_ERROR_NONE, NULL, NULL }
+ "%err_name: Self-signed SSL Certificate: %ssl_subject",
+ "Self signed certificate"},
+ {X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
+ "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN",
+ "%err_name: Self-signed SSL Certificate in chain: %ssl_subject",
+ "Self signed certificate in certificate chain"},
+ {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
+ "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
+ "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name",
+ "Unable to get local issuer certificate"},
+ {X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
+ "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Unable to verify the first certificate"},
+ {X509_V_ERR_CERT_CHAIN_TOO_LONG,
+ "X509_V_ERR_CERT_CHAIN_TOO_LONG",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Certificate chain too long"},
+ {X509_V_ERR_CERT_REVOKED,
+ "X509_V_ERR_CERT_REVOKED",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Certificate revoked"},
+ {X509_V_ERR_INVALID_CA,
+ "X509_V_ERR_INVALID_CA",
+ "%err_name: %ssl_error_descr: %ssl_ca_name",
+ "Invalid CA certificate"},
+ {X509_V_ERR_PATH_LENGTH_EXCEEDED,
+ "X509_V_ERR_PATH_LENGTH_EXCEEDED",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Path length constraint exceeded"},
+ {X509_V_ERR_INVALID_PURPOSE,
+ "X509_V_ERR_INVALID_PURPOSE",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Unsupported certificate purpose"},
+ {X509_V_ERR_CERT_UNTRUSTED,
+ "X509_V_ERR_CERT_UNTRUSTED",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Certificate not trusted"},
+ {X509_V_ERR_CERT_REJECTED,
+ "X509_V_ERR_CERT_REJECTED",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Certificate rejected"},
+ {X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
+ "X509_V_ERR_SUBJECT_ISSUER_MISMATCH",
+ "%err_name: %ssl_error_descr: %ssl_ca_name",
+ "Subject issuer mismatch"},
+ {X509_V_ERR_AKID_SKID_MISMATCH,
+ "X509_V_ERR_AKID_SKID_MISMATCH",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Authority and subject key identifier mismatch"},
+ {X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
+ "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH",
+ "%err_name: %ssl_error_descr: %ssl_ca_name",
+ "Authority and issuer serial number mismatch"},
+ {X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
+ "X509_V_ERR_KEYUSAGE_NO_CERTSIGN",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Key usage does not include certificate signing"},
+ {X509_V_ERR_APPLICATION_VERIFICATION,
+ "X509_V_ERR_APPLICATION_VERIFICATION",
+ "%err_name: %ssl_error_descr: %ssl_subject",
+ "Application verification failure"},
+ { SSL_ERROR_NONE, "SSL_ERROR_NONE", "%err_name: No error", "No error" },
+ {SSL_ERROR_NONE, NULL, NULL, NULL }
};
+void loadSslDetailMap()
+{
+ assert(TheSslDetailMap.empty());
+ for (int i = 0; TheSslDetailArray[i].name; ++i) {
+ TheSslDetailMap[TheSslDetailArray[i].value] = &TheSslDetailArray[i];
+ }
+}
+
Ssl::ssl_error_t
Ssl::parseErrorString(const char *name)
{
assert(name);
- for (int i = 0; TheSslDetailMap[i].name; ++i) {
- if (strcmp(name, TheSslDetailMap[i].name) == 0)
- return TheSslDetailMap[i].value;
+ if (TheSslDetailMap.empty())
+ loadSslDetailMap();
+
+ std::map<Ssl::ssl_error_t, SslErrorDetailEntry *>::iterator it;
+ for (it=TheSslDetailMap.begin(); it != TheSslDetailMap.end(); ++it) {
+ if (strcmp(name, it->second->name) == 0)
+ return it->second->value;
}
if (xisdigit(*name)) {
const long int value = strtol(name, NULL, 0);
if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX)
return value;
fatalf("Too small or too bug SSL error code '%s'", name);
}
fatalf("Unknown SSL error name '%s'", name);
return SSL_ERROR_SSL; // not reached
}
+static SslErrorDetailEntry *getErrorRecord(Ssl::ssl_error_t value)
+{
+ if (TheSslDetailMap.empty())
+ loadSslDetailMap();
+
+ std::map<Ssl::ssl_error_t, SslErrorDetailEntry *>::iterator it = TheSslDetailMap.find(value);
+ if (it != TheSslDetailMap.end())
+ return it->second;
+
+ return NULL;
+}
+
+//TODO: capitalize globals
const char *
Ssl::getErrorName(Ssl::ssl_error_t value)
{
-
- for (int i = 0; TheSslDetailMap[i].name; ++i) {
- if (TheSslDetailMap[i].value == value)
- return TheSslDetailMap[i].name;
- }
+ if (SslErrorDetailEntry *errorRecord = getErrorRecord(value))
+ return errorRecord->name;
return NULL;
}
static const char *getErrorDetail(Ssl::ssl_error_t value)
{
- for (int i = 0; TheSslDetailMap[i].name; ++i) {
- if (TheSslDetailMap[i].value == value)
- return TheSslDetailMap[i].detail;
- }
+ if (SslErrorDetailEntry *errorRecord = getErrorRecord(value))
+ return errorRecord->detail;
// we must always return something because ErrorDetail::buildDetail
// will hit an assertion
return SslErrorDetailDefaultStr;
}
+//TODO: capitalize globals
+const char *
+Ssl::getErrorDescr(Ssl::ssl_error_t value)
+{
+ if (SslErrorDetailEntry *errorRecord = getErrorRecord(value))
+ return errorRecord->descr;
+
+ return NULL;
+}
+
Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
{"ssl_subject", &Ssl::ErrorDetail::subject},
{"ssl_ca_name", &Ssl::ErrorDetail::ca_name},
{"ssl_cn", &Ssl::ErrorDetail::cn},
{"ssl_notbefore", &Ssl::ErrorDetail::notbefore},
{"ssl_notafter", &Ssl::ErrorDetail::notafter},
{"err_name", &Ssl::ErrorDetail::err_code},
+ {"ssl_error_descr", &Ssl::ErrorDetail::err_descr},
{NULL,NULL}
};
/**
* The subject of the current certification in text form
*/
const char *Ssl::ErrorDetail::subject() const
{
if (!peer_cert)
return "[Not available]";
static char tmpBuffer[256]; // A temporary buffer
X509_NAME_oneline(X509_get_subject_name(peer_cert.get()), tmpBuffer,
sizeof(tmpBuffer));
return tmpBuffer;
}
// helper function to be used with Ssl::matchX509CommonNames
static int copy_cn(void *check_data, ASN1_STRING *cn_data)
{
@@ -172,43 +313,56 @@
ASN1_UTCTIME * tm = X509_get_notAfter(peer_cert.get());
Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
return tmpBuffer;
}
/**
* The string representation of the error_no
*/
const char *Ssl::ErrorDetail::err_code() const
{
static char tmpBuffer[64];
const char *err = getErrorName(error_no);
if (!err) {
snprintf(tmpBuffer, 64, "%d", (int)error_no);
err = tmpBuffer;
}
return err;
}
/**
+ * A short description of the error_no
+ */
+const char *Ssl::ErrorDetail::err_descr() const
+{
+ const char *err = getErrorDescr(error_no);
+ if (!err)
+ return "[Not available]";
+ else
+ return err;
+}
+
+/**
* It converts the code to a string value. Currently the following
* formating codes are supported:
* %err_name: The name of the SSL error
+ * %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
\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
{
*value = "-";
for (int i=0; ErrorFormatingCodes[i].code!=NULL; i++) {
const int len = strlen(ErrorFormatingCodes[i].code);
if (strncmp(code,ErrorFormatingCodes[i].code, len)==0) {
ErrorDetail::fmt_action_t action = ErrorFormatingCodes[i].fmt_action;
*value = (this->*action)();
return len;
}
}
return 0;
}
=== modified file 'src/ssl/ErrorDetail.h'
--- src/ssl/ErrorDetail.h 2010-12-14 12:17:34 +0000
+++ src/ssl/ErrorDetail.h 2011-03-24 09:50:10 +0000
@@ -17,60 +17,67 @@
namespace Ssl
{
/// Squid defined error code (<0), an error code returned by SSL X509 api, or SSL_ERROR_NONE
typedef int ssl_error_t;
/**
\ingroup ServerProtocolSSLAPI
* The ssl_error_t representation of the error described by "name".
*/
ssl_error_t parseErrorString(const char *name);
/**
\ingroup ServerProtocolSSLAPI
* The string representation of the SSL error "value"
*/
const char *getErrorName(ssl_error_t value);
/**
\ingroup ServerProtocolSSLAPI
+ * A short description of the SSL error "value"
+ */
+const char *getErrorDescr(ssl_error_t value);
+
+/**
+ \ingroup ServerProtocolSSLAPI
* Used to pass SSL error details to the error pages returned to the
* end user.
*/
class ErrorDetail
{
public:
ErrorDetail(ssl_error_t err_no, X509 *cert);
ErrorDetail(ErrorDetail const &);
const String &toString() const; ///< An error detail string to embed in squid error pages
private:
typedef const char * (ErrorDetail::*fmt_action_t)() const;
/**
* Holds a formating code and its conversion method
*/
class err_frm_code
{
public:
const char *code; ///< The formating code
fmt_action_t fmt_action; ///< A pointer to the conversion method
};
static err_frm_code ErrorFormatingCodes[]; ///< The supported formating codes
const char *subject() const;
const char *ca_name() const;
const char *cn() const;
const char *notbefore() const;
const char *notafter() const;
const char *err_code() const;
+ const char *err_descr() 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
X509_Pointer peer_cert; ///< A pointer to the peer certificate
};
}//namespace Ssl
#endif
=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc 2010-12-14 12:17:34 +0000
+++ src/ssl/support.cc 2011-03-24 10:17:09 +0000
@@ -216,73 +216,44 @@
X509_NAME_oneline(X509_get_subject_name(peer_cert), buffer,
sizeof(buffer));
if (ok) {
debugs(83, 5, "SSL Certificate signature OK: " << buffer);
if (server) {
int found = Ssl::matchX509CommonNames(peer_cert, (void *)server, check_domain);
if (!found) {
debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server);
ok = 0;
error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
if (check)
Filled(check)->ssl_error = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
}
}
} else {
error_no = ctx->error;
- switch (ctx->error) {
-
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
- debugs(83, 5, "SSL Certficate error: CA not known: " << buffer);
- break;
-
- case X509_V_ERR_CERT_NOT_YET_VALID:
- debugs(83, 5, "SSL Certficate not yet valid: " << buffer);
- break;
-
- case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
- debugs(83, 5, "SSL Certificate has illegal \'not before\' field: " <<
- buffer);
-
- break;
-
- case X509_V_ERR_CERT_HAS_EXPIRED:
- debugs(83, 5, "SSL Certificate expired: " << buffer);
- break;
-
- case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
- debugs(83, 5, "SSL Certificate has invalid \'not after\' field: " << buffer);
- break;
-
- case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- debugs(83, 5, "SSL Certificate is self signed: " << buffer);
- break;
-
- default:
+ if (const char *err_descr = Ssl::getErrorDescr(ctx->error))
+ debugs(83, 5, err_descr << ": " << buffer);
+ else
debugs(83, 1, "SSL unknown certificate error " << ctx->error << " in " << buffer);
- break;
- }
if (check)
Filled(check)->ssl_error = ctx->error;
}
if (!ok && check) {
if (check->fastCheck()) {
debugs(83, 3, "bypassing SSL error " << ctx->error << " in " << buffer);
ok = 1;
} else {
debugs(83, 5, "confirming SSL error " << ctx->error);
}
}
if (!dont_verify_domain && server) {}
if (error_no != SSL_ERROR_NONE && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(error_no, peer_cert);
if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_error_detail, errDetail)) {
debugs(83, 2, "Failed to set Ssl::ErrorDetail in ssl_verify_cb: Certificate " << buffer);