]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/ssl/ErrorDetail.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ssl / ErrorDetail.cc
index 27190a5e58ee991ba2c474c3436e651688b37aa9..b42548bf8583b3adbb74daf0908d50d2042e8b0c 100644 (file)
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
 #include "squid.h"
+#include "errorpage.h"
+#include "fatal.h"
 #include "ssl/ErrorDetail.h"
-#if HAVE_MAP
+
+#include <climits>
 #include <map>
-#endif
 
-struct SslErrorDetailEntry {
+struct SslErrorEntry {
     Ssl::ssl_error_t value;
     const char *name;
-    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";
+static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)";
 //Use std::map to optimize search
-typedef std::map<Ssl::ssl_error_t, const SslErrorDetailEntry *> SslErrorDetails;
-SslErrorDetails TheSslDetail;
-
-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",
-     "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 }
+typedef std::map<Ssl::ssl_error_t, const SslErrorEntry *> SslErrors;
+SslErrors TheSslErrors;
+
+static SslErrorEntry TheSslErrorArray[] = {
+    {   SQUID_X509_V_ERR_INFINITE_VALIDATION,
+        "SQUID_X509_V_ERR_INFINITE_VALIDATION"
+    },
+    {   SQUID_X509_V_ERR_CERT_CHANGE,
+        "SQUID_X509_V_ERR_CERT_CHANGE"
+    },
+    {   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,
+        "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"
+    },
+    {   X509_V_ERR_UNABLE_TO_GET_CRL,
+        "X509_V_ERR_UNABLE_TO_GET_CRL"
+    },
+    {   X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
+        "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"
+    },
+    {   X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
+        "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"
+    },
+    {   X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
+        "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"
+    },
+    {   X509_V_ERR_CERT_SIGNATURE_FAILURE,
+        "X509_V_ERR_CERT_SIGNATURE_FAILURE"
+    },
+    {   X509_V_ERR_CRL_SIGNATURE_FAILURE,
+        "X509_V_ERR_CRL_SIGNATURE_FAILURE"
+    },
+    {   X509_V_ERR_CERT_NOT_YET_VALID,
+        "X509_V_ERR_CERT_NOT_YET_VALID"
+    },
+    {   X509_V_ERR_CERT_HAS_EXPIRED,
+        "X509_V_ERR_CERT_HAS_EXPIRED"
+    },
+    {   X509_V_ERR_CRL_NOT_YET_VALID,
+        "X509_V_ERR_CRL_NOT_YET_VALID"
+    },
+    {   X509_V_ERR_CRL_HAS_EXPIRED,
+        "X509_V_ERR_CRL_HAS_EXPIRED"
+    },
+    {   X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
+        "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"
+    },
+    {   X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
+        "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"
+    },
+    {   X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
+        "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"
+    },
+    {   X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
+        "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"
+    },
+    {   X509_V_ERR_OUT_OF_MEM,
+        "X509_V_ERR_OUT_OF_MEM"
+    },
+    {   X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
+        "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
+    },
+    {   X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
+        "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"
+    },
+    {   X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
+        "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
+    },
+    {   X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
+        "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
+    },
+    {   X509_V_ERR_CERT_CHAIN_TOO_LONG,
+        "X509_V_ERR_CERT_CHAIN_TOO_LONG"
+    },
+    {   X509_V_ERR_CERT_REVOKED,
+        "X509_V_ERR_CERT_REVOKED"
+    },
+    {   X509_V_ERR_INVALID_CA,
+        "X509_V_ERR_INVALID_CA"
+    },
+    {   X509_V_ERR_PATH_LENGTH_EXCEEDED,
+        "X509_V_ERR_PATH_LENGTH_EXCEEDED"
+    },
+    {   X509_V_ERR_INVALID_PURPOSE,
+        "X509_V_ERR_INVALID_PURPOSE"
+    },
+    {   X509_V_ERR_CERT_UNTRUSTED,
+        "X509_V_ERR_CERT_UNTRUSTED"
+    },
+    {   X509_V_ERR_CERT_REJECTED,
+        "X509_V_ERR_CERT_REJECTED"
+    },
+    {   X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
+        "X509_V_ERR_SUBJECT_ISSUER_MISMATCH"
+    },
+    {   X509_V_ERR_AKID_SKID_MISMATCH,
+        "X509_V_ERR_AKID_SKID_MISMATCH"
+    },
+    {   X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
+        "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"
+    },
+    {   X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
+        "X509_V_ERR_KEYUSAGE_NO_CERTSIGN"
+    },
+#if defined(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER)
+    {
+        X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, //33
+        "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER"
+    },
+#endif
+#if defined(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
+    {
+        X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, //34
+        "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION"
+    },
+#endif
+#if defined(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN)
+    {
+        X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, //35
+        "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN"
+    },
+#endif
+#if defined(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION)
+    {
+        X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, //36
+        "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION"
+    },
+#endif
+#if defined(X509_V_ERR_INVALID_NON_CA)
+    {
+        X509_V_ERR_INVALID_NON_CA, //37
+        "X509_V_ERR_INVALID_NON_CA"
+    },
+#endif
+#if defined(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED)
+    {
+        X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, //38
+        "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
+    },
+#endif
+#if defined(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE)
+    {
+        X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, //39
+        "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE"
+    },
+#endif
+#if defined(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED)
+    {
+        X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, //40
+        "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED"
+    },
+#endif
+#if defined(X509_V_ERR_INVALID_EXTENSION)
+    {
+        X509_V_ERR_INVALID_EXTENSION, //41
+        "X509_V_ERR_INVALID_EXTENSION"
+    },
+#endif
+#if defined(X509_V_ERR_INVALID_POLICY_EXTENSION)
+    {
+        X509_V_ERR_INVALID_POLICY_EXTENSION, //42
+        "X509_V_ERR_INVALID_POLICY_EXTENSION"
+    },
+#endif
+#if defined(X509_V_ERR_NO_EXPLICIT_POLICY)
+    {
+        X509_V_ERR_NO_EXPLICIT_POLICY, //43
+        "X509_V_ERR_NO_EXPLICIT_POLICY"
+    },
+#endif
+#if defined(X509_V_ERR_DIFFERENT_CRL_SCOPE)
+    {
+        X509_V_ERR_DIFFERENT_CRL_SCOPE, //44
+        "X509_V_ERR_DIFFERENT_CRL_SCOPE"
+    },
+#endif
+#if defined(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE)
+    {
+        X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, //45
+        "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE"
+    },
+#endif
+#if defined(X509_V_ERR_UNNESTED_RESOURCE)
+    {
+        X509_V_ERR_UNNESTED_RESOURCE, //46
+        "X509_V_ERR_UNNESTED_RESOURCE"
+    },
+#endif
+#if defined(X509_V_ERR_PERMITTED_VIOLATION)
+    {
+        X509_V_ERR_PERMITTED_VIOLATION, //47
+        "X509_V_ERR_PERMITTED_VIOLATION"
+    },
+#endif
+#if defined(X509_V_ERR_EXCLUDED_VIOLATION)
+    {
+        X509_V_ERR_EXCLUDED_VIOLATION, //48
+        "X509_V_ERR_EXCLUDED_VIOLATION"
+    },
+#endif
+#if defined(X509_V_ERR_SUBTREE_MINMAX)
+    {
+        X509_V_ERR_SUBTREE_MINMAX, //49
+        "X509_V_ERR_SUBTREE_MINMAX"
+    },
+#endif
+#if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE)
+    {
+        X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, //51
+        "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE"
+    },
+#endif
+#if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX)
+    {
+        X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, //52
+        "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX"
+    },
+#endif
+#if defined(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX)
+    {
+        X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, //53
+        "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX"
+    },
+#endif
+#if defined(X509_V_ERR_CRL_PATH_VALIDATION_ERROR)
+    {
+        X509_V_ERR_CRL_PATH_VALIDATION_ERROR, //54
+        "X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
+    },
+#endif
+    {   X509_V_ERR_APPLICATION_VERIFICATION,
+        "X509_V_ERR_APPLICATION_VERIFICATION"
+    },
+    { SSL_ERROR_NONE, "SSL_ERROR_NONE"},
+    {SSL_ERROR_NONE, NULL}
+};
+
+static const char *OptionalSslErrors[] = {
+    "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER",
+    "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION",
+    "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN",
+    "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION",
+    "X509_V_ERR_INVALID_NON_CA",
+    "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED",
+    "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE",
+    "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED",
+    "X509_V_ERR_INVALID_EXTENSION",
+    "X509_V_ERR_INVALID_POLICY_EXTENSION",
+    "X509_V_ERR_NO_EXPLICIT_POLICY",
+    "X509_V_ERR_DIFFERENT_CRL_SCOPE",
+    "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE",
+    "X509_V_ERR_UNNESTED_RESOURCE",
+    "X509_V_ERR_PERMITTED_VIOLATION",
+    "X509_V_ERR_EXCLUDED_VIOLATION",
+    "X509_V_ERR_SUBTREE_MINMAX",
+    "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE",
+    "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX",
+    "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX",
+    "X509_V_ERR_CRL_PATH_VALIDATION_ERROR",
+    NULL
 };
 
-static void loadSslDetailMap()
+struct SslErrorAlias {
+    const char *name;
+    const Ssl::ssl_error_t *errors;
+};
+
+static const Ssl::ssl_error_t hasExpired[] = {X509_V_ERR_CERT_HAS_EXPIRED, SSL_ERROR_NONE};
+static const Ssl::ssl_error_t notYetValid[] = {X509_V_ERR_CERT_NOT_YET_VALID, SSL_ERROR_NONE};
+static const Ssl::ssl_error_t domainMismatch[] = {SQUID_X509_V_ERR_DOMAIN_MISMATCH, SSL_ERROR_NONE};
+static const Ssl::ssl_error_t certUntrusted[] = {X509_V_ERR_INVALID_CA,
+                                                 X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
+                                                 X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
+                                                 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
+                                                 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
+                                                 X509_V_ERR_CERT_UNTRUSTED, SSL_ERROR_NONE
+                                                };
+static const Ssl::ssl_error_t certSelfSigned[] = {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, SSL_ERROR_NONE};
+
+// The list of error name shortcuts  for use with ssl_error acls.
+// The keys without the "ssl::" scope prefix allow shorter error
+// names within the SSL options scope. This is easier than
+// carefully stripping the scope prefix in Ssl::ParseErrorString().
+static SslErrorAlias TheSslErrorShortcutsArray[] = {
+    {"ssl::certHasExpired", hasExpired},
+    {"certHasExpired", hasExpired},
+    {"ssl::certNotYetValid", notYetValid},
+    {"certNotYetValid", notYetValid},
+    {"ssl::certDomainMismatch", domainMismatch},
+    {"certDomainMismatch", domainMismatch},
+    {"ssl::certUntrusted", certUntrusted},
+    {"certUntrusted", certUntrusted},
+    {"ssl::certSelfSigned", certSelfSigned},
+    {"certSelfSigned", certSelfSigned},
+    {NULL, NULL}
+};
+
+// Use std::map to optimize search.
+typedef std::map<std::string, const Ssl::ssl_error_t *> SslErrorShortcuts;
+SslErrorShortcuts TheSslErrorShortcuts;
+
+static void loadSslErrorMap()
+{
+    assert(TheSslErrors.empty());
+    for (int i = 0; TheSslErrorArray[i].name; ++i) {
+        TheSslErrors[TheSslErrorArray[i].value] = &TheSslErrorArray[i];
+    }
+}
+
+static void loadSslErrorShortcutsMap()
 {
-    assert(TheSslDetail.empty());
-    for (int i = 0; TheSslDetailArray[i].name; ++i) {
-        TheSslDetail[TheSslDetailArray[i].value] = &TheSslDetailArray[i];
+    assert(TheSslErrorShortcuts.empty());
+    for (int i = 0; TheSslErrorShortcutsArray[i].name; ++i)
+        TheSslErrorShortcuts[TheSslErrorShortcutsArray[i].name] = TheSslErrorShortcutsArray[i].errors;
+}
+
+Ssl::ssl_error_t Ssl::GetErrorCode(const char *name)
+{
+    //TODO: use a std::map?
+    for (int i = 0; TheSslErrorArray[i].name != NULL; ++i) {
+        if (strcmp(name, TheSslErrorArray[i].name) == 0)
+            return TheSslErrorArray[i].value;
     }
+    return SSL_ERROR_NONE;
 }
 
-Ssl::ssl_error_t
+Ssl::Errors *
 Ssl::ParseErrorString(const char *name)
 {
     assert(name);
 
-    if (TheSslDetail.empty())
-        loadSslDetailMap();
-
-    typedef SslErrorDetails::const_iterator SEDCI;
-    for (SEDCI i = TheSslDetail.begin(); i != TheSslDetail.end(); ++i) {
-        if (strcmp(name, i->second->name) == 0)
-            return i->second->value;
-    }
+    const Ssl::ssl_error_t ssl_error = GetErrorCode(name);
+    if (ssl_error != SSL_ERROR_NONE)
+        return new Ssl::Errors(ssl_error);
 
     if (xisdigit(*name)) {
         const long int value = strtol(name, NULL, 0);
         if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX)
-            return value;
+            return new Ssl::Errors(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 const SslErrorDetailEntry *getErrorRecord(Ssl::ssl_error_t value)
-{
-    if (TheSslDetail.empty())
-        loadSslDetailMap();
+    if (TheSslErrorShortcuts.empty())
+        loadSslErrorShortcutsMap();
 
-    const SslErrorDetails::const_iterator it = TheSslDetail.find(value);
-    if (it != TheSslDetail.end())
-        return it->second;
+    const SslErrorShortcuts::const_iterator it = TheSslErrorShortcuts.find(name);
+    if (it != TheSslErrorShortcuts.end()) {
+        // Should not be empty...
+        assert(it->second[0] != SSL_ERROR_NONE);
+        Ssl::Errors *errors = new Ssl::Errors(it->second[0]);
+        for (int i =1; it->second[i] != SSL_ERROR_NONE; ++i) {
+            errors->push_back_unique(it->second[i]);
+        }
+        return errors;
+    }
 
-    return NULL;
+    fatalf("Unknown SSL error name '%s'", name);
+    return NULL; // not reached
 }
 
-const char *
-Ssl::GetErrorName(Ssl::ssl_error_t value)
+const char *Ssl::GetErrorName(Ssl::ssl_error_t value)
 {
-    if (const SslErrorDetailEntry *errorRecord = getErrorRecord(value))
-        return errorRecord->name;
+    if (TheSslErrors.empty())
+        loadSslErrorMap();
+
+    const SslErrors::const_iterator it = TheSslErrors.find(value);
+    if (it != TheSslErrors.end())
+        return it->second->name;
 
     return NULL;
 }
 
-static const char *getErrorDetail(Ssl::ssl_error_t value)
+bool
+Ssl::ErrorIsOptional(const char *name)
 {
-    if (const SslErrorDetailEntry *errorRecord = getErrorRecord(value))
-        return errorRecord->detail;
-
-    // we must always return something because ErrorDetail::buildDetail
-    // will hit an assertion
-    return SslErrorDetailDefaultStr;
+    for (int i = 0; OptionalSslErrors[i] != NULL; ++i) {
+        if (strcmp(name, OptionalSslErrors[i]) == 0)
+            return true;
+    }
+    return false;
 }
 
 const char *
 Ssl::GetErrorDescr(Ssl::ssl_error_t value)
 {
-    if (const SslErrorDetailEntry *errorRecord = getErrorRecord(value))
-        return errorRecord->descr;
-
-    return NULL;
+    return ErrorDetailsManager::GetInstance().getDefaultErrorDescr(value);
 }
 
 Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
@@ -230,6 +422,7 @@ Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
     {"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}
 };
 
@@ -238,11 +431,11 @@ Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
  */
 const char  *Ssl::ErrorDetail::subject() const
 {
-    if (!peer_cert)
+    if (!broken_cert)
         return "[Not available]";
 
     static char tmpBuffer[256]; // A temporary buffer
-    X509_NAME_oneline(X509_get_subject_name(peer_cert.get()), tmpBuffer,
+    X509_NAME_oneline(X509_get_subject_name(broken_cert.get()), tmpBuffer,
                       sizeof(tmpBuffer));
     return tmpBuffer;
 }
@@ -253,7 +446,7 @@ static int copy_cn(void *check_data,  ASN1_STRING *cn_data)
     String *str = (String *)check_data;
     if (!str) // no data? abort
         return 0;
-    if (str->defined())
+    if (str->size() > 0)
         str->append(", ");
     str->append((const char *)cn_data->data, cn_data->length);
     return 1;
@@ -264,12 +457,12 @@ static int copy_cn(void *check_data,  ASN1_STRING *cn_data)
  */
 const char *Ssl::ErrorDetail::cn() const
 {
-    if (!peer_cert)
+    if (!broken_cert)
         return "[Not available]";
 
     static String tmpStr;  ///< A temporary string buffer
     tmpStr.clean();
-    Ssl::matchX509CommonNames(peer_cert.get(), &tmpStr, copy_cn);
+    Ssl::matchX509CommonNames(broken_cert.get(), &tmpStr, copy_cn);
     return tmpStr.termedBuf();
 }
 
@@ -278,11 +471,11 @@ const char *Ssl::ErrorDetail::cn() const
  */
 const char *Ssl::ErrorDetail::ca_name() const
 {
-    if (!peer_cert)
+    if (!broken_cert)
         return "[Not available]";
 
     static char tmpBuffer[256]; // A temporary buffer
-    X509_NAME_oneline(X509_get_issuer_name(peer_cert.get()), tmpBuffer, sizeof(tmpBuffer));
+    X509_NAME_oneline(X509_get_issuer_name(broken_cert.get()), tmpBuffer, sizeof(tmpBuffer));
     return tmpBuffer;
 }
 
@@ -291,11 +484,11 @@ const char *Ssl::ErrorDetail::ca_name() const
  */
 const char *Ssl::ErrorDetail::notbefore() const
 {
-    if (!peer_cert)
+    if (!broken_cert)
         return "[Not available]";
 
     static char tmpBuffer[256]; // A temporary buffer
-    ASN1_UTCTIME * tm = X509_get_notBefore(peer_cert.get());
+    ASN1_UTCTIME * tm = X509_get_notBefore(broken_cert.get());
     Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
     return tmpBuffer;
 }
@@ -305,11 +498,11 @@ const char *Ssl::ErrorDetail::notbefore() const
  */
 const char *Ssl::ErrorDetail::notafter() const
 {
-    if (!peer_cert)
+    if (!broken_cert)
         return "[Not available]";
 
     static char tmpBuffer[256]; // A temporary buffer
-    ASN1_UTCTIME * tm = X509_get_notAfter(peer_cert.get());
+    ASN1_UTCTIME * tm = X509_get_notAfter(broken_cert.get());
     Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
     return tmpBuffer;
 }
@@ -320,7 +513,15 @@ const char *Ssl::ErrorDetail::notafter() const
 const char *Ssl::ErrorDetail::err_code() const
 {
     static char tmpBuffer[64];
-    const char *err = GetErrorName(error_no);
+    // We can use the GetErrorName but using the detailEntry is faster,
+    // so try it first.
+    const char *err = detailEntry.name.termedBuf();
+
+    // error details not loaded yet or not defined in error_details.txt,
+    // try the GetErrorName...
+    if (!err)
+        err = GetErrorName(error_no);
+
     if (!err) {
         snprintf(tmpBuffer, 64, "%d", (int)error_no);
         err = tmpBuffer;
@@ -333,27 +534,44 @@ const char *Ssl::ErrorDetail::err_code() const
  */
 const char *Ssl::ErrorDetail::err_descr() const
 {
-    if (const char *err = GetErrorDescr(error_no))
+    if (error_no == SSL_ERROR_NONE)
+        return "[No Error]";
+    if (const char *err = detailEntry.descr.termedBuf())
         return err;
     return "[Not available]";
 }
 
+const char *Ssl::ErrorDetail::err_lib_error() const
+{
+    if (errReason.size() > 0)
+        return errReason.termedBuf();
+    else 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
+ * Converts the code to a string value. Supported formating codes are:
+ *
+ * Error meta information:
+ * %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_lib_error: human-readable low-level error string by ERR_error_string(3SSL)
+ *
+ * Certificate information extracted from broken (not necessarily peer!) cert
  * %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++) {
+    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;
@@ -372,11 +590,17 @@ int Ssl::ErrorDetail::convert(const char *code, const char **value) const
  */
 void Ssl::ErrorDetail::buildDetail() const
 {
-    char const *s = getErrorDetail(error_no);
+    char const *s = NULL;
     char const *p;
     char const *t;
     int code_len = 0;
 
+    if (ErrorDetailsManager::GetInstance().getErrorDetail(error_no, request, detailEntry))
+        s = detailEntry.detail.termedBuf();
+
+    if (!s)
+        s = SslErrorDetailDefaultStr;
+
     assert(s);
     while ((p = strchr(s, '%'))) {
         errDetailStr.append(s, p - s);
@@ -392,25 +616,39 @@ void Ssl::ErrorDetail::buildDetail() const
 
 const String &Ssl::ErrorDetail::toString() const
 {
-    if (!errDetailStr.defined())
+    if (errDetailStr.size() == 0)
         buildDetail();
     return errDetailStr;
 }
 
-/* We may do not want to use X509_dup but instead
-   internal SSL locking:
-   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, X509 *broken, const char *aReason): error_no (err_no), lib_error_no(SSL_ERROR_NONE), errReason(aReason)
 {
-    peer_cert.reset(X509_dup(cert));
+    if (cert)
+        peer_cert.resetAndLock(cert);
+
+    if (broken)
+        broken_cert.resetAndLock(broken);
+    else
+        broken_cert.resetAndLock(cert);
+
+    detailEntry.error_no = SSL_ERROR_NONE;
 }
 
 Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail)
 {
     error_no = anErrDetail.error_no;
+    request = anErrDetail.request;
+
     if (anErrDetail.peer_cert.get()) {
-        peer_cert.reset(X509_dup(anErrDetail.peer_cert.get()));
+        peer_cert.resetAndLock(anErrDetail.peer_cert.get());
     }
+
+    if (anErrDetail.broken_cert.get()) {
+        broken_cert.resetAndLock(anErrDetail.broken_cert.get());
+    }
+
+    detailEntry = anErrDetail.detailEntry;
+
+    lib_error_no = anErrDetail.lib_error_no;
 }
+