]> Pileus Git - ~andy/fetchmail/blob - x509_name_match.c
ddfee3eb5839a5bb9c85968571705d8cd0583ce0
[~andy/fetchmail] / x509_name_match.c
1 #include "fetchmail.h"
2
3 #include <string.h>
4 #include <strings.h>
5
6 /** A picky certificate name check:
7  * check if the pattern or string in s1 (from a certificate) matches the
8  * hostname (in s2), returns true if matched.
9  *
10  * The only place where a wildcard is allowed is in the leftmost
11  * position of p1. */
12 int name_match(const char *p1, const char *p2) {
13     const char *const dom = "0123456789.";
14     int wildcard_ok = 1;
15
16     /* blank patterns never match */
17     if (p1[0] == '\0')
18         return 0;
19
20     /* disallow wildcards in certificates for domain literals
21      * (10.9.8.7-like) */
22     if (strspn(p1+(*p1 == '*' ? 1 : 0), dom) == strlen(p1))
23         wildcard_ok = 0;
24
25     /* disallow wildcards for domain literals */
26     if (strspn(p2, dom) == strlen(p2))
27         wildcard_ok = 0;
28
29     if (wildcard_ok && p1[0] == '*' && p1[1] == '.') {
30         size_t l1, l2;
31         int number_dots = 0;
32         const char *tmp;
33
34         ++p1;
35         /* make sure CAs don't wildcard top-level domains by requiring there
36          * are at least two dots in wildcarded X.509 CN/SANs */
37
38         for(tmp = p1; *tmp; tmp += strcspn(tmp, ".")) {
39             if (*tmp == '.') {
40                 ++number_dots;
41                 ++tmp;
42             }
43         }
44
45         if (number_dots >= 2) {
46             l1 = strlen(p1);
47             l2 = strlen(p2);
48             if (l2 > l1)
49                 p2 += l2 - l1;
50         }
51     }
52
53     return (0 == strcasecmp(p1, p2));
54 }
55
56 #ifdef TEST
57 #include <stdlib.h>
58 #include <stdio.h>
59
60 static int verbose;
61
62 /* print test and return true on failure */
63 static int test(const char *p1, const char *p2, int expect) {
64     int match = name_match(p1, p2);
65     if (verbose)
66         printf("name_match(\"%s\", \"%s\") == %d (%d expected)\n", p1, p2, match, expect);
67     return expect != match;
68 }
69
70 int main(int argc, const char **argv) {
71     int rc = 0;
72
73     if (argc > 1 && 0 == strcmp(argv[1], "-v"))
74         verbose = 1;
75
76     rc |= test("example.org", "example.org", 1);
77     rc |= test("*example.org", "foo.example.org", 0);
78     rc |= test("*.example.org", "foo.example.org", 1);
79     rc |= test("*.168.23.23", "192.168.23.23", 0);
80     rc |= test("*.com", "example.com", 0);
81     if (verbose) {
82         printf("x509_name_match: ");
83         puts(rc ? "FAIL" : "PASS");
84     }
85     return rc;
86 }
87 #endif