From: Christos Tsantilas Date: Thu, 2 Feb 2012 10:53:08 +0000 (+0200) Subject: Server certificate testing ACLs X-Git-Tag: BumpSslServerFirst.take05~31 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cf1c09f6c73b4e5dd04222444184d3ab738e040c;p=thirdparty%2Fsquid.git Server certificate testing ACLs This patch add the following ACLs to test the condition of the origin server certificate: ssl::certHasExpired, ssl::certNotYetValid, ssl::certDomainMismatch, ssl::certUntrusted and ssl::certSelfSigned. The above in this patch are predifined acl lists or/and can be used as error name shortcuts with ssl_error ACL lists Implementation details: 1) The ssl::certHasExpired, ssl::certNotYetValid, ssl::certDomainMismatch ssl::certUntrusted and ssl::certSelfSign acl lists are predifined in cf.data.pre 2) The above names can also used as error names. The ssl-error parser (Ssl::ParseErrorString function) replace them with the appropriate SSL error list. 3) The Ssl::ParseErrorString function modified to return a list of errors, not just an error code. 4) I implement the IFDEF /ENDIF block support for cf.data.pre file. --- diff --git a/include/CbDataList.h b/include/CbDataList.h index cdb6e2bfda..8e221e20e2 100644 --- a/include/CbDataList.h +++ b/include/CbDataList.h @@ -47,6 +47,8 @@ public: bool push_back_unique(C const &); bool find(C const &)const; bool findAndTune(C const &); + /// iterates entire list to return the last element holder + CbDataList *tail(); CbDataList *next; C element; bool empty() const { return this == NULL; } @@ -140,6 +142,16 @@ CbDataList::push_back_unique(C const &toAdd) return true; } +template +CbDataList * +CbDataList::tail() +{ + CbDataList *last; + for (last = this; last->next; last = last->next); + return last; +} + + template bool CbDataList::find (C const &toFind) const diff --git a/src/Makefile.am b/src/Makefile.am index 2dd33acbe8..6f285e4050 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -937,6 +937,7 @@ cf.data: cf.data.pre Makefile -e "s%[@]DEFAULT_SSL_DB_DIR[@]%$(DEFAULT_SSL_DB_DIR)%g" \ -e "s%[@]DEFAULT_ICON_DIR[@]%$(DEFAULT_ICON_DIR)%g" \ -e "s%[@]DEFAULT_CONFIG_DIR[@]%$(DEFAULT_CONFIG_DIR)%g" \ + -e "s%[@]DEFAULT_ERROR_DIR[@]%$(DEFAULT_ERROR_DIR)%g" \ -e "s%[@]DEFAULT_PREFIX[@]%$(DEFAULT_PREFIX)%g" \ -e "s%[@]DEFAULT_HOSTS[@]%$(DEFAULT_HOSTS)%g" \ -e "s%[@]SQUID[@]%SQUID\ $(VERSION)%g" \ diff --git a/src/acl/SslErrorData.cc b/src/acl/SslErrorData.cc index 0abeb2daa2..daf4756602 100644 --- a/src/acl/SslErrorData.cc +++ b/src/acl/SslErrorData.cc @@ -60,9 +60,9 @@ ACLSslErrorData::parse() for (Tail = &values; *Tail; Tail = &((*Tail)->next)); while ((t = strtokFile())) { - Ssl::Errors *q = new Ssl::Errors(Ssl::ParseErrorString(t)); + Ssl::Errors *q = Ssl::ParseErrorString(t); *(Tail) = q; - Tail = &q->next; + Tail = &q->tail()->next; } } diff --git a/src/cf.data.pre b/src/cf.data.pre index 0b1a2c4a4a..f1f54d09c0 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -634,6 +634,13 @@ DOC_END NAME: acl TYPE: acl LOC: Config.aclList +IFDEF USE_SSL +DEFAULT: ssl::certHasExpired ssl_error X509_V_ERR_CERT_HAS_EXPIRED +DEFAULT: ssl::certNotYetValid ssl_error X509_V_ERR_CERT_NOT_YET_VALID +DEFAULT: ssl::certDomainMismatch ssl_error SQUID_X509_V_ERR_DOMAIN_MISMATCH +DEFAULT: ssl::certUntrusted ssl_error X509_V_ERR_INVALID_CA X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY X509_V_ERR_CERT_UNTRUSTED +DEFAULT: ssl::certSelfSigned ssl_error X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT +ENDIF DEFAULT: all src all DEFAULT: manager url_regex -i ^cache_object:// +i ^https?://[^/]+/squid-internal-mgr/ DEFAULT: localhost src 127.0.0.1/32 ::1 @@ -858,6 +865,30 @@ DOC_START # effect in rules that affect the reply data stream such as # http_reply_access. +IFDEF USE_SSL + 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 user aditionaly can use as error name the following error name + # shortcuts: + # [ssl::]certHasExpired: certificate "not after" field is in the past + # [ssl::]certNotYetValid: certificate "not before" field is in the + # future + # [ssl::]certDomainMismatch: The certificate CN domain does not match + # connecting host name + # [ssl::]certUntrusted: The certificate is untrusted because of an + # error says that the certificate issuer is not trusted. + # [ssl::]certSelfSigned: The certificate is self signed + # + # The ssl::certHasExpired, ssl::certNotYetValid ssl::certDomainMismatch, + # ssl::certUntrusted and ssl::certSelfSigned also exists as predefined + # acl lists. + # + # NOTE: The ssl_error acl has effect only when used with + # sslproxy_cert_error, sslproxy_cert_sign and sslproxy_cert_adapt + # access lists. +ENDIF + Examples: acl macaddress arp 09:00:2b:23:45:67 acl myexample dst_as 1241 @@ -2072,8 +2103,13 @@ DOC_START generate a X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT error in the browser. - When the acl(s) match, the corresponding signing algorithm is used to - generate the certificate. Otherwise, the default signing algorithm used + This clause only supports fast acl types. + + When the acl(s) match, the corresponding signing algorithm is used to + generate the certificate. Otherwise, the default signing algorithm used + + BUG: The SQUID_X509_V_ERR_DOMAIN_MISMATCH and ssl:certDomainMismatch ssl + errors can not be used with ssl_error acl type. DOC_END NAME: sslproxy_cert_adapt @@ -2100,9 +2136,14 @@ DOC_START without an explicit parameter for intercepted or tproxied SSL transactions. + This clause only supports fast acl types. + When the acl(s) match, the corresponding adaptation algorithm is applied to the fake/generated certificate. Otherwise, the default mimicking action takes place. + + BUG: The SQUID_X509_V_ERR_DOMAIN_MISMATCH and ssl:certDomainMismatch ssl + errors can not be used with ssl_error acl type DOC_END NAME: sslpassword_program diff --git a/src/cf_gen.cc b/src/cf_gen.cc index dfac371586..e61dc5dd45 100644 --- a/src/cf_gen.cc +++ b/src/cf_gen.cc @@ -64,6 +64,7 @@ _FILE_OFFSET_BITS==64 #include #include #include +#include #include "cf_gen_defines.cci" @@ -151,6 +152,7 @@ static void gen_dump(const EntryList &, std::ostream&); static void gen_free(const EntryList &, std::ostream&); static void gen_conf(const EntryList &, std::ostream&, bool verbose_output); static void gen_default_if_none(const EntryList &, std::ostream&); +static bool isDefined(const std::string &name); static void checkDepend(const std::string &directive, const char *name, const TypeList &types, const EntryList &entries) @@ -198,6 +200,7 @@ main(int argc, char *argv[]) char *ptr = NULL; char buff[MAX_LINE]; std::ifstream fp; + std::stack IFDEFS; if (argc != 3) usage(argv[0]); @@ -252,6 +255,22 @@ main(int argc, char *argv[]) if ((t = strchr(buff, '\n'))) *t = '\0'; + if(strncmp(buff, "IFDEF ", 6) == 0) { + if ((ptr = strtok(buff + 6, WS)) == NULL) { + std::cerr << "Missing IFDEF parameter on line" << linenum << std::endl; + exit(1); + } + IFDEFS.push(ptr); + std::cerr << "Entering IFDEF " << ptr << std::endl; + continue; + } else if (strcmp(buff, "ENDIF") == 0) { + if (IFDEFS.size() == 0) { + std::cerr << "ENDIF without IFDEF before on line " << linenum << std::endl; + exit(1); + } + IFDEFS.pop(); + } + else if (!IFDEFS.size() || isDefined(IFDEFS.top())) switch (state) { case sSTART: diff --git a/src/ssl/ErrorDetail.cc b/src/ssl/ErrorDetail.cc index 74ce22e117..ca20d65f26 100644 --- a/src/ssl/ErrorDetail.cc +++ b/src/ssl/ErrorDetail.cc @@ -90,6 +90,44 @@ static SslErrorEntry TheSslErrorArray[] = { {SSL_ERROR_NONE, NULL} }; +struct SslErrorAlias { + const char *name; + const Ssl::ssl_error_t *errors; +}; + +static const Ssl::ssl_error_t hasExpired[] = {X509_V_ERR_CERT_HAS_EXPIRED, SSL_ERROR_NONE}; +static const Ssl::ssl_error_t notYetValid[] = {X509_V_ERR_CERT_NOT_YET_VALID, SSL_ERROR_NONE}; +static const Ssl::ssl_error_t domainMismatch[] = {SQUID_X509_V_ERR_DOMAIN_MISMATCH, SSL_ERROR_NONE}; +static const Ssl::ssl_error_t certUntrusted[] = {X509_V_ERR_INVALID_CA, + X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, + X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + X509_V_ERR_CERT_UNTRUSTED, SSL_ERROR_NONE}; +static const Ssl::ssl_error_t certSelfSigned[] = {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, SSL_ERROR_NONE}; + +// The list of error name shortcuts for use with ssl_error acls. +// The keys without the "ssl::" scope prefix allow shorter error +// names within the SSL options scope. This is easier than +// carefully stripping the scope prefix in Ssl::ParseErrorString() +static SslErrorAlias TheSslErrorShortcutsArray[] = { + {"ssl::certHasExpired", hasExpired}, + {"certHasExpired", hasExpired}, + {"ssl::certNotYetValid", notYetValid}, + {"certNotYetValid", notYetValid}, + {"ssl::certDomainMismatch", domainMismatch}, + {"certDomainMismatch", domainMismatch}, + {"ssl::certUntrusted", certUntrusted}, + {"certUntrusted", certUntrusted}, + {"ssl::certSelfSigned", certSelfSigned}, + {"certSelfSigned", certSelfSigned}, + {NULL, NULL} +}; + +//Use std::map to optimize search +typedef std::map SslErrorShortcuts; +SslErrorShortcuts TheSslErrorShortcuts; + static void loadSslErrorMap() { assert(TheSslErrors.empty()); @@ -98,6 +136,13 @@ static void loadSslErrorMap() } } +static void loadSslErrorShortcutsMap() +{ + assert(TheSslErrorShortcuts.empty()); + for (int i = 0; TheSslErrorShortcutsArray[i].name; i++) + TheSslErrorShortcuts[TheSslErrorShortcutsArray[i].name] = TheSslErrorShortcutsArray[i].errors; +} + Ssl::ssl_error_t Ssl::GetErrorCode(const char *name) { for (int i = 0; TheSslErrorArray[i].name != NULL; i++) { @@ -107,24 +152,38 @@ Ssl::ssl_error_t Ssl::GetErrorCode(const char *name) return SSL_ERROR_NONE; } -Ssl::ssl_error_t +Ssl::Errors * Ssl::ParseErrorString(const char *name) { assert(name); const Ssl::ssl_error_t ssl_error = GetErrorCode(name); if (ssl_error != SSL_ERROR_NONE) - return ssl_error; + return new Ssl::Errors(ssl_error); if (xisdigit(*name)) { const long int value = strtol(name, NULL, 0); if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX) - return value; + return new Ssl::Errors(value); fatalf("Too small or too bug SSL error code '%s'", name); } + if (TheSslErrorShortcuts.empty()) + loadSslErrorShortcutsMap(); + + const SslErrorShortcuts::const_iterator it = TheSslErrorShortcuts.find(name); + if (it != TheSslErrorShortcuts.end()) { + // Should not be empty... + assert(it->second[0] != SSL_ERROR_NONE); + Ssl::Errors *errors = new Ssl::Errors(it->second[0]); + for (int i =1; it->second[i] != SSL_ERROR_NONE; i++) { + errors->push_back_unique(it->second[i]); + } + return errors; + } + fatalf("Unknown SSL error name '%s'", name); - return SSL_ERROR_SSL; // not reached + return NULL; // not reached } const char *Ssl::GetErrorName(Ssl::ssl_error_t value) diff --git a/src/ssl/ErrorDetail.h b/src/ssl/ErrorDetail.h index 186c006630..b26a87a66e 100644 --- a/src/ssl/ErrorDetail.h +++ b/src/ssl/ErrorDetail.h @@ -15,10 +15,12 @@ namespace Ssl { /** \ingroup ServerProtocolSSLAPI - * The ssl_error_t representation of the error described by "name". + * The Ssl::Errors representation of the error described by "name". + * The result may be a single element of a list of errors, and needs to be + * released by the caller. * This function also parses numeric arguments. */ -ssl_error_t ParseErrorString(const char *name); +Ssl::Errors *ParseErrorString(const char *name); /** \ingroup ServerProtocolSSLAPI