]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Server certificate testing ACLs
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 2 Feb 2012 10:53:08 +0000 (12:53 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 2 Feb 2012 10:53:08 +0000 (12:53 +0200)
This patch add the following ACLs to test the condition of the origin server
certificate: ssl::certHasExpired, ssl::certNotYetValid, ssl::certDomainMismatch,
ssl::certUntrusted and ssl::certSelfSigned.

The above in this patch are predifined acl lists or/and can be used as error
name shortcuts with ssl_error ACL lists

Implementation details:
1) The ssl::certHasExpired,  ssl::certNotYetValid, ssl::certDomainMismatch
ssl::certUntrusted and ssl::certSelfSign acl lists are predifined in cf.data.pre

2) The above names can also used as error names. The ssl-error parser
(Ssl::ParseErrorString function) replace them with the appropriate SSL error
list.

3) The Ssl::ParseErrorString function modified to return a list of errors, not
just an error code.

4) I implement the IFDEF /ENDIF block support for cf.data.pre file.

include/CbDataList.h
src/Makefile.am
src/acl/SslErrorData.cc
src/cf.data.pre
src/cf_gen.cc
src/ssl/ErrorDetail.cc
src/ssl/ErrorDetail.h

index cdb6e2bfdaf9e3e798c00de235e2ac11460b340a..8e221e20e20e17390400005001355e526712383c 100644 (file)
@@ -47,6 +47,8 @@ public:
     bool push_back_unique(C const &);
     bool find(C const &)const;
     bool findAndTune(C const &);
+    /// iterates entire list to return the last element holder
+    CbDataList *tail();
     CbDataList *next;
     C element;
     bool empty() const { return this == NULL; }
@@ -140,6 +142,16 @@ CbDataList<C>::push_back_unique(C const &toAdd)
     return true;
 }
 
+template <class C>
+CbDataList<C> *
+CbDataList<C>::tail()
+{
+    CbDataList<C> *last;
+    for (last = this; last->next; last = last->next);
+    return last;
+}
+
+
 template <class C>
 bool
 CbDataList<C>::find (C const &toFind) const
index 2dd33acbe896a7834bd510c5fd84d11a3d5370f8..6f285e4050c75a932c528de6ad059673b33944c8 100644 (file)
@@ -937,6 +937,7 @@ cf.data: cf.data.pre Makefile
        -e "s%[@]DEFAULT_SSL_DB_DIR[@]%$(DEFAULT_SSL_DB_DIR)%g" \
        -e "s%[@]DEFAULT_ICON_DIR[@]%$(DEFAULT_ICON_DIR)%g" \
        -e "s%[@]DEFAULT_CONFIG_DIR[@]%$(DEFAULT_CONFIG_DIR)%g" \
+       -e "s%[@]DEFAULT_ERROR_DIR[@]%$(DEFAULT_ERROR_DIR)%g" \
        -e "s%[@]DEFAULT_PREFIX[@]%$(DEFAULT_PREFIX)%g" \
        -e "s%[@]DEFAULT_HOSTS[@]%$(DEFAULT_HOSTS)%g" \
        -e "s%[@]SQUID[@]%SQUID\ $(VERSION)%g" \
index 0abeb2daa25cdbe23511134f9fbc536b453eddbf..daf4756602abc066528f317693a6a75fe6948cf1 100644 (file)
@@ -60,9 +60,9 @@ ACLSslErrorData::parse()
 
     for (Tail = &values; *Tail; Tail = &((*Tail)->next));
     while ((t = strtokFile())) {
-        Ssl::Errors *q = new Ssl::Errors(Ssl::ParseErrorString(t));
+        Ssl::Errors *q = Ssl::ParseErrorString(t);
         *(Tail) = q;
-        Tail = &q->next;
+        Tail = &q->tail()->next;
     }
 }
 
index 0b1a2c4a4ab65564ff746c98f80426ee07335a10..f1f54d09c098b19df612337f739ee71fb4d45dff 100644 (file)
@@ -634,6 +634,13 @@ DOC_END
 NAME: acl
 TYPE: acl
 LOC: Config.aclList
+IFDEF USE_SSL
+DEFAULT: ssl::certHasExpired ssl_error X509_V_ERR_CERT_HAS_EXPIRED
+DEFAULT: ssl::certNotYetValid ssl_error X509_V_ERR_CERT_NOT_YET_VALID
+DEFAULT: ssl::certDomainMismatch ssl_error SQUID_X509_V_ERR_DOMAIN_MISMATCH
+DEFAULT: ssl::certUntrusted ssl_error 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
+DEFAULT: ssl::certSelfSigned ssl_error X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
+ENDIF
 DEFAULT: all src all
 DEFAULT: manager url_regex -i ^cache_object:// +i ^https?://[^/]+/squid-internal-mgr/
 DEFAULT: localhost src 127.0.0.1/32 ::1
@@ -858,6 +865,30 @@ DOC_START
          # effect in rules that affect the reply data stream such as
          # http_reply_access.
 
+IFDEF USE_SSL
+       acl aclname ssl_error errorname
+         # match against SSL certificate validation error [fast]
+         # For valid error names see in @DEFAULT_ERROR_DIR@/templates/error-details.txt template file
+         # The user aditionaly can use as error name the following error name
+         # shortcuts:
+         #  [ssl::]certHasExpired: certificate "not after" field is in the past
+         #  [ssl::]certNotYetValid: certificate "not before" field is in the 
+         #       future
+         #  [ssl::]certDomainMismatch: The certificate CN domain does not match
+         #       connecting host name
+         #  [ssl::]certUntrusted: The certificate is untrusted because of an
+         #       error says that the certificate issuer is not trusted.
+         #  [ssl::]certSelfSigned: The certificate is self signed
+         #
+         # The ssl::certHasExpired, ssl::certNotYetValid ssl::certDomainMismatch,
+         # ssl::certUntrusted and ssl::certSelfSigned also exists as predefined
+         # acl lists.
+         #
+         # NOTE: The ssl_error acl has effect only when used with
+         # sslproxy_cert_error, sslproxy_cert_sign and sslproxy_cert_adapt
+         # access lists.
+ENDIF
+
        Examples:
                acl macaddress arp 09:00:2b:23:45:67
                acl myexample dst_as 1241
@@ -2072,8 +2103,13 @@ DOC_START
                generate a X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT error in the
                browser.
 
-        When the acl(s) match, the corresponding signing algorithm is used to
-        generate the certificate. Otherwise, the default signing algorithm used
+       This clause only supports fast acl types.
+
+       When the acl(s) match, the corresponding signing algorithm is used to
+       generate the certificate. Otherwise, the default signing algorithm used
+
+       BUG: The SQUID_X509_V_ERR_DOMAIN_MISMATCH and ssl:certDomainMismatch ssl
+       errors can not be used with ssl_error acl type.
 DOC_END
 
 NAME: sslproxy_cert_adapt 
@@ -2100,9 +2136,14 @@ DOC_START
                without an explicit parameter for intercepted or tproxied SSL
                transactions. 
                
+       This clause only supports fast acl types.
+
        When the acl(s) match, the corresponding adaptation algorithm is
        applied to the fake/generated certificate. Otherwise, the
        default mimicking action takes place.
+
+       BUG: The SQUID_X509_V_ERR_DOMAIN_MISMATCH and ssl:certDomainMismatch ssl
+       errors can not be used with ssl_error acl type
 DOC_END
 
 NAME: sslpassword_program
index dfac371586a4f597a7085d16103595152307b44f..e61dc5dd45ebc1f5275fec65e2fe14444cab3909 100644 (file)
@@ -64,6 +64,7 @@ _FILE_OFFSET_BITS==64
 #include <fstream>
 #include <iostream>
 #include <list>
+#include <stack>
 
 #include "cf_gen_defines.cci"
 
@@ -151,6 +152,7 @@ static void gen_dump(const EntryList &, std::ostream&);
 static void gen_free(const EntryList &, std::ostream&);
 static void gen_conf(const EntryList &, std::ostream&, bool verbose_output);
 static void gen_default_if_none(const EntryList &, std::ostream&);
+static bool isDefined(const std::string &name);
 
 static void
 checkDepend(const std::string &directive, const char *name, const TypeList &types, const EntryList &entries)
@@ -198,6 +200,7 @@ main(int argc, char *argv[])
     char *ptr = NULL;
     char buff[MAX_LINE];
     std::ifstream fp;
+    std::stack<std::string> IFDEFS;
 
     if (argc != 3)
         usage(argv[0]);
@@ -252,6 +255,22 @@ main(int argc, char *argv[])
         if ((t = strchr(buff, '\n')))
             *t = '\0';
 
+        if(strncmp(buff, "IFDEF ", 6) == 0) {
+            if ((ptr = strtok(buff + 6, WS)) == NULL) {
+                std::cerr << "Missing IFDEF parameter on line" << linenum << std::endl;
+                exit(1);
+            }
+            IFDEFS.push(ptr);
+            std::cerr << "Entering IFDEF " << ptr << std::endl;
+            continue;
+        } else if (strcmp(buff, "ENDIF") == 0) {
+            if (IFDEFS.size() == 0) {
+                std::cerr << "ENDIF without IFDEF before on line " << linenum << std::endl;
+                exit(1);
+            }
+            IFDEFS.pop();
+        }
+        else if (!IFDEFS.size() || isDefined(IFDEFS.top()))
         switch (state) {
 
         case sSTART:
index 74ce22e117480b296e3ce075c2884276a931d193..ca20d65f26ccc7eb064968b95b441633108c844e 100644 (file)
@@ -90,6 +90,44 @@ static SslErrorEntry TheSslErrorArray[] = {
     {SSL_ERROR_NONE, NULL}
 };
 
+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());
@@ -98,6 +136,13 @@ static void loadSslErrorMap()
     }
 }
 
+static void loadSslErrorShortcutsMap()
+{
+    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)
 {
     for (int i = 0; TheSslErrorArray[i].name != NULL; i++) {
@@ -107,24 +152,38 @@ Ssl::ssl_error_t Ssl::GetErrorCode(const char *name)
     return SSL_ERROR_NONE;
 }
 
-Ssl::ssl_error_t
+Ssl::Errors *
 Ssl::ParseErrorString(const char *name)
 {
     assert(name);
 
     const Ssl::ssl_error_t ssl_error = GetErrorCode(name);
     if (ssl_error != SSL_ERROR_NONE)
-        return ssl_error;
+        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);
     }
 
+    if (TheSslErrorShortcuts.empty())
+        loadSslErrorShortcutsMap();
+
+    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;
+    }
+
     fatalf("Unknown SSL error name '%s'", name);
-    return SSL_ERROR_SSL; // not reached
+    return NULL; // not reached
 }
 
 const char *Ssl::GetErrorName(Ssl::ssl_error_t value)
index 186c006630f6eed6bcf58a45cefb68b38689c75b..b26a87a66e0121cb560b003de823ac45ccb8e882 100644 (file)
@@ -15,10 +15,12 @@ namespace Ssl
 {
 /**
   \ingroup ServerProtocolSSLAPI
- * The ssl_error_t representation of the error described by "name".
+ * The Ssl::Errors representation of the error described by "name".
+ * The result may be a single element of a list of errors, and needs to be
+ * released by the caller.
  * This function also parses numeric arguments.
  */
-ssl_error_t ParseErrorString(const char *name);
+Ssl::Errors *ParseErrorString(const char *name);
 
 /**
    \ingroup ServerProtocolSSLAPI