]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
merge from SslServerCertValidator r12332
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 13 Nov 2012 18:19:17 +0000 (20:19 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 13 Nov 2012 18:19:17 +0000 (20:19 +0200)
15 files changed:
src/AclRegs.cc
src/acl/Certificate.cc
src/acl/Certificate.h
src/acl/CertificateData.cc
src/acl/CertificateData.h
src/acl/FilledChecklist.h
src/acl/Makefile.am
src/acl/ServerCertificate.cc [new file with mode: 0644]
src/acl/ServerCertificate.h [new file with mode: 0644]
src/acl/StringData.cc
src/acl/StringData.h
src/cf.data.pre
src/ssl/support.cc
src/ssl/support.h
src/tests/stub_libsslsquid.cc

index d56c3c289b5cc434dae644694a56c684bfb78d43..564f071d2ad0198cb11c55263dc16f929b28ced7 100644 (file)
@@ -59,6 +59,9 @@
 #include "acl/Strategised.h"
 #include "acl/Strategy.h"
 #include "acl/StringData.h"
+#if USE_SSL
+#include "acl/ServerCertificate.h"
+#endif
 #include "acl/Tag.h"
 #include "acl/TimeData.h"
 #include "acl/Time.h"
@@ -143,9 +146,11 @@ ACLStrategised<int> ACLUrlPort::RegistryEntry_(new ACLIntRange, ACLUrlPortStrate
 ACL::Prototype ACLSslError::RegistryProtoype(&ACLSslError::RegistryEntry_, "ssl_error");
 ACLStrategised<const Ssl::Errors *> ACLSslError::RegistryEntry_(new ACLSslErrorData, ACLSslErrorStrategy::Instance(), "ssl_error");
 ACL::Prototype ACLCertificate::UserRegistryProtoype(&ACLCertificate::UserRegistryEntry_, "user_cert");
-ACLStrategised<SSL *> ACLCertificate::UserRegistryEntry_(new ACLCertificateData (sslGetUserAttribute), ACLCertificateStrategy::Instance(), "user_cert");
+ACLStrategised<X509 *> ACLCertificate::UserRegistryEntry_(new ACLCertificateData (Ssl::GetX509UserAttribute, "*"), ACLCertificateStrategy::Instance(), "user_cert");
 ACL::Prototype ACLCertificate::CARegistryProtoype(&ACLCertificate::CARegistryEntry_, "ca_cert");
-ACLStrategised<SSL *> ACLCertificate::CARegistryEntry_(new ACLCertificateData (sslGetCAAttribute), ACLCertificateStrategy::Instance(), "ca_cert");
+ACLStrategised<X509 *> ACLCertificate::CARegistryEntry_(new ACLCertificateData (Ssl::GetX509CAAttribute, "*"), ACLCertificateStrategy::Instance(), "ca_cert");
+ACL::Prototype ACLServerCertificate::X509FingerprintRegistryProtoype(&ACLServerCertificate::X509FingerprintRegistryEntry_, "server_ssl_cert_fingerprint");
+ACLStrategised<X509 *> ACLServerCertificate::X509FingerprintRegistryEntry_(new ACLCertificateData(Ssl::GetX509Fingerprint, "-sha1", true), ACLServerCertificateStrategy::Instance(), "server_ssl_cert_fingerprint");
 #endif
 
 #if USE_SQUID_EUI
index cf70f86e94f5fef03383b2230388376d7141c8e5..ad39cfc0b8af34f94a57ccfd27195f80cf69bf7a 100644 (file)
@@ -53,7 +53,10 @@ ACLCertificateStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *c
     const int fd = checklist->fd();
     const bool goodDescriptor = 0 <= fd && fd <= Biggest_FD;
     SSL *ssl = goodDescriptor ? fd_table[fd].ssl : 0;
-    return data->match (ssl);
+    X509 *cert = SSL_get_peer_certificate(ssl);
+    const bool res = data->match (cert);
+    X509_free(cert);
+    return res;
 }
 
 ACLCertificateStrategy *
index 1984c420faefb8035ff51469a0a2ea2804d6c694..56db4fb800620034c7264d51f6899d9c47f2d4ab 100644 (file)
@@ -40,7 +40,7 @@
 #include "acl/Strategised.h"
 
 /// \ingroup ACLAPI
-class ACLCertificateStrategy : public ACLStrategy<SSL *>
+class ACLCertificateStrategy : public ACLStrategy<X509 *>
 {
 
 public:
@@ -64,9 +64,9 @@ class ACLCertificate
 
 private:
     static ACL::Prototype UserRegistryProtoype;
-    static ACLStrategised<SSL*> UserRegistryEntry_;
+    static ACLStrategised<X509*> UserRegistryEntry_;
     static ACL::Prototype CARegistryProtoype;
-    static ACLStrategised<SSL *> CARegistryEntry_;
+    static ACLStrategised<X509 *> CARegistryEntry_;
 };
 
 #endif /* SQUID_ACLCERTIFICATE_H */
index 9c069ecc6d78dc0cbfbe038ec522817e856567de..bf0995f62c6dec6dcfcc7af5b5ea6e745003246e 100644 (file)
 #include "cache_cf.h"
 #include "wordlist.h"
 
-ACLCertificateData::ACLCertificateData(SSLGETATTRIBUTE *sslStrategy) : attribute (NULL), values (), sslAttributeCall (sslStrategy)
-{}
+ACLCertificateData::ACLCertificateData(Ssl::GETX509ATTRIBUTE *sslStrategy, const char *attrs, bool optionalAttr) : validAttributesStr(attrs), attributeIsOptional(optionalAttr), attribute (NULL), values (), sslAttributeCall (sslStrategy)
+{
+    if (attrs) {
+        size_t current;
+        size_t next = -1;
+        std::string valid(attrs);
+        do {
+            current = next + 1;
+            next = valid.find_first_of( "|", current);
+            validAttributes.push_back(valid.substr( current, next - current ));
+        } while (next != std::string::npos);
+    }
+}
 
 ACLCertificateData::ACLCertificateData(ACLCertificateData const &old) : attribute (NULL), values (old.values), sslAttributeCall (old.sslAttributeCall)
 {
+    validAttributesStr = old.validAttributesStr;
+    validAttributes.assign (old.validAttributes.begin(), old.validAttributes.end());
+    attributeIsOptional = old.attributeIsOptional;
     if (old.attribute)
         attribute = xstrdup (old.attribute);
 }
@@ -67,13 +81,13 @@ splaystrcmp (T&l, T&r)
 }
 
 bool
-ACLCertificateData::match(SSL *ssl)
+ACLCertificateData::match(X509 *cert)
 {
-    if (!ssl)
+    if (!cert)
         return 0;
 
-    char const *value = sslAttributeCall(ssl, attribute);
-
+    char const *value = sslAttributeCall(cert, attribute);
+    debugs(28, 6, HERE << (attribute ? attribute : "value") << "=" << value);
     if (value == NULL)
         return 0;
 
@@ -91,7 +105,8 @@ wordlist *
 ACLCertificateData::dump()
 {
     wordlist *wl = NULL;
-    wordlistAdd(&wl, attribute);
+    if (validAttributesStr)
+        wordlistAdd(&wl, attribute);
     /* damn this is VERY inefficient for long ACL lists... filling
      * a wordlist this way costs Sum(1,N) iterations. For instance
      * a 1000-elements list will be filled in 499500 iterations.
@@ -104,17 +119,45 @@ ACLCertificateData::dump()
 void
 ACLCertificateData::parse()
 {
-    char *newAttribute = strtokFile();
+    if (validAttributesStr) {
+        char *newAttribute = strtokFile();
 
-    if (!newAttribute)
-        self_destruct();
+        if (!newAttribute) {
+            if (attributeIsOptional)
+                return;
 
-    /* an acl must use consistent attributes in all config lines */
-    if (attribute) {
-        if (strcasecmp(newAttribute, attribute) != 0)
+            debugs(28, DBG_CRITICAL, "required attribute argument missing");
             self_destruct();
-    } else
-        attribute = xstrdup(newAttribute);
+        }
+
+        // Handle the cases where we have optional -x type attributes
+        if (attributeIsOptional && newAttribute[0] != '-')
+            // The read token is not an attribute/option, so add it to values list
+            values.insert(newAttribute);
+        else {
+            bool valid = false;
+            for (std::list<std::string>::const_iterator it = validAttributes.begin(); it != validAttributes.end(); ++it) {
+                if (*it == "*" || *it == newAttribute) {
+                    valid = true;
+                    break;
+                }
+            }
+
+            if (!valid) {
+                debugs(28, DBG_CRITICAL, "Unknown option. Supported option(s) are: " << validAttributesStr);
+                self_destruct();
+            }
+            
+            /* an acl must use consistent attributes in all config lines */
+            if (attribute) {
+                if (strcasecmp(newAttribute, attribute) != 0) {
+                    debugs(28, DBG_CRITICAL, "An acl must use consistent attributes in all config lines (" << newAttribute << "!=" << attribute << ").");
+                    self_destruct();
+                }
+            } else
+                attribute = xstrdup(newAttribute);
+        }
+    }
 
     values.parse();
 }
@@ -125,7 +168,7 @@ ACLCertificateData::empty() const
     return values.empty();
 }
 
-ACLData<SSL *> *
+ACLData<X509 *> *
 ACLCertificateData::clone() const
 {
     /* Splay trees don't clone yet. */
index 879146861cbff780bf9992f7923b447b4928abfc..911f549bd67e486dff3ea7f8495067c65fe5ac8c 100644 (file)
 #include "acl/Data.h"
 #include "ssl/support.h"
 #include "acl/StringData.h"
+#include <string>
+#include <list>
 
 /// \ingroup ACLAPI
-class ACLCertificateData : public ACLData<SSL *>
+class ACLCertificateData : public ACLData<X509 *>
 {
 
 public:
     MEMPROXY_CLASS(ACLCertificateData);
 
-    ACLCertificateData(SSLGETATTRIBUTE *);
+    ACLCertificateData(Ssl::GETX509ATTRIBUTE *, const char *attributes, bool optionalAttr = false);
     ACLCertificateData(ACLCertificateData const &);
     ACLCertificateData &operator= (ACLCertificateData const &);
     virtual ~ACLCertificateData();
-    bool match(SSL *);
+    bool match(X509 *);
     wordlist *dump();
     void parse();
     bool empty() const;
-    virtual ACLData<SSL *> *clone() const;
+    virtual ACLData<X509 *> *clone() const;
 
+    /// A '|'-delimited list of valid ACL attributes.
+    /// A "*" item means that any attribute is acceptable.
+    /// Assumed to be a const-string and is never duped/freed.
+    /// Nil unless ACL form is: acl Name type attribute value1 ...
+    const char *validAttributesStr;
+    /// Parsed list of valid attribute names
+    std::list<std::string> validAttributes;
+    /// True if the attribute is optional (-xxx options)
+    bool attributeIsOptional;
     char *attribute;
     ACLStringData values;
 
 private:
-    SSLGETATTRIBUTE *sslAttributeCall;
+    /// The callback used to retrieve the data from X509 cert
+    Ssl::GETX509ATTRIBUTE *sslAttributeCall;
 };
 
 MEMPROXY_CLASS_INLINE(ACLCertificateData);
index 4e8119ade32bd04fcaa1692313cdf3f56fd7652f..362711bf9ffef786d55f19619233958e403f0b60 100644 (file)
@@ -73,6 +73,8 @@ public:
 #if USE_SSL
     /// SSL [certificate validation] errors, in undefined order
     Ssl::Errors *sslErrors;
+    /// The peer certificate
+    Ssl::X509_Pointer serverCert;
 #endif
 
     ExternalACLEntry *extacl_entry;
index d8401f71a0c51d8d3c609ac030c58996839dde19..3a1d122766049ac4c8fe37fc0d2d3bbe22b4b173 100644 (file)
@@ -122,6 +122,8 @@ SSL_ACLS = \
         CertificateData.h  \
         Certificate.cc \
         Certificate.h  \
+       ServerCertificate.cc \
+       ServerCertificate.h \
         SslError.cc \
         SslError.h \
         SslErrorData.cc \
diff --git a/src/acl/ServerCertificate.cc b/src/acl/ServerCertificate.cc
new file mode 100644 (file)
index 0000000..043df0e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ */
+
+#include "squid.h"
+
+#if USE_SSL
+
+#include "acl/ServerCertificate.h"
+#include "acl/Checklist.h"
+#include "acl/CertificateData.h"
+#include "fde.h"
+#include "client_side.h"
+#include "ssl/ServerBump.h"
+
+
+int
+ACLServerCertificateStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
+{
+    X509 *cert = NULL;
+    if (checklist->serverCert.get())
+        cert = checklist->serverCert.get();
+    else if (checklist->conn() != NULL && checklist->conn()->serverBump())
+        cert = checklist->conn()->serverBump()->serverCert.get();
+
+    if (!cert)
+        return 0;
+
+    return data->match(cert);
+}
+
+ACLServerCertificateStrategy *
+ACLServerCertificateStrategy::Instance()
+{
+    return &Instance_;
+}
+
+ACLServerCertificateStrategy ACLServerCertificateStrategy::Instance_;
+
+#endif /* USE_SSL */
diff --git a/src/acl/ServerCertificate.h b/src/acl/ServerCertificate.h
new file mode 100644 (file)
index 0000000..6edbe86
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_ACLSERVERCERTIFICATE_H
+#define SQUID_ACLSERVERCERTIFICATE_H
+
+#include "acl/Acl.h"
+#include "acl/Data.h"
+#include "acl/Checklist.h"
+#include "ssl/support.h"
+#include "acl/Strategised.h"
+
+/// \ingroup ACLAPI
+class ACLServerCertificateStrategy : public ACLStrategy<X509 *>
+{
+
+public:
+    virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *);
+    static ACLServerCertificateStrategy *Instance();
+    /* Not implemented to prevent copies of the instance. */
+    /* Not private to prevent brain dead g+++ warnings about
+     * private constructors with no friends */
+    ACLServerCertificateStrategy(ACLServerCertificateStrategy const &);
+
+private:
+    static ACLServerCertificateStrategy Instance_;
+    ACLServerCertificateStrategy() {}
+
+    ACLServerCertificateStrategy&operator=(ACLServerCertificateStrategy const &);
+};
+
+/// \ingroup ACLAPI
+class ACLServerCertificate
+{
+private:
+    static ACL::Prototype X509FingerprintRegistryProtoype;
+    static ACLStrategised<X509*> X509FingerprintRegistryEntry_;
+};
+
+#endif /* SQUID_ACLSERVERCERTIFICATE_H */
index 91b2a55824c91b0cd0c5e09db20f794eabd81406..2067b910433e2c1f3567012e64601612ccf55ba4 100644 (file)
@@ -66,6 +66,12 @@ splaystrcmp (char * const &l, char * const &r)
     return strcmp (l,r);
 }
 
+void
+ACLStringData::insert(const char *value)
+{
+    values = values->insert(xstrdup(value), splaystrcmp);
+}
+
 bool
 ACLStringData::match(char const *toFind)
 {
index 16b2ab94bdc15872d92624ec9b55bfbd656bded3..aa6b534c564a727958198e6896af8b3339d71596 100644 (file)
@@ -52,6 +52,8 @@ public:
     void parse();
     bool empty() const;
     virtual ACLData<char const *> *clone() const;
+    /// Insert custom values
+    void insert(const char *);
 
     SplayNode<char *> *values;
 };
index 354384edf388857256a5cd94485f6720742ec4a8..59c7d144d9e1ef11c1740f01e0904d504c2aa794 100644 (file)
@@ -912,6 +912,15 @@ IF USE_SSL
          #
          # NOTE: The ssl_error ACL is only supported with sslproxy_cert_error,
          # sslproxy_cert_sign, and sslproxy_cert_adapt options.
+
+       acl aclname server_ssl_cert_fingerprint [-sha1] fingerprint
+         # match against server SSL certificate fingerprint [fast]
+         #
+         # The fingerprint is the digest of the DER encoded version 
+         # of the whole certificate. The user should use the form: XX:XX:...
+         # Optional argument specifies the digest algorithm to use.
+         # The SHA1 digest algorithm is the default and is currently
+         # the only algorithm supported (-sha1).
 ENDIF
 
        Examples:
index 4b42d0278a23c99398170634669535e3daf9818d..ff7e292e58a38174154709783662b85c9ee66673 100644 (file)
@@ -280,6 +280,7 @@ ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
             ACLFilledChecklist *filledCheck = Filled(check);
             assert(!filledCheck->sslErrors);
             filledCheck->sslErrors = new Ssl::Errors(error_no);
+            filledCheck->serverCert.resetAndLock(peer_cert);
             if (check->fastCheck() == ACCESS_ALLOWED) {
                 debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
                 ok = 1;
@@ -288,6 +289,7 @@ ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
             }
             delete filledCheck->sslErrors;
             filledCheck->sslErrors = NULL;
+            filledCheck->serverCert.reset(NULL);
         }
 #if 1 // USE_SSL_CERT_VALIDATOR
         // If the certificate validator is used then we need to allow all errors and 
@@ -1173,17 +1175,11 @@ done:
 
 /// \ingroup ServerProtocolSSLInternal
 const char *
-sslGetUserAttribute(SSL * ssl, const char *attribute_name)
+Ssl::GetX509UserAttribute(X509 * cert, const char *attribute_name)
 {
-    X509 *cert;
     X509_NAME *name;
     const char *ret;
 
-    if (!ssl)
-        return NULL;
-
-    cert = SSL_get_peer_certificate(ssl);
-
     if (!cert)
         return NULL;
 
@@ -1191,24 +1187,40 @@ sslGetUserAttribute(SSL * ssl, const char *attribute_name)
 
     ret = ssl_get_attribute(name, attribute_name);
 
-    X509_free(cert);
-
     return ret;
 }
 
+const char *
+Ssl::GetX509Fingerprint(X509 * cert, const char *)
+{
+    static char buf[1024];
+    if (!cert)
+        return NULL;
+    
+    unsigned int n;
+    unsigned char md[EVP_MAX_MD_SIZE];
+    if (!X509_digest(cert, EVP_sha1(), md, &n))
+        return NULL;
+
+    assert(3 * n + 1 < sizeof(buf));
+
+    char *s = buf;
+    for (unsigned int i=0; i < n; ++i, s += 3) {
+        const char term = (i + 1 < n) ? ':' : '\0';
+        snprintf(s, 4, "%02X%c", md[i], term);
+    }
+
+    return buf;
+}
+
 /// \ingroup ServerProtocolSSLInternal
 const char *
-sslGetCAAttribute(SSL * ssl, const char *attribute_name)
+Ssl::GetX509CAAttribute(X509 * cert, const char *attribute_name)
 {
-    X509 *cert;
+
     X509_NAME *name;
     const char *ret;
 
-    if (!ssl)
-        return NULL;
-
-    cert = SSL_get_peer_certificate(ssl);
-
     if (!cert)
         return NULL;
 
@@ -1216,9 +1228,33 @@ sslGetCAAttribute(SSL * ssl, const char *attribute_name)
 
     ret = ssl_get_attribute(name, attribute_name);
 
+    return ret;
+}
+
+const char *sslGetUserAttribute(SSL *ssl, const char *attribute_name)
+{
+    if (!ssl)
+        return NULL;
+
+    X509 *cert = SSL_get_peer_certificate(ssl);
+
+    const char *attr = Ssl::GetX509UserAttribute(cert, attribute_name);
+
     X509_free(cert);
+    return attr;
+}
 
-    return ret;
+const char *sslGetCAAttribute(SSL *ssl, const char *attribute_name)
+{
+    if (!ssl)
+        return NULL;
+
+    X509 *cert = SSL_get_peer_certificate(ssl);
+
+    const char *attr = Ssl::GetX509CAAttribute(cert, attribute_name);
+
+    X509_free(cert);
+    return attr;
 }
 
 const char *
index efff316471e26bcabc93b20e607047a51498911f..f0f24c6eb6a2981715f4533dc275d616d23af94a 100644 (file)
@@ -95,13 +95,10 @@ void ssl_shutdown_method(SSL *ssl);
 const char *sslGetUserEmail(SSL *ssl);
 
 /// \ingroup ServerProtocolSSLAPI
-typedef char const *SSLGETATTRIBUTE(SSL *, const char *);
+const char *sslGetUserAttribute(SSL *ssl, const char *attribute_name);
 
 /// \ingroup ServerProtocolSSLAPI
-SSLGETATTRIBUTE sslGetUserAttribute;
-
-/// \ingroup ServerProtocolSSLAPI
-SSLGETATTRIBUTE sslGetCAAttribute;
+const char *sslGetCAAttribute(SSL *ssl, const char *attribute_name);
 
 /// \ingroup ServerProtocolSSLAPI
 const char *sslGetUserCertificatePEM(SSL *ssl);
@@ -111,6 +108,18 @@ const char *sslGetUserCertificateChainPEM(SSL *ssl);
 
 namespace Ssl
 {
+/// \ingroup ServerProtocolSSLAPI
+typedef char const *GETX509ATTRIBUTE(X509 *, const char *);
+
+/// \ingroup ServerProtocolSSLAPI
+GETX509ATTRIBUTE GetX509UserAttribute;
+
+/// \ingroup ServerProtocolSSLAPI
+GETX509ATTRIBUTE GetX509CAAttribute;
+
+/// \ingroup ServerProtocolSSLAPI
+GETX509ATTRIBUTE GetX509Fingerprint;
+
 /**
   \ingroup ServerProtocolSSLAPI
  * Supported ssl-bump modes
index 36ed40145647d612c9e623a54e6d04008c5bff73..e2ccc07e2fd94cad5aa64c088acf88e079e2dc6e 100644 (file)
@@ -48,9 +48,9 @@ int ssl_read_method(int, char *, int) STUB_RETVAL(0)
 int ssl_write_method(int, const char *, int) STUB_RETVAL(0)
 void ssl_shutdown_method(SSL *) STUB
 const char *sslGetUserEmail(SSL *ssl) STUB_RETVAL(NULL)
-// typedef char const *SSLGETATTRIBUTE(SSL *, const char *);
-// SSLGETATTRIBUTE sslGetUserAttribute;
-// SSLGETATTRIBUTE sslGetCAAttribute;
+// typedef char const *Ssl::GETATTRIBUTE(X509 *, const char *);
+// Ssl::GETATTRIBUTE Ssl::GetX509UserAttribute;
+// Ssl::GETATTRIBUTE Ssl::GetX509CAAttribute;
 const char *sslGetUserCertificatePEM(SSL *ssl) STUB_RETVAL(NULL)
 const char *sslGetUserCertificateChainPEM(SSL *ssl) STUB_RETVAL(NULL)
 SSL_CTX * Ssl::generateSslContext(CertificateProperties const &properties, AnyP::PortCfg &) STUB_RETVAL(NULL)