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 ACLBrowser::RegistryEntry_(new ACLRegexData, ACLRequestHeaderStrategy::Instance(), "browser"); ACL::Prototype ACLDestinationDomain::LiteralRegistryProtoype(&ACLDestinationDomain::LiteralRegistryEntry_, "dstdomain"); ACLStrategised ACLDestinationDomain::LiteralRegistryEntry_(new ACLDomainData, ACLDestinationDomainStrategy::Instance(), "dstdomain"); @@ -126,43 +129,45 @@ ACL::Prototype ACLSourceDomain::RegexRegistryProtoype(&ACLSourceDomain::RegexRegistryEntry_, "srcdom_regex"); ACLStrategised 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 ACLTime::RegistryEntry_(new ACLTimeData, ACLTimeStrategy::Instance(), "time"); ACL::Prototype ACLUrl::RegistryProtoype(&ACLUrl::RegistryEntry_, "url_regex"); ACLStrategised ACLUrl::RegistryEntry_(new ACLRegexData, ACLUrlStrategy::Instance(), "url_regex"); ACL::Prototype ACLUrlLogin::RegistryProtoype(&ACLUrlLogin::RegistryEntry_, "urllogin"); ACLStrategised ACLUrlLogin::RegistryEntry_(new ACLRegexData, ACLUrlLoginStrategy::Instance(), "urllogin"); ACL::Prototype ACLUrlPath::LegacyRegistryProtoype(&ACLUrlPath::RegistryEntry_, "pattern"); ACL::Prototype ACLUrlPath::RegistryProtoype(&ACLUrlPath::RegistryEntry_, "urlpath_regex"); ACLStrategised ACLUrlPath::RegistryEntry_(new ACLRegexData, ACLUrlPathStrategy::Instance(), "urlpath_regex"); ACL::Prototype ACLUrlPort::RegistryProtoype(&ACLUrlPort::RegistryEntry_, "port"); ACLStrategised ACLUrlPort::RegistryEntry_(new ACLIntRange, ACLUrlPortStrategy::Instance(), "port"); #if USE_SSL ACL::Prototype ACLSslError::RegistryProtoype(&ACLSslError::RegistryEntry_, "ssl_error"); ACLStrategised ACLSslError::RegistryEntry_(new ACLSslErrorData, ACLSslErrorStrategy::Instance(), "ssl_error"); ACL::Prototype ACLCertificate::UserRegistryProtoype(&ACLCertificate::UserRegistryEntry_, "user_cert"); -ACLStrategised ACLCertificate::UserRegistryEntry_(new ACLCertificateData (sslGetUserAttribute), ACLCertificateStrategy::Instance(), "user_cert"); +ACLStrategised ACLCertificate::UserRegistryEntry_(new ACLCertificateData (Ssl::GetX509UserAttribute, "*"), ACLCertificateStrategy::Instance(), "user_cert"); ACL::Prototype ACLCertificate::CARegistryProtoype(&ACLCertificate::CARegistryEntry_, "ca_cert"); -ACLStrategised ACLCertificate::CARegistryEntry_(new ACLCertificateData (sslGetCAAttribute), ACLCertificateStrategy::Instance(), "ca_cert"); +ACLStrategised ACLCertificate::CARegistryEntry_(new ACLCertificateData (Ssl::GetX509CAAttribute, "*"), ACLCertificateStrategy::Instance(), "ca_cert"); +ACL::Prototype ACLServerCertificate::X509FingerprintRegistryProtoype(&ACLServerCertificate::X509FingerprintRegistryEntry_, "server_ssl_cert_fingerprint"); +ACLStrategised 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 * &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 */ #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 +class ACLCertificateStrategy : public ACLStrategy { public: virtual int match (ACLData * &, 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 UserRegistryEntry_; + static ACLStrategised UserRegistryEntry_; static ACL::Prototype CARegistryProtoype; - static ACLStrategised CARegistryEntry_; + static ACLStrategised 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 */ #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 inline void xRefFree(T &thing) { xfree (thing); } ACLCertificateData::~ACLCertificateData() { safe_free (attribute); } template 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::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 * +ACLData * 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 */ #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 +#include /// \ingroup ACLAPI -class ACLCertificateData : public ACLData +class ACLCertificateData : public ACLData { 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 *clone() const; + virtual ACLData *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 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 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 * &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 +{ + +public: + virtual int match (ACLData * &, 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 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 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 { 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 *clone() const; + /// Insert custom values + void insert(const char *); SplayNode *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_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