From 8d10076819e41620147013c8446809c085b1e0bf Mon Sep 17 00:00:00 2001 From: Matthias Andree Date: Fri, 27 Aug 2010 21:33:04 +0200 Subject: X.509 matching split out; disallow TLD wildcards. --- Makefile.am | 2 +- NEWS | 1 + fetchmail.h | 3 ++ socket.c | 36 ----------------------- x509_name_match.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 37 deletions(-) create mode 100644 x509_name_match.c diff --git a/Makefile.am b/Makefile.am index 0e597d05..6a12786b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,7 @@ libfm_a_SOURCES= xmalloc.c base64.c rfc822.c report.c rfc2047e.c \ smbencrypt.h smbdes.c smbencrypt.c smbmd4.c smbutil.c \ libesmtp/gethostbyname.h libesmtp/gethostbyname.c \ smbtypes.h fm_getaddrinfo.c tls.c rfc822valid.c \ - xmalloc.h sdump.h sdump.c + xmalloc.h sdump.h sdump.c x509_name_match.c libfm_a_LIBADD= $(EXTRAOBJ) libfm_a_DEPENDENCIES= $(EXTRAOBJ) LDADD = libfm.a @LIBINTL@ $(LIBOBJS) diff --git a/NEWS b/NEWS index a63a3d9c..06e3f855 100644 --- a/NEWS +++ b/NEWS @@ -63,6 +63,7 @@ fetchmail-6.3.18 (not yet released): The test is overly picky and triggers if the pattern (after skipping the initial wildcard "*") or domain consist solely of digits and dots and matches more than needed. +* Fetchmail now disallows wildcarding top-level domains. # BUG FIXES * Fetchmail would warn about insecure SSL/TLS connections even if a matching diff --git a/fetchmail.h b/fetchmail.h index 0315a9da..9502d1e3 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -772,6 +772,9 @@ int must_tls(struct query *ctl); /* prototype from rfc822valid.c */ int rfc822_valid_msgid(const unsigned char *); +/* prototype from x509_name_match.c */ +int name_match(const char *p1, const char *p2); + /* macro to determine if we want to spam progress to stdout */ #define want_progress() \ ((outlevel >= O_VERBOSE || (outlevel > O_SILENT && run.showdots)) \ diff --git a/socket.c b/socket.c index d3cf90d7..2a3e1ba1 100644 --- a/socket.c +++ b/socket.c @@ -593,42 +593,6 @@ SSL *SSLGetContext( int sock ) return _ssl_context[sock]; } -/** A picky certificate name check: - * check if the pattern or string in s1 (from a certificate) matches the - * hostname (in s2), returns true if matched. - * - * The only place where a wildcard is allowed is in the leftmost - * position of p1. */ -static int name_match(const char *p1, const char *p2) { - const char *const dom = "0123456789."; - int wildcard_ok = 1; - - /* blank patterns never match */ - if (p1[0] == '\0') - return 0; - - /* disallow wildcards in certificates for domain literals - * (10.9.8.7-like) */ - if (strspn(p1+(*p1 == '*' ? 1 : 0), dom) == strlen(p1)) - wildcard_ok = 0; - - /* disallow wildcards for domain literals */ - if (strspn(p2, dom) == strlen(p2)) - wildcard_ok = 0; - - if (wildcard_ok && p1[0] == '*' && p1[1] == '.') { - size_t l1, l2; - - ++p1; - l1 = strlen(p1); - l2 = strlen(p2); - if (l2 > l1) - p2 += l2 - l1; - } - - return (0 == strcasecmp(p1, p2)); -} - /* ok_return (preverify_ok) is 1 if this stage of certificate verification passed, or 0 if it failed. This callback lets us display informative errors, and perform additional validation (e.g. CN matches) */ diff --git a/x509_name_match.c b/x509_name_match.c new file mode 100644 index 00000000..ddfee3eb --- /dev/null +++ b/x509_name_match.c @@ -0,0 +1,87 @@ +#include "fetchmail.h" + +#include +#include + +/** A picky certificate name check: + * check if the pattern or string in s1 (from a certificate) matches the + * hostname (in s2), returns true if matched. + * + * The only place where a wildcard is allowed is in the leftmost + * position of p1. */ +int name_match(const char *p1, const char *p2) { + const char *const dom = "0123456789."; + int wildcard_ok = 1; + + /* blank patterns never match */ + if (p1[0] == '\0') + return 0; + + /* disallow wildcards in certificates for domain literals + * (10.9.8.7-like) */ + if (strspn(p1+(*p1 == '*' ? 1 : 0), dom) == strlen(p1)) + wildcard_ok = 0; + + /* disallow wildcards for domain literals */ + if (strspn(p2, dom) == strlen(p2)) + wildcard_ok = 0; + + if (wildcard_ok && p1[0] == '*' && p1[1] == '.') { + size_t l1, l2; + int number_dots = 0; + const char *tmp; + + ++p1; + /* make sure CAs don't wildcard top-level domains by requiring there + * are at least two dots in wildcarded X.509 CN/SANs */ + + for(tmp = p1; *tmp; tmp += strcspn(tmp, ".")) { + if (*tmp == '.') { + ++number_dots; + ++tmp; + } + } + + if (number_dots >= 2) { + l1 = strlen(p1); + l2 = strlen(p2); + if (l2 > l1) + p2 += l2 - l1; + } + } + + return (0 == strcasecmp(p1, p2)); +} + +#ifdef TEST +#include +#include + +static int verbose; + +/* print test and return true on failure */ +static int test(const char *p1, const char *p2, int expect) { + int match = name_match(p1, p2); + if (verbose) + printf("name_match(\"%s\", \"%s\") == %d (%d expected)\n", p1, p2, match, expect); + return expect != match; +} + +int main(int argc, const char **argv) { + int rc = 0; + + if (argc > 1 && 0 == strcmp(argv[1], "-v")) + verbose = 1; + + rc |= test("example.org", "example.org", 1); + rc |= test("*example.org", "foo.example.org", 0); + rc |= test("*.example.org", "foo.example.org", 1); + rc |= test("*.168.23.23", "192.168.23.23", 0); + rc |= test("*.com", "example.com", 0); + if (verbose) { + printf("x509_name_match: "); + puts(rc ? "FAIL" : "PASS"); + } + return rc; +} +#endif -- cgit v1.2.3