]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Initial server_ssl_cert_fingerprint ACL implementation.
authorAlex Rousskov <rousskov@measurement-factory.com>
Fri, 14 Sep 2012 20:45:05 +0000 (14:45 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Fri, 14 Sep 2012 20:45:05 +0000 (14:45 -0600)
13 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/StringData.cc
src/acl/StringData.h
src/cf.data.pre
src/ssl/support.cc
src/ssl/support.h
src/tests/stub_libsslsquid.cc

index 2035f9015aa934f165d8af4ff17ec8075d5aa3ef..db6e0bb2ed89ab70408b71a4699c7de17bdd266f 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"
@@ -140,9 +143,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 7b24581a074efe42ac8190e8e6b4b9f9d2926474..e65ba407a818af38db2fbeba47a020e32375bdb6 100644 (file)
@@ -55,7 +55,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 a2ccbbc3a576e93c6be361142aa323b5aa19a711..d08f78abd40da1ff8e2669ae61ec895a70a7faf0 100644 (file)
@@ -42,7 +42,7 @@
 #include "acl/Strategised.h"
 
 /// \ingroup ACLAPI
-class ACLCertificateStrategy : public ACLStrategy<SSL *>
+class ACLCertificateStrategy : public ACLStrategy<X509 *>
 {
 
 public:
@@ -66,9 +66,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 972e9536165f059b48646fad9d344fcb8ce9c88f..4651e2480a8267e2d8fac70ec47382067a02ad87 100644 (file)
 #include "protos.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);
 }
@@ -70,13 +84,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;
 
@@ -94,7 +108,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.
@@ -107,17 +122,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();
 }
@@ -128,7 +171,7 @@ ACLCertificateData::empty() const
     return values.empty();
 }
 
-ACLData<SSL *> *
+ACLData<X509 *> *
 ACLCertificateData::clone() const
 {
     /* Splay trees don't clone yet. */
index 3ddbc855c804ea94fcc6d13ef4fce0bec1267997..5aec38f8ab49e01b7cd58a1d1bdda91691b5f63f 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 6c926d85ed2824e0dbd401be5e9d0ffe64fab58f..7ca55a8b2a6cded34ed6695fbc248d8f8244a539 100644 (file)
@@ -69,6 +69,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 112230036b8d726bd0b7c9444bce10b26c04ef05..d3c786ac79fd76b2ade2c2c005a0f6dc2141570e 100644 (file)
@@ -118,6 +118,8 @@ SSL_ACLS = \
         CertificateData.h  \
         Certificate.cc \
         Certificate.h  \
+       ServerCertificate.cc \
+       ServerCertificate.h \
         SslError.cc \
         SslError.h \
         SslErrorData.cc \
index b257edad2ccddb5c44cbcdf9641c67f9a6bb54aa..fcdbf126bca2b9ef013a485f1aff1a7d0dc1dfda 100644 (file)
@@ -67,6 +67,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 aa4d593363572bd2bb7ad6e477c0ed5a1464237d..207528c140eed7577046faa36991deb70ba86dd7 100644 (file)
@@ -54,6 +54,8 @@ public:
     void parse();
     bool empty() const;
     virtual ACLData<char const *> *clone() const;
+    /// Insert custom values
+    void insert(const char *);
 
     SplayNode<char *> *values;
 };
index 98fe7fd8e40dd9c5155c3ae78971978a860a8ad0..6433db3fabacd183f4fd6988a676af4545428313 100644 (file)
@@ -910,6 +910,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 1380531a6a97c8c657decdf5f4c91371df66f370..5da986022ec63c686324068c780b51679c7d3810 100644 (file)
@@ -281,6 +281,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;
@@ -289,6 +290,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 
@@ -1174,17 +1176,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;
 
@@ -1192,24 +1188,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;
 
@@ -1217,9 +1229,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 4a49b9574f9d20ed33d025331afae2e9e522e00f..62539f319fd7f19c0958f4f06166ff54d329a086 100644 (file)
@@ -97,13 +97,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);
@@ -113,6 +110,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 be7232611575125118d293b4e823830619bfb5b5..20c3eb71ea3043eb2a540dc11fa7ce618c5c77e5 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)