SSL server certificate fingerprint ACL type
This patch add the "server_ssl_cert_fingerprint" acl type to match
against server SSL certificate fingerprint.
The new acl type has the form:
acl aclname server_ssl_cert_fingerprint [-sha1] fingerprint1 ...
The fingerprint must given in the form:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
where X are any valid hexadecimal number
Example usage:
acl BrokeServer dst 192.168.1.23
acl GoodCert server_ssl_cert_fingerprint
AB:2A:82:AF:46:AE:1F:31:21:74:65:BF:56:47:25:D1:87:51:41:AE
sslproxy_cert_error allow BrokeServer GoodCert
sslproxy_cert_error deny all
Someone can retrieve the fingerprint of a certificate using the openssl
command:
# openssl x509 -fingerprint -in test.pem -noout
# openssl s_client -host www.paypal.com -port 443 2> /dev/null |
openssl x509 -fingerprint -noout
This is a Measurement Factory project
SSL server certificate fingerprint ACL type
This patch add the "server_ssl_cert_fingerprint" acl type to match against
server SSL certificate fingerprint.
The new acl type has the form:
acl aclname server_ssl_cert_fingerprint [-sha1] fingerprint1 ...
The fingerprint must given in the form:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
where X are any valid hexadecimal number
Example usage:
acl BrokeServer dst 192.168.1.23
acl GoodCert server_ssl_cert_fingerprint AB:2A:82:AF:46:AE:1F:31:21:74:65:BF:56:47:25:D1:87:51:41:AE
sslproxy_cert_error allow BrokeServer GoodCert
sslproxy_cert_error deny all
=== modified file 'src/AclRegs.cc'
--- src/AclRegs.cc 2012-10-29 01:31:29 +0000
+++ src/AclRegs.cc 2012-11-13 18:18:05 +0000
@@ -42,40 +42,43 @@
#include "acl/Protocol.h"
#include "acl/Random.h"
#include "acl/Referer.h"
#include "acl/RegexData.h"
#include "acl/ReplyHeaderStrategy.h"
#include "acl/ReplyMimeType.h"
#include "acl/RequestHeaderStrategy.h"
#include "acl/RequestMimeType.h"
#include "acl/SourceAsn.h"
#include "acl/SourceDomain.h"
#include "acl/SourceIp.h"
#if USE_SSL
#include "acl/SslErrorData.h"
#include "acl/SslError.h"
#include "acl/CertificateData.h"
#include "acl/Certificate.h"
#endif
#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"
#include "acl/Url.h"
#include "acl/UrlLogin.h"
#include "acl/UrlPath.h"
#include "acl/UrlPort.h"
#include "acl/UserData.h"
#if USE_AUTH
#include "auth/AclProxyAuth.h"
#include "auth/AclMaxUserIp.h"
#endif
#if USE_IDENT
#include "ident/AclIdent.h"
#endif
ACL::Prototype ACLBrowser::RegistryProtoype(&ACLBrowser::RegistryEntry_, "browser");
ACLStrategised<char const *> ACLBrowser::RegistryEntry_(new ACLRegexData, ACLRequestHeaderStrategy<HDR_USER_AGENT>::Instance(), "browser");
ACL::Prototype ACLDestinationDomain::LiteralRegistryProtoype(&ACLDestinationDomain::LiteralRegistryEntry_, "dstdomain");
ACLStrategised<char const *> ACLDestinationDomain::LiteralRegistryEntry_(new ACLDomainData, ACLDestinationDomainStrategy::Instance(), "dstdomain");
@@ -126,43 +129,45 @@
ACL::Prototype ACLSourceDomain::RegexRegistryProtoype(&ACLSourceDomain::RegexRegistryEntry_, "srcdom_regex");
ACLStrategised<char const *> ACLSourceDomain::RegexRegistryEntry_(new ACLRegexData,ACLSourceDomainStrategy::Instance() ,"srcdom_regex");
ACL::Prototype ACLSourceIP::RegistryProtoype(&ACLSourceIP::RegistryEntry_, "src");
ACLSourceIP ACLSourceIP::RegistryEntry_;
ACL::Prototype ACLTime::RegistryProtoype(&ACLTime::RegistryEntry_, "time");
ACLStrategised<time_t> ACLTime::RegistryEntry_(new ACLTimeData, ACLTimeStrategy::Instance(), "time");
ACL::Prototype ACLUrl::RegistryProtoype(&ACLUrl::RegistryEntry_, "url_regex");
ACLStrategised<char const *> ACLUrl::RegistryEntry_(new ACLRegexData, ACLUrlStrategy::Instance(), "url_regex");
ACL::Prototype ACLUrlLogin::RegistryProtoype(&ACLUrlLogin::RegistryEntry_, "urllogin");
ACLStrategised<char const *> ACLUrlLogin::RegistryEntry_(new ACLRegexData, ACLUrlLoginStrategy::Instance(), "urllogin");
ACL::Prototype ACLUrlPath::LegacyRegistryProtoype(&ACLUrlPath::RegistryEntry_, "pattern");
ACL::Prototype ACLUrlPath::RegistryProtoype(&ACLUrlPath::RegistryEntry_, "urlpath_regex");
ACLStrategised<char const *> ACLUrlPath::RegistryEntry_(new ACLRegexData, ACLUrlPathStrategy::Instance(), "urlpath_regex");
ACL::Prototype ACLUrlPort::RegistryProtoype(&ACLUrlPort::RegistryEntry_, "port");
ACLStrategised<int> ACLUrlPort::RegistryEntry_(new ACLIntRange, ACLUrlPortStrategy::Instance(), "port");
#if USE_SSL
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
ACL::Prototype ACLARP::RegistryProtoype(&ACLARP::RegistryEntry_, "arp");
ACLARP ACLARP::RegistryEntry_("arp");
ACL::Prototype ACLEui64::RegistryProtoype(&ACLEui64::RegistryEntry_, "eui64");
ACLEui64 ACLEui64::RegistryEntry_("eui64");
#endif
#if USE_IDENT
ACL::Prototype ACLIdent::UserRegistryProtoype(&ACLIdent::UserRegistryEntry_, "ident");
ACLIdent ACLIdent::UserRegistryEntry_(new ACLUserData, "ident");
ACL::Prototype ACLIdent::RegexRegistryProtoype(&ACLIdent::RegexRegistryEntry_, "ident_regex" );
ACLIdent ACLIdent::RegexRegistryEntry_(new ACLRegexData, "ident_regex");
#endif
#if USE_AUTH
ACL::Prototype ACLProxyAuth::UserRegistryProtoype(&ACLProxyAuth::UserRegistryEntry_, "proxy_auth");
ACLProxyAuth ACLProxyAuth::UserRegistryEntry_(new ACLUserData, "proxy_auth");
ACL::Prototype ACLProxyAuth::RegexRegistryProtoype(&ACLProxyAuth::RegexRegistryEntry_, "proxy_auth_regex" );
=== modified file 'src/acl/Certificate.cc'
--- src/acl/Certificate.cc 2012-09-01 14:38:36 +0000
+++ src/acl/Certificate.cc 2012-11-13 18:18:05 +0000
@@ -36,32 +36,35 @@
/* MS Visual Studio Projects are monolithic, so we need the following
* #if to exclude the SSL code from compile process when not needed.
*/
#if USE_SSL
#include "acl/Certificate.h"
#include "acl/Checklist.h"
#include "acl/CertificateData.h"
#include "HttpRequest.h"
#include "client_side.h"
#include "fde.h"
#include "globals.h"
int
ACLCertificateStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
{
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 *
ACLCertificateStrategy::Instance()
{
return &Instance_;
}
ACLCertificateStrategy ACLCertificateStrategy::Instance_;
#endif /* USE_SSL */
=== modified file 'src/acl/Certificate.h'
--- src/acl/Certificate.h 2012-09-01 14:38:36 +0000
+++ src/acl/Certificate.h 2012-11-13 18:18:05 +0000
@@ -23,50 +23,50 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*
* Copyright (c) 2003, Robert Collins <[email protected]>
*/
#ifndef SQUID_ACLCERTIFICATE_H
#define SQUID_ACLCERTIFICATE_H
#include "acl/Acl.h"
#include "acl/Data.h"
#include "acl/Checklist.h"
#include "ssl/support.h"
#include "acl/Strategised.h"
/// \ingroup ACLAPI
-class ACLCertificateStrategy : public ACLStrategy<SSL *>
+class ACLCertificateStrategy : public ACLStrategy<X509 *>
{
public:
virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *);
static ACLCertificateStrategy *Instance();
/* Not implemented to prevent copies of the instance. */
/* Not private to prevent brain dead g+++ warnings about
* private constructors with no friends */
ACLCertificateStrategy(ACLCertificateStrategy const &);
private:
static ACLCertificateStrategy Instance_;
ACLCertificateStrategy() {}
ACLCertificateStrategy&operator=(ACLCertificateStrategy const &);
};
/// \ingroup ACLAPI
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 */
=== modified file 'src/acl/CertificateData.cc'
--- src/acl/CertificateData.cc 2012-09-01 14:38:36 +0000
+++ src/acl/CertificateData.cc 2012-11-13 21:13:25 +0000
@@ -18,116 +18,160 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*
* Copyright (c) 2003, Robert Collins <[email protected]>
*/
#include "squid.h"
#include "acl/CertificateData.h"
#include "acl/Checklist.h"
+#include "Debug.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);
}
template<class T>
inline void
xRefFree(T &thing)
{
xfree (thing);
}
ACLCertificateData::~ACLCertificateData()
{
safe_free (attribute);
}
template<class T>
inline int
splaystrcmp (T&l, T&r)
{
return strcmp ((char *)l,(char *)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;
return values.match(value);
}
static void
aclDumpAttributeListWalkee(char * const & node_data, void *outlist)
{
/* outlist is really a wordlist ** */
wordlistAdd((wordlist **)outlist, node_data);
}
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.
*/
/* XXX FIXME: don't break abstraction */
values.values->walk(aclDumpAttributeListWalkee, &wl);
return wl;
}
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();
}
bool
ACLCertificateData::empty() const
{
return values.empty();
}
-ACLData<SSL *> *
+ACLData<X509 *> *
ACLCertificateData::clone() const
{
/* Splay trees don't clone yet. */
return new ACLCertificateData(*this);
}
=== modified file 'src/acl/CertificateData.h'
--- src/acl/CertificateData.h 2012-09-01 14:38:36 +0000
+++ src/acl/CertificateData.h 2012-11-13 18:18:05 +0000
@@ -21,48 +21,60 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*
* Copyright (c) 2003, Robert Collins <[email protected]>
*/
#ifndef SQUID_ACLCERTIFICATEDATA_H
#define SQUID_ACLCERTIFICATEDATA_H
#include "splay.h"
#include "acl/Acl.h"
#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);
#endif /* SQUID_ACLCERTIFICATEDATA_H */
=== modified file 'src/acl/FilledChecklist.h'
--- src/acl/FilledChecklist.h 2012-10-03 07:34:10 +0000
+++ src/acl/FilledChecklist.h 2012-11-13 18:18:05 +0000
@@ -56,40 +56,42 @@
Ip::Address src_addr;
Ip::Address dst_addr;
Ip::Address my_addr;
CachePeer *dst_peer;
char *dst_rdns;
HttpRequest *request;
HttpReply *reply;
char rfc931[USER_IDENT_SZ];
#if USE_AUTH
Auth::UserRequest::Pointer auth_user_request;
#endif
#if SQUID_SNMP
char *snmp_community;
#endif
#if USE_SSL
/// SSL [certificate validation] errors, in undefined order
Ssl::Errors *sslErrors;
+ /// The peer certificate
+ Ssl::X509_Pointer serverCert;
#endif
ExternalACLEntry *extacl_entry;
private:
ConnStateData * conn_; /**< hack for ident and NTLM */
int fd_; /**< may be available when conn_ is not */
bool destinationDomainChecked_;
bool sourceDomainChecked_;
/// not implemented; will cause link failures if used
ACLFilledChecklist(const ACLFilledChecklist &);
/// not implemented; will cause link failures if used
ACLFilledChecklist &operator=(const ACLFilledChecklist &);
CBDATA_CLASS(ACLFilledChecklist);
};
/// convenience and safety wrapper for dynamic_cast<ACLFilledChecklist*>
inline
ACLFilledChecklist *Filled(ACLChecklist *checklist)
=== modified file 'src/acl/Makefile.am'
--- src/acl/Makefile.am 2012-10-29 01:31:29 +0000
+++ src/acl/Makefile.am 2012-11-13 18:18:05 +0000
@@ -105,39 +105,41 @@
UrlPort.cc \
UrlPort.h \
UserData.cc \
UserData.h \
AclNameList.h \
AclDenyInfoList.h \
Gadgets.cc \
Gadgets.h \
AclSizeLimit.h
## Add conditional sources
## TODO: move these to their respectful dirs when those dirs are created
EXTRA_libacls_la_SOURCES =
SSL_ACLS = \
CertificateData.cc \
CertificateData.h \
Certificate.cc \
Certificate.h \
+ ServerCertificate.cc \
+ ServerCertificate.h \
SslError.cc \
SslError.h \
SslErrorData.cc \
SslErrorData.h
if ENABLE_SSL
libacls_la_SOURCES += $(SSL_ACLS)
endif
EXTRA_libacls_la_SOURCES += $(SSL_ACLS)
ARP_ACLS = Arp.cc Arp.h Eui64.cc Eui64.h
if USE_SQUID_EUI
libacls_la_SOURCES += $(ARP_ACLS)
endif
EXTRA_libacls_la_SOURCES += $(ARP_ACLS)
=== added file 'src/acl/ServerCertificate.cc'
--- src/acl/ServerCertificate.cc 1970-01-01 00:00:00 +0000
+++ src/acl/ServerCertificate.cc 2012-11-13 17:28:30 +0000
@@ -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 */
=== added file 'src/acl/ServerCertificate.h'
--- src/acl/ServerCertificate.h 1970-01-01 00:00:00 +0000
+++ src/acl/ServerCertificate.h 2012-11-13 17:28:30 +0000
@@ -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 */
=== modified file 'src/acl/StringData.cc'
--- src/acl/StringData.cc 2012-09-01 14:38:36 +0000
+++ src/acl/StringData.cc 2012-11-13 18:18:05 +0000
@@ -49,40 +49,46 @@
template<class T>
inline void
xRefFree(T &thing)
{
xfree (thing);
}
ACLStringData::~ACLStringData()
{
if (values)
values->destroy(xRefFree);
}
static int
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)
{
if (!values || !toFind)
return 0;
debugs(28, 3, "aclMatchStringList: checking '" << toFind << "'");
values = values->splay((char *)toFind, splaystrcmp);
debugs(28, 3, "aclMatchStringList: '" << toFind << "' " << (splayLastResult ? "NOT found" : "found"));
return !splayLastResult;
}
static void
aclDumpStringWalkee(char * const & node_data, void *outlist)
{
/* outlist is really a wordlist ** */
wordlistAdd((wordlist **)outlist, node_data);
=== modified file 'src/acl/StringData.h'
--- src/acl/StringData.h 2012-09-01 14:38:36 +0000
+++ src/acl/StringData.h 2012-11-13 18:18:05 +0000
@@ -35,29 +35,31 @@
#define SQUID_ACLSTRINGDATA_H
#include "splay.h"
#include "acl/Acl.h"
#include "acl/Data.h"
class ACLStringData : public ACLData<char const *>
{
public:
MEMPROXY_CLASS(ACLStringData);
ACLStringData();
ACLStringData(ACLStringData const &);
ACLStringData &operator= (ACLStringData const &);
virtual ~ACLStringData();
bool match(char const *);
wordlist *dump();
void parse();
bool empty() const;
virtual ACLData<char const *> *clone() const;
+ /// Insert custom values
+ void insert(const char *);
SplayNode<char *> *values;
};
/* TODO move into .cci files */
MEMPROXY_CLASS_INLINE(ACLStringData);
#endif /* SQUID_ACLSTRINGDATA_H */
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2012-11-13 18:13:50 +0000
+++ src/cf.data.pre 2012-11-13 18:18:05 +0000
@@ -895,40 +895,49 @@
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 following can be used as shortcuts for certificate properties:
# [ssl::]certHasExpired: the "not after" field is in the past
# [ssl::]certNotYetValid: the "not before" field is in the future
# [ssl::]certUntrusted: The certificate issuer is not to be trusted.
# [ssl::]certSelfSigned: The certificate is self signed.
# [ssl::]certDomainMismatch: The certificate CN domain does not
# match the name the name of the host we are connecting to.
#
# The ssl::certHasExpired, ssl::certNotYetValid, ssl::certDomainMismatch,
# ssl::certUntrusted, and ssl::certSelfSigned can also be used as
# predefined ACLs, just like the 'all' ACL.
#
# 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:
acl macaddress arp 09:00:2b:23:45:67
acl myexample dst_as 1241
acl password proxy_auth REQUIRED
acl fileupload req_mime_type -i ^multipart/form-data$
acl javascript rep_mime_type -i ^application/x-javascript$
NOCOMMENT_START
#
# Recommended minimum configuration:
#
# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc 2012-11-13 18:13:50 +0000
+++ src/ssl/support.cc 2012-11-13 18:18:05 +0000
@@ -263,48 +263,50 @@
Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors));
if (!errs) {
errs = new Ssl::Errors(error_no);
if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_errors, (void *)errs)) {
debugs(83, 2, "Failed to set ssl error_no in ssl_verify_cb: Certificate " << buffer);
delete errs;
errs = NULL;
}
} else // remember another error number
errs->push_back_unique(error_no);
if (const char *err_descr = Ssl::GetErrorDescr(error_no))
debugs(83, 5, err_descr << ": " << buffer);
else
debugs(83, DBG_IMPORTANT, "SSL unknown certificate error " << error_no << " in " << buffer);
if (check) {
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;
} else {
debugs(83, 5, "confirming SSL error " << error_no);
}
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
// pass them to certficate validator for more processing
else if (Ssl::TheConfig.ssl_crt_validator)
ok = 1;
#endif
}
if (!dont_verify_domain && server) {}
if (!ok && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
// Find the broken certificate. It may be intermediate.
X509 *broken_cert = peer_cert; // reasonable default if search fails
// Our SQUID_X509_V_ERR_DOMAIN_MISMATCH implies peer_cert is at fault.
if (error_no != SQUID_X509_V_ERR_DOMAIN_MISMATCH) {
if (X509 *last_used_cert = X509_STORE_CTX_get_current_cert(ctx))
broken_cert = last_used_cert;
}
@@ -1156,86 +1158,120 @@
if (strcmp(attribute_name, "DN") == 0) {
X509_NAME_oneline(name, buffer, sizeof(buffer));
goto done;
}
nid = OBJ_txt2nid((char *) attribute_name);
if (nid == 0) {
debugs(83, DBG_IMPORTANT, "WARNING: Unknown SSL attribute name '" << attribute_name << "'");
return NULL;
}
X509_NAME_get_text_by_NID(name, nid, buffer, sizeof(buffer));
done:
return *buffer ? buffer : NULL;
}
/// \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;
name = X509_get_subject_name(cert);
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;
name = X509_get_issuer_name(cert);
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 *
sslGetUserEmail(SSL * ssl)
{
return sslGetUserAttribute(ssl, "emailAddress");
}
const char *
sslGetUserCertificatePEM(SSL *ssl)
{
X509 *cert;
BIO *mem;
static char *str = NULL;
char *ptr;
long len;
safe_free(str);
if (!ssl)
=== modified file 'src/ssl/support.h'
--- src/ssl/support.h 2012-10-08 05:21:11 +0000
+++ src/ssl/support.h 2012-11-13 18:18:05 +0000
@@ -78,56 +78,65 @@
/// \ingroup ServerProtocolSSLAPI
SSL_CTX *sslCreateServerContext(AnyP::PortCfg &port);
/// \ingroup ServerProtocolSSLAPI
SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile);
/// \ingroup ServerProtocolSSLAPI
int ssl_read_method(int, char *, int);
/// \ingroup ServerProtocolSSLAPI
int ssl_write_method(int, const char *, int);
/// \ingroup ServerProtocolSSLAPI
void ssl_shutdown_method(SSL *ssl);
/// \ingroup ServerProtocolSSLAPI
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);
/// \ingroup ServerProtocolSSLAPI
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
*/
enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpEnd};
/**
\ingroup ServerProtocolSSLAPI
* Short names for ssl-bump modes
*/
extern const char *BumpModeStr[];
/**
\ingroup ServerProtocolSSLAPI
* Return the short name of the ssl-bump mode "bm"
*/
inline const char *bumpMode(int bm)
{
return (0 <= bm && bm < Ssl::bumpEnd) ? Ssl::BumpModeStr[bm] : NULL;
}
=== modified file 'src/tests/stub_libsslsquid.cc'
--- src/tests/stub_libsslsquid.cc 2012-09-20 16:26:47 +0000
+++ src/tests/stub_libsslsquid.cc 2012-11-13 17:28:30 +0000
@@ -31,31 +31,31 @@
void Ssl::GlobalContextStorage::addLocalStorage(Ip::Address const & address, size_t size_of_store) STUB
Ssl::LocalContextStorage & Ssl::GlobalContextStorage::getLocalStorage(Ip::Address const & address)
{ fatal(STUB_API " required"); static Ssl::LocalContextStorage v(0); return v; }
void Ssl::GlobalContextStorage::reconfigureStart() STUB
//Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage;
#include "ssl/ErrorDetail.h"
Ssl::ssl_error_t parseErrorString(const char *name) STUB_RETVAL(0)
//const char *Ssl::getErrorName(ssl_error_t value) STUB_RETVAL(NULL)
Ssl::ErrorDetail::ErrorDetail(ssl_error_t err_no, X509 *, X509 *, const char *) STUB
Ssl::ErrorDetail::ErrorDetail(ErrorDetail const &) STUB
const String & Ssl::ErrorDetail::toString() const STUB_RETSTATREF(String)
#include "ssl/support.h"
SSL_CTX *sslCreateServerContext(AnyP::PortCfg &) STUB_RETVAL(NULL)
SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile) STUB_RETVAL(NULL)
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)
SSL_CTX * Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data, AnyP::PortCfg &) STUB_RETVAL(NULL)
int Ssl::matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data, ASN1_STRING *cn_data)) STUB_RETVAL(0)
int Ssl::asn1timeToString(ASN1_TIME *tm, char *buf, int len) STUB_RETVAL(0)
#endif