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);

Reply via email to