1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
#include "fetchmail.h"
#include <string.h>
#include <strings.h>
/** 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 we decided above that wildcarding is still OK,
* try a wildcard match first - providing that
* the wildcard is for a full component,
* i. e. starts with "*." */
if (wildcard_ok && p1[0] == '*' && p1[1] == '.') {
size_t l1, l2;
int number_dots = 0;
const char *tmp;
/* skip over the asterisk */
++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 there are at least 2 dots, do the wildcard match.
* Match from the end by incrementing the p2 pointer by the
* length difference between remainder of pattern and string to
* be matched. */
if (number_dots >= 2) {
l1 = strlen(p1);
l2 = strlen(p2);
if (l2 > l1)
p2 += l2 - l1;
/* FALLTHROUGH */
}
}
/* Now to the match. Either wildcards are forbidden or not found,
* then it's a case-insensitive full-string match, or wildcards are
* permitted and found and we've bumped the start-string pointers
* sufficiently. */
return (0 == strcasecmp(p1, p2));
/* XXX open issue: do we need to deal with trailing dots in patterns
* or domains? A trailing dot is an anchor that prevents resolver
* "search"es to DNS, so might cause false mismatches. */
}
#ifdef TEST
#include <stdlib.h>
#include <stdio.h>
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
|