NGX-2040 - fix wildcard match on punycode/IDNA DNS names
[openssl.git] / test / v3nametest.c
1 #include <openssl/x509.h>
2 #include <openssl/x509v3.h>
3 #include "../e_os.h"
4 #include <string.h>
5
6 static 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
25 static 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
63 static 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
72 static 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 /*-
102 int             X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
103 X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
104                         int nid, int crit, ASN1_OCTET_STRING *data);
105 int             X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
106 */
107
108 static 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
158 static int set_cn1(X509 *crt, const char *name)
159 {
160     return set_cn(crt, NID_commonName, name, 0);
161 }
162
163 static 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
169 static 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
175 static 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
181 static int set_email1(X509 *crt, const char *name)
182 {
183     return set_cn(crt, NID_pkcs9_emailAddress, name, 0);
184 }
185
186 static 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
192 static 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
198 static 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
204 static int set_altname_dns(X509 *crt, const char *name)
205 {
206     return set_altname(crt, GEN_DNS, name, 0);
207 }
208
209 static int set_altname_email(X509 *crt, const char *name)
210 {
211     return set_altname(crt, GEN_EMAIL, name, 0);
212 }
213
214 struct set_name_fn {
215     int (*fn) (X509 *, const char *);
216     const char *name;
217     int host;
218     int email;
219 };
220
221 static 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
235 static 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
252 static int errors;
253
254 static 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
269 static 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
324 int 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 }