#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"
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
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 *
#include "acl/Strategised.h"
/// \ingroup ACLAPI
-class ACLCertificateStrategy : public ACLStrategy<SSL *>
+class ACLCertificateStrategy : public ACLStrategy<X509 *>
{
public:
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 */
#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);
}
}
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;
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.
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();
}
return values.empty();
}
-ACLData<SSL *> *
+ACLData<X509 *> *
ACLCertificateData::clone() const
{
/* Splay trees don't clone yet. */
#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);
#if USE_SSL
/// SSL [certificate validation] errors, in undefined order
Ssl::Errors *sslErrors;
+ /// The peer certificate
+ Ssl::X509_Pointer serverCert;
#endif
ExternalACLEntry *extacl_entry;
CertificateData.h \
Certificate.cc \
Certificate.h \
+ ServerCertificate.cc \
+ ServerCertificate.h \
SslError.cc \
SslError.h \
SslErrorData.cc \
--- /dev/null
+/*
+ * $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 */
--- /dev/null
+/*
+ * $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 */
return strcmp (l,r);
}
+void
+ACLStringData::insert(const char *value)
+{
+ values = values->insert(xstrdup(value), splaystrcmp);
+}
+
bool
ACLStringData::match(char const *toFind)
{
void parse();
bool empty() const;
virtual ACLData<char const *> *clone() const;
+ /// Insert custom values
+ void insert(const char *);
SplayNode<char *> *values;
};
#
# 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:
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;
}
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
/// \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;
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;
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 *
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);
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
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)