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