1 /* $OpenBSD: verifytest.c,v 1.8 2023/05/28 09:02:01 beck Exp $ */
2 /*
3 * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <err.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21
22 #include <openssl/x509v3.h>
23 #include <tls.h>
24
25 extern int tls_check_name(struct tls *ctx, X509 *cert, const char *name,
26 int *match);
27
28 struct alt_name {
29 const char name[128];
30 int name_len;
31 int name_type;
32 };
33
34 struct verify_test {
35 const char common_name[128];
36 int common_name_len;
37 struct alt_name alt_name1;
38 struct alt_name alt_name2;
39 struct alt_name alt_name3;
40 const char name[128];
41 int want_return;
42 int want_match;
43 int name_type;
44 };
45
46 struct verify_test verify_tests[] = {
47 {
48 /* CN without SANs - matching. */
49 .common_name = "www.openbsd.org",
50 .common_name_len = -1,
51 .name = "www.openbsd.org",
52 .want_return = 0,
53 .want_match = 1,
54 },
55 {
56 /* Zero length name - non-matching. */
57 .common_name = "www.openbsd.org",
58 .common_name_len = -1,
59 .name = "",
60 .want_return = 0,
61 .want_match = 0,
62 },
63 {
64 /* CN wildcard without SANs - matching. */
65 .common_name = "*.openbsd.org",
66 .common_name_len = -1,
67 .name = "www.openbsd.org",
68 .want_return = 0,
69 .want_match = 1,
70 },
71 {
72 /* CN without SANs - non-matching. */
73 .common_name = "www.openbsdfoundation.org",
74 .common_name_len = -1,
75 .name = "www.openbsd.org",
76 .want_return = 0,
77 .want_match = 0,
78 },
79 {
80 /* CN wildcard without SANs - invalid CN wildcard. */
81 .common_name = "w*.openbsd.org",
82 .common_name_len = -1,
83 .name = "www.openbsd.org",
84 .want_return = 0,
85 .want_match = 0,
86 },
87 {
88 /* CN wildcard without SANs - invalid CN wildcard. */
89 .common_name = "www.*.org",
90 .common_name_len = -1,
91 .name = "www.openbsd.org",
92 .want_return = 0,
93 .want_match = 0,
94 },
95 {
96 /* CN wildcard without SANs - invalid CN wildcard. */
97 .common_name = "www.openbsd.*",
98 .common_name_len = -1,
99 .name = "www.openbsd.org",
100 .want_return = 0,
101 .want_match = 0,
102 },
103 {
104 /* CN wildcard without SANs - invalid CN wildcard. */
105 .common_name = "*",
106 .common_name_len = -1,
107 .name = "www.openbsd.org",
108 .want_return = 0,
109 .want_match = 0,
110 },
111 {
112 /* CN wildcard without SANs - invalid CN wildcard. */
113 .common_name = "*.org",
114 .common_name_len = -1,
115 .name = "www.openbsd.org",
116 .want_return = 0,
117 .want_match = 0,
118 },
119 {
120 /* CN wildcard without SANs - invalid CN wildcard. */
121 .common_name = "*.org",
122 .common_name_len = -1,
123 .name = "openbsd.org",
124 .want_return = 0,
125 .want_match = 0,
126 },
127 {
128 /* CN IPv4 without SANs - matching. */
129 .common_name = "1.2.3.4",
130 .common_name_len = -1,
131 .name = "1.2.3.4",
132 .want_return = 0,
133 .want_match = 1,
134 },
135 {
136 /* CN IPv4 wildcard without SANS - invalid IP wildcard. */
137 .common_name = "*.2.3.4",
138 .common_name_len = -1,
139 .name = "1.2.3.4",
140 .want_return = 0,
141 .want_match = 0,
142 },
143 {
144 /* CN IPv6 without SANs - matching. */
145 .common_name = "cafe::beef",
146 .common_name_len = -1,
147 .name = "cafe::beef",
148 .want_return = 0,
149 .want_match = 1,
150 },
151 {
152 /* CN without SANs - error due to embedded NUL in CN. */
153 .common_name = {
154 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
155 0x62, 0x73, 0x64, 0x2e, 0x6f, 0x72, 0x67, 0x00,
156 0x6e, 0x61, 0x73, 0x74, 0x79, 0x2e, 0x6f, 0x72,
157 0x67,
158 },
159 .common_name_len = 25,
160 .name = "www.openbsd.org",
161 .want_return = -1,
162 .want_match = 0,
163 },
164 {
165 /* CN wildcard without SANs - invalid non-matching name. */
166 .common_name = "*.openbsd.org",
167 .common_name_len = -1,
168 .name = ".openbsd.org",
169 .want_return = 0,
170 .want_match = 0,
171 },
172 {
173 /* CN with SANs - matching on first SAN. */
174 .common_name = "www.openbsd.org",
175 .common_name_len = -1,
176 .alt_name1 = {
177 .name = "www.openbsd.org",
178 .name_len = -1,
179 .name_type = GEN_DNS,
180 },
181 .alt_name2 = {
182 .name = "ftp.openbsd.org",
183 .name_len = -1,
184 .name_type = GEN_DNS,
185 },
186 .name = "www.openbsd.org",
187 .want_return = 0,
188 .want_match = 1,
189 },
190 {
191 /* SANs only - matching on first SAN. */
192 .common_name_len = 0,
193 .alt_name1 = {
194 .name = "www.openbsd.org",
195 .name_len = -1,
196 .name_type = GEN_DNS,
197 },
198 .alt_name2 = {
199 .name = "ftp.openbsd.org",
200 .name_len = -1,
201 .name_type = GEN_DNS,
202 },
203 .name = "www.openbsd.org",
204 .want_return = 0,
205 .want_match = 1,
206 },
207 {
208 /* SANs only - matching on second SAN. */
209 .common_name_len = 0,
210 .alt_name1 = {
211 .name = "www.openbsd.org",
212 .name_len = -1,
213 .name_type = GEN_DNS,
214 },
215 .alt_name2 = {
216 .name = "ftp.openbsd.org",
217 .name_len = -1,
218 .name_type = GEN_DNS,
219 },
220 .name = "ftp.openbsd.org",
221 .want_return = 0,
222 .want_match = 1,
223 },
224 {
225 /* SANs only - non-matching. */
226 .common_name_len = 0,
227 .alt_name1 = {
228 .name = "www.openbsd.org",
229 .name_len = -1,
230 .name_type = GEN_DNS,
231 },
232 .alt_name2 = {
233 .name = "ftp.openbsd.org",
234 .name_len = -1,
235 .name_type = GEN_DNS,
236 },
237 .name = "mail.openbsd.org",
238 .want_return = 0,
239 .want_match = 0,
240 },
241 {
242 /* CN with SANs - matching on second SAN. */
243 .common_name = "www.openbsd.org",
244 .common_name_len = -1,
245 .alt_name1 = {
246 .name = "www.openbsd.org",
247 .name_len = -1,
248 .name_type = GEN_DNS,
249 },
250 .alt_name2 = {
251 .name = "ftp.openbsd.org",
252 .name_len = -1,
253 .name_type = GEN_DNS,
254 },
255 .name = "ftp.openbsd.org",
256 .want_return = 0,
257 .want_match = 1,
258 },
259 {
260 /* CN with SANs - matching on wildcard second SAN. */
261 .common_name = "www.openbsdfoundation.org",
262 .common_name_len = -1,
263 .alt_name1 = {
264 .name = "www.openbsdfoundation.org",
265 .name_len = -1,
266 .name_type = GEN_DNS,
267 },
268 .alt_name2 = {
269 .name = "*.openbsd.org",
270 .name_len = -1,
271 .name_type = GEN_DNS,
272 },
273 .name = "www.openbsd.org",
274 .want_return = 0,
275 .want_match = 1,
276 },
277 {
278 /* CN with SANs - non-matching invalid wildcard. */
279 .common_name = "www.openbsdfoundation.org",
280 .common_name_len = -1,
281 .alt_name1 = {
282 .name = "www.openbsdfoundation.org",
283 .name_len = -1,
284 .name_type = GEN_DNS,
285 },
286 .alt_name2 = {
287 .name = "*.org",
288 .name_len = -1,
289 .name_type = GEN_DNS,
290 },
291 .name = "www.openbsd.org",
292 .want_return = 0,
293 .want_match = 0,
294 },
295 {
296 /* CN with SANs - non-matching IPv4 due to GEN_DNS SAN. */
297 .common_name = "www.openbsd.org",
298 .common_name_len = -1,
299 .alt_name1 = {
300 .name = "www.openbsd.org",
301 .name_len = -1,
302 .name_type = GEN_DNS,
303 },
304 .alt_name2 = {
305 .name = "1.2.3.4",
306 .name_len = -1,
307 .name_type = GEN_DNS,
308 },
309 .name = "1.2.3.4",
310 .want_return = 0,
311 .want_match = 0,
312 },
313 {
314 /* CN with SANs - matching IPv4 on GEN_IPADD SAN. */
315 .common_name = "www.openbsd.org",
316 .common_name_len = -1,
317 .alt_name1 = {
318 .name = "www.openbsd.org",
319 .name_len = -1,
320 .name_type = GEN_DNS,
321 },
322 .alt_name2 = {
323 .name = {0x01, 0x02, 0x03, 0x04},
324 .name_len = 4,
325 .name_type = GEN_IPADD,
326 },
327 .name = "1.2.3.4",
328 .want_return = 0,
329 .want_match = 1,
330 },
331 {
332 /* CN with SANs - matching IPv6 on GEN_IPADD SAN. */
333 .common_name = "www.openbsd.org",
334 .common_name_len = -1,
335 .alt_name1 = {
336 .name = "www.openbsd.org",
337 .name_len = -1,
338 .name_type = GEN_DNS,
339 },
340 .alt_name2 = {
341 .name = {
342 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xef,
344 },
345 .name_len = 16,
346 .name_type = GEN_IPADD,
347 },
348 .name = "cafe::beef",
349 .want_return = 0,
350 .want_match = 1,
351 },
352 {
353 /* CN with SANs - error due to embedded NUL in GEN_DNS. */
354 .common_name = "www.openbsd.org.nasty.org",
355 .common_name_len = -1,
356 .alt_name1 = {
357 .name = "www.openbsd.org.nasty.org",
358 .name_len = -1,
359 .name_type = GEN_DNS,
360 },
361 .alt_name2 = {
362 .name = {
363 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
364 0x62, 0x73, 0x64, 0x2e, 0x6f, 0x72, 0x67, 0x00,
365 0x6e, 0x61, 0x73, 0x74, 0x79, 0x2e, 0x6f, 0x72,
366 0x67,
367 },
368 .name_len = 25,
369 .name_type = GEN_DNS,
370 },
371 .name = "www.openbsd.org",
372 .want_return = -1,
373 .want_match = 0,
374 },
375 {
376 /* CN with SAN - non-matching due to non-matching SAN. */
377 .common_name = "www.openbsd.org",
378 .common_name_len = -1,
379 .alt_name1 = {
380 .name = "ftp.openbsd.org",
381 .name_len = -1,
382 .name_type = GEN_DNS,
383 },
384 .name = "www.openbsd.org",
385 .want_return = 0,
386 .want_match = 0,
387 },
388 {
389 /* CN with SAN - error due to illegal dNSName. */
390 .common_name = "www.openbsd.org",
391 .common_name_len = -1,
392 .alt_name1 = {
393 .name = " ",
394 .name_len = -1,
395 .name_type = GEN_DNS,
396 },
397 .name = "www.openbsd.org",
398 .want_return = -1,
399 .want_match = 0,
400 },
401 };
402
403 #define N_VERIFY_TESTS \
404 (sizeof(verify_tests) / sizeof(*verify_tests))
405
406 static void
alt_names_add(STACK_OF (GENERAL_NAME)* alt_name_stack,struct alt_name * alt)407 alt_names_add(STACK_OF(GENERAL_NAME) *alt_name_stack, struct alt_name *alt)
408 {
409 ASN1_STRING *alt_name_str;
410 GENERAL_NAME *alt_name;
411
412 if ((alt_name = GENERAL_NAME_new()) == NULL)
413 errx(1, "failed to malloc GENERAL_NAME");
414 alt_name->type = alt->name_type;
415
416 if ((alt_name_str = ASN1_STRING_new()) == NULL)
417 errx(1, "failed to malloc alt name");
418 if (ASN1_STRING_set(alt_name_str, alt->name, alt->name_len) == 0)
419 errx(1, "failed to set alt name");
420
421 switch (alt_name->type) {
422 case GEN_DNS:
423 alt_name->d.dNSName = alt_name_str;
424 break;
425 case GEN_IPADD:
426 alt_name->d.iPAddress = alt_name_str;
427 break;
428 default:
429 errx(1, "unknown alt name type (%i)", alt_name->type);
430 }
431
432 if (sk_GENERAL_NAME_push(alt_name_stack, alt_name) == 0)
433 errx(1, "failed to push alt_name");
434 }
435
436 static void
cert_add_alt_names(X509 * cert,struct verify_test * vt)437 cert_add_alt_names(X509 *cert, struct verify_test *vt)
438 {
439 STACK_OF(GENERAL_NAME) *alt_name_stack = NULL;
440
441 if (vt->alt_name1.name_type == 0)
442 return;
443
444 if ((alt_name_stack = sk_GENERAL_NAME_new_null()) == NULL)
445 errx(1, "failed to malloc sk_GENERAL_NAME");
446
447 if (vt->alt_name1.name_type != 0)
448 alt_names_add(alt_name_stack, &vt->alt_name1);
449 if (vt->alt_name2.name_type != 0)
450 alt_names_add(alt_name_stack, &vt->alt_name2);
451 if (vt->alt_name3.name_type != 0)
452 alt_names_add(alt_name_stack, &vt->alt_name3);
453
454 if (X509_add1_ext_i2d(cert, NID_subject_alt_name,
455 alt_name_stack, 0, 0) == 0)
456 errx(1, "failed to set subject alt name");
457
458 sk_GENERAL_NAME_pop_free(alt_name_stack, GENERAL_NAME_free);
459 }
460
461 static int
do_verify_test(int test_no,struct verify_test * vt)462 do_verify_test(int test_no, struct verify_test *vt)
463 {
464 struct tls *tls;
465 X509_NAME *name;
466 X509 *cert;
467 int failed = 1;
468 int match;
469
470 /* Build certificate structure. */
471 if ((cert = X509_new()) == NULL)
472 errx(1, "failed to malloc X509");
473
474 if (vt->common_name_len != 0) {
475 if ((name = X509_NAME_new()) == NULL)
476 errx(1, "failed to malloc X509_NAME");
477 if (X509_NAME_add_entry_by_NID(name, NID_commonName,
478 vt->name_type ? vt->name_type : MBSTRING_ASC,
479 (unsigned char *)vt->common_name,
480 vt->common_name_len, -1, 0) == 0)
481 errx(1, "failed to add name entry");
482 if (X509_set_subject_name(cert, name) == 0)
483 errx(1, "failed to set subject name");
484 X509_NAME_free(name);
485 }
486
487 if ((tls = tls_client()) == NULL)
488 errx(1, "failed to malloc tls_client");
489
490 cert_add_alt_names(cert, vt);
491
492 match = 1;
493
494 if (tls_check_name(tls, cert, vt->name, &match) != vt->want_return) {
495 fprintf(stderr, "FAIL: test %i failed for check name '%s': "
496 "%s\n", test_no, vt->name, tls_error(tls));
497 goto done;
498 }
499 if (match != vt->want_match) {
500 fprintf(stderr, "FAIL: test %i failed to match name '%s'\n",
501 test_no, vt->name);
502 goto done;
503 }
504
505 failed = 0;
506
507 done:
508 X509_free(cert);
509 tls_free(tls);
510
511 return (failed);
512 }
513
514 int
main(int argc,char ** argv)515 main(int argc, char **argv)
516 {
517 int failed = 0;
518 size_t i;
519
520 tls_init();
521
522 for (i = 0; i < N_VERIFY_TESTS; i++)
523 failed += do_verify_test(i, &verify_tests[i]);
524
525 return (failed);
526 }
527