1#include <openssl/x509.h>
2#include <openssl/x509v3.h>
3#include "../e_os.h"
4#include <string.h>
5
6static const char *const names[] = {
7    "a", "b", ".", "*", "@",
8    ".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..",
9    "-example.com", "example-.com",
10    "@@", "**", "*.com", "*com", "*.*.com", "*com", "com*", "*example.com",
11    "*@example.com", "test@*.example.com", "example.com", "www.example.com",
12    "test.www.example.com", "*.example.com", "*.www.example.com",
13    "test.*.example.com", "www.*.com",
14    ".www.example.com", "*www.example.com",
15    "example.net", "xn--rger-koa.example.com",
16    "*.xn--rger-koa.example.com", "www.xn--rger-koa.example.com",
17    "*.good--example.com", "www.good--example.com",
18    "*.xn--bar.com", "xn--foo.xn--bar.com",
19    "a.example.com", "b.example.com",
20    "postmaster@example.com", "Postmaster@example.com",
21    "postmaster@EXAMPLE.COM",
22    NULL
23};
24
25static const char *const exceptions[] = {
26    "set CN: host: [*.example.com] matches [a.example.com]",
27    "set CN: host: [*.example.com] matches [b.example.com]",
28    "set CN: host: [*.example.com] matches [www.example.com]",
29    "set CN: host: [*.example.com] matches [xn--rger-koa.example.com]",
30    "set CN: host: [*.www.example.com] matches [test.www.example.com]",
31    "set CN: host: [*.www.example.com] matches [.www.example.com]",
32    "set CN: host: [*www.example.com] matches [www.example.com]",
33    "set CN: host: [test.www.example.com] matches [.www.example.com]",
34    "set CN: host: [*.xn--rger-koa.example.com] matches [www.xn--rger-koa.example.com]",
35    "set CN: host: [*.xn--bar.com] matches [xn--foo.xn--bar.com]",
36    "set CN: host: [*.good--example.com] matches [www.good--example.com]",
37    "set CN: host-no-wildcards: [*.www.example.com] matches [.www.example.com]",
38    "set CN: host-no-wildcards: [test.www.example.com] matches [.www.example.com]",
39    "set emailAddress: email: [postmaster@example.com] does not match [Postmaster@example.com]",
40    "set emailAddress: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]",
41    "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@example.com]",
42    "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
43    "set dnsName: host: [*.example.com] matches [www.example.com]",
44    "set dnsName: host: [*.example.com] matches [a.example.com]",
45    "set dnsName: host: [*.example.com] matches [b.example.com]",
46    "set dnsName: host: [*.example.com] matches [xn--rger-koa.example.com]",
47    "set dnsName: host: [*.www.example.com] matches [test.www.example.com]",
48    "set dnsName: host-no-wildcards: [*.www.example.com] matches [.www.example.com]",
49    "set dnsName: host-no-wildcards: [test.www.example.com] matches [.www.example.com]",
50    "set dnsName: host: [*.www.example.com] matches [.www.example.com]",
51    "set dnsName: host: [*www.example.com] matches [www.example.com]",
52    "set dnsName: host: [test.www.example.com] matches [.www.example.com]",
53    "set dnsName: host: [*.xn--rger-koa.example.com] matches [www.xn--rger-koa.example.com]",
54    "set dnsName: host: [*.xn--bar.com] matches [xn--foo.xn--bar.com]",
55    "set dnsName: host: [*.good--example.com] matches [www.good--example.com]",
56    "set rfc822Name: email: [postmaster@example.com] does not match [Postmaster@example.com]",
57    "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@example.com]",
58    "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]",
59    "set rfc822Name: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]",
60    NULL
61};
62
63static int is_exception(const char *msg)
64{
65    const char *const *p;
66    for (p = exceptions; *p; ++p)
67        if (strcmp(msg, *p) == 0)
68            return 1;
69    return 0;
70}
71
72static int set_cn(X509 *crt, ...)
73{
74    int ret = 0;
75    X509_NAME *n = NULL;
76    va_list ap;
77    va_start(ap, crt);
78    n = X509_NAME_new();
79    if (n == NULL)
80        goto out;
81    while (1) {
82        int nid;
83        const char *name;
84        nid = va_arg(ap, int);
85        if (nid == 0)
86            break;
87        name = va_arg(ap, const char *);
88        if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_ASC,
89                                        (unsigned char *)name, -1, -1, 1))
90            goto out;
91    }
92    if (!X509_set_subject_name(crt, n))
93        goto out;
94    ret = 1;
95 out:
96    X509_NAME_free(n);
97    va_end(ap);
98    return ret;
99}
100
101/*-
102int             X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
103X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
104                        int nid, int crit, ASN1_OCTET_STRING *data);
105int             X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
106*/
107
108static int set_altname(X509 *crt, ...)
109{
110    int ret = 0;
111    GENERAL_NAMES *gens = NULL;
112    GENERAL_NAME *gen = NULL;
113    ASN1_IA5STRING *ia5 = NULL;
114    va_list ap;
115    va_start(ap, crt);
116    gens = sk_GENERAL_NAME_new_null();
117    if (gens == NULL)
118        goto out;
119    while (1) {
120        int type;
121        const char *name;
122        type = va_arg(ap, int);
123        if (type == 0)
124            break;
125        name = va_arg(ap, const char *);
126
127        gen = GENERAL_NAME_new();
128        if (gen == NULL)
129            goto out;
130        ia5 = ASN1_IA5STRING_new();
131        if (ia5 == NULL)
132            goto out;
133        if (!ASN1_STRING_set(ia5, name, -1))
134            goto out;
135        switch (type) {
136        case GEN_EMAIL:
137        case GEN_DNS:
138            GENERAL_NAME_set0_value(gen, type, ia5);
139            ia5 = NULL;
140            break;
141        default:
142            abort();
143        }
144        sk_GENERAL_NAME_push(gens, gen);
145        gen = NULL;
146    }
147    if (!X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0))
148        goto out;
149    ret = 1;
150 out:
151    ASN1_IA5STRING_free(ia5);
152    GENERAL_NAME_free(gen);
153    GENERAL_NAMES_free(gens);
154    va_end(ap);
155    return ret;
156}
157
158static int set_cn1(X509 *crt, const char *name)
159{
160    return set_cn(crt, NID_commonName, name, 0);
161}
162
163static int set_cn_and_email(X509 *crt, const char *name)
164{
165    return set_cn(crt, NID_commonName, name,
166                  NID_pkcs9_emailAddress, "dummy@example.com", 0);
167}
168
169static int set_cn2(X509 *crt, const char *name)
170{
171    return set_cn(crt, NID_commonName, "dummy value",
172                  NID_commonName, name, 0);
173}
174
175static int set_cn3(X509 *crt, const char *name)
176{
177    return set_cn(crt, NID_commonName, name,
178                  NID_commonName, "dummy value", 0);
179}
180
181static int set_email1(X509 *crt, const char *name)
182{
183    return set_cn(crt, NID_pkcs9_emailAddress, name, 0);
184}
185
186static int set_email2(X509 *crt, const char *name)
187{
188    return set_cn(crt, NID_pkcs9_emailAddress, "dummy@example.com",
189                  NID_pkcs9_emailAddress, name, 0);
190}
191
192static int set_email3(X509 *crt, const char *name)
193{
194    return set_cn(crt, NID_pkcs9_emailAddress, name,
195                  NID_pkcs9_emailAddress, "dummy@example.com", 0);
196}
197
198static int set_email_and_cn(X509 *crt, const char *name)
199{
200    return set_cn(crt, NID_pkcs9_emailAddress, name,
201                  NID_commonName, "www.example.org", 0);
202}
203
204static int set_altname_dns(X509 *crt, const char *name)
205{
206    return set_altname(crt, GEN_DNS, name, 0);
207}
208
209static int set_altname_email(X509 *crt, const char *name)
210{
211    return set_altname(crt, GEN_EMAIL, name, 0);
212}
213
214struct set_name_fn {
215    int (*fn) (X509 *, const char *);
216    const char *name;
217    int host;
218    int email;
219};
220
221static const struct set_name_fn name_fns[] = {
222    {set_cn1, "set CN", 1, 0},
223    {set_cn2, "set CN", 1, 0},
224    {set_cn3, "set CN", 1, 0},
225    {set_cn_and_email, "set CN", 1, 0},
226    {set_email1, "set emailAddress", 0, 1},
227    {set_email2, "set emailAddress", 0, 1},
228    {set_email3, "set emailAddress", 0, 1},
229    {set_email_and_cn, "set emailAddress", 0, 1},
230    {set_altname_dns, "set dnsName", 1, 0},
231    {set_altname_email, "set rfc822Name", 0, 1},
232    {NULL, NULL, 0}
233};
234
235static X509 *make_cert()
236{
237    X509 *ret = NULL;
238    X509 *crt = NULL;
239    X509_NAME *issuer = NULL;
240    crt = X509_new();
241    if (crt == NULL)
242        goto out;
243    if (!X509_set_version(crt, 3))
244        goto out;
245    ret = crt;
246    crt = NULL;
247 out:
248    X509_NAME_free(issuer);
249    return ret;
250}
251
252static int errors;
253
254static void check_message(const struct set_name_fn *fn, const char *op,
255                          const char *nameincert, int match, const char *name)
256{
257    char msg[1024];
258    if (match < 0)
259        return;
260    BIO_snprintf(msg, sizeof(msg), "%s: %s: [%s] %s [%s]",
261                 fn->name, op, nameincert,
262                 match ? "matches" : "does not match", name);
263    if (is_exception(msg))
264        return;
265    puts(msg);
266    ++errors;
267}
268
269static void run_cert(X509 *crt, const char *nameincert,
270                     const struct set_name_fn *fn)
271{
272    const char *const *pname = names;
273    while (*pname) {
274        int samename = strcasecmp(nameincert, *pname) == 0;
275        size_t namelen = strlen(*pname);
276        char *name = malloc(namelen);
277        int match, ret;
278        memcpy(name, *pname, namelen);
279
280        ret = X509_check_host(crt, name, namelen, 0, NULL);
281        match = -1;
282        if (ret < 0) {
283            fprintf(stderr, "internal error in X509_check_host");
284            ++errors;
285        } else if (fn->host) {
286            if (ret == 1 && !samename)
287                match = 1;
288            if (ret == 0 && samename)
289                match = 0;
290        } else if (ret == 1)
291            match = 1;
292        check_message(fn, "host", nameincert, match, *pname);
293
294        ret = X509_check_host(crt, name, namelen,
295                              X509_CHECK_FLAG_NO_WILDCARDS, NULL);
296        match = -1;
297        if (ret < 0) {
298            fprintf(stderr, "internal error in X509_check_host");
299            ++errors;
300        } else if (fn->host) {
301            if (ret == 1 && !samename)
302                match = 1;
303            if (ret == 0 && samename)
304                match = 0;
305        } else if (ret == 1)
306            match = 1;
307        check_message(fn, "host-no-wildcards", nameincert, match, *pname);
308
309        ret = X509_check_email(crt, name, namelen, 0);
310        match = -1;
311        if (fn->email) {
312            if (ret && !samename)
313                match = 1;
314            if (!ret && samename && strchr(nameincert, '@') != NULL)
315                match = 0;
316        } else if (ret)
317            match = 1;
318        check_message(fn, "email", nameincert, match, *pname);
319        ++pname;
320        free(name);
321    }
322}
323
324int main(void)
325{
326    const struct set_name_fn *pfn = name_fns;
327    while (pfn->name) {
328        const char *const *pname = names;
329        while (*pname) {
330            X509 *crt = make_cert();
331            if (crt == NULL) {
332                fprintf(stderr, "make_cert failed\n");
333                return 1;
334            }
335            if (!pfn->fn(crt, *pname)) {
336                fprintf(stderr, "X509 name setting failed\n");
337                return 1;
338            }
339            run_cert(crt, *pname, pfn);
340            X509_free(crt);
341            ++pname;
342        }
343        ++pfn;
344    }
345    return errors > 0 ? 1 : 0;
346}
347