verifytest.c revision 1.6
1/*	$OpenBSD: verifytest.c,v 1.6 2017/04/10 17:12:30 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
25extern int tls_check_name(struct tls *ctx, X509 *cert, const char *name,
26    int *match);
27
28struct alt_name {
29	const char name[128];
30	int name_len;
31	int name_type;
32};
33
34struct 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
45struct 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
405static void
406alt_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
435static void
436cert_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
460static int
461do_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
508	return (failed);
509}
510
511int
512main(int argc, char **argv)
513{
514	int failed = 0;
515	size_t i;
516
517	for (i = 0; i < N_VERIFY_TESTS; i++)
518		failed += do_verify_test(i, &verify_tests[i]);
519
520	return (failed);
521}
522