xref: /openbsd-src/regress/lib/libcrypto/x509/constraints.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: constraints.c,v 1.15 2022/11/28 07:24:03 tb Exp $	*/
2 /*
3  * Copyright (c) 2020 Bob Beck <beck@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 <string.h>
20 
21 #include <openssl/safestack.h>
22 #include <openssl/x509.h>
23 #include <openssl/x509v3.h>
24 #include "x509_internal.h"
25 
26 #define FAIL(msg, ...)							\
27 do {									\
28 	fprintf(stderr, "[%s:%d] FAIL: ", __FILE__, __LINE__);		\
29 	fprintf(stderr, msg, ##__VA_ARGS__);				\
30 } while(0)
31 
32 unsigned char *valid_hostnames[] = {
33 	"openbsd.org",
34 	"op3nbsd.org",
35 	"org",
36 	"3openbsd.com",
37 	"3-0penb-d.c-m",
38 	"a",
39 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com",
40 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
41 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
42 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
43 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
44 	"open_bsd.org", /* because this is liberal */
45 	NULL,
46 };
47 
48 unsigned char *valid_sandns_names[] = {
49 	"*.ca",
50 	"*.op3nbsd.org",
51 	"c*.openbsd.org",
52 	"foo.*.d*.c*.openbsd.org",
53 	NULL,
54 };
55 
56 unsigned char *valid_domain_constraints[] = {
57 	"",
58 	".ca",
59 	".op3nbsd.org",
60 	".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
61 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
62 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
63 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
64 	"www.openbsd.org",
65 	NULL,
66 };
67 
68 unsigned char *valid_mbox_names[] = {
69 	"\"!#$%&\\\"*+-/=?\002^_`{|}~.\"@openbsd.org",
70 	"beck@openbsd.org",
71 	"beck@openbsd.org",
72 	"beck@op3nbsd.org",
73 	"beck@org",
74 	"beck@3openbsd.com",
75 	"beck@3-0penb-d.c-m",
76 	"bec@a",
77 	"beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com",
78 	"beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
79 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
80 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
81 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
82 	"beck@open_bsd.org", /* because this is liberal */
83 	NULL,
84 };
85 
86 unsigned char *invalid_hostnames[] = {
87 	"openbsd.org.",
88 	"openbsd..org",
89 	"openbsd.org-",
90 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com",
91 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
92 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
93 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
94 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a",
95 	"-p3nbsd.org",
96 	"openbs-.org",
97 	"openbsd\n.org",
98 	"open\178bsd.org",
99 	"open\255bsd.org",
100 	"*.openbsd.org",
101 	NULL,
102 };
103 
104 unsigned char *invalid_sandns_names[] = {
105 	"",
106 	".",
107 	"*.a",
108 	"*.",
109 	"*.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com",
110 	".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
111 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
112 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
113 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a",
114 	"*.-p3nbsd.org",
115 	"*.*..openbsd.org",
116 	"*..openbsd.org",
117 	".openbsd.org",
118 	"c*c.openbsd.org",
119 	NULL,
120 };
121 
122 unsigned char *invalid_mbox_names[] = {
123 	"beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com",
124 	"beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
125 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
126 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
127 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a",
128 	"beck@.-openbsd.org",
129 	"beck@.openbsd.org.",
130 	"beck@.a",
131 	"beck@.",
132 	"beck@",
133 	"beck@.ca",
134 	"@openbsd.org",
135 	NULL,
136 };
137 
138 unsigned char *invalid_domain_constraints[] = {
139 	".",
140 	".a",
141 	"..",
142 	".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com",
143 	".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
144 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
145 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
146 	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a",
147 	".-p3nbsd.org",
148 	"..openbsd.org",
149 	NULL,
150 };
151 
152 unsigned char *invaliduri[] = {
153 	"https://-www.openbsd.org",
154 	"https://.www.openbsd.org/",
155 	"https://www.ope|nbsd.org%",
156 	"https://www.openbsd.org.#",
157 	"///",
158 	"//",
159 	"/",
160 	"",
161 	NULL,
162 };
163 
164 static int
165 test_valid_hostnames(void)
166 {
167 	int i, failure = 0;
168 
169 	for (i = 0; valid_hostnames[i] != NULL; i++) {
170 		CBS cbs;
171 		CBS_init(&cbs, valid_hostnames[i], strlen(valid_hostnames[i]));
172 		if (!x509_constraints_valid_host(&cbs)) {
173 			FAIL("Valid hostname '%s' rejected\n",
174 			    valid_hostnames[i]);
175 			failure = 1;
176 			goto done;
177 		}
178 		CBS_init(&cbs, valid_hostnames[i], strlen(valid_hostnames[i]));
179 		if (!x509_constraints_valid_sandns(&cbs)) {
180 			FAIL("Valid sandns '%s' rejected\n",
181 			    valid_hostnames[i]);
182 			failure = 1;
183 			goto done;
184 		}
185 	}
186  done:
187 	return failure;
188 }
189 
190 static int
191 test_valid_sandns_names(void)
192 {
193 	int i, failure = 0;
194 	for (i = 0; valid_sandns_names[i] != NULL; i++) {
195 		CBS cbs;
196 		CBS_init(&cbs, valid_sandns_names[i],
197 		    strlen(valid_sandns_names[i]));
198 		if (!x509_constraints_valid_sandns(&cbs)) {
199 			FAIL("Valid dnsname '%s' rejected\n",
200 			    valid_sandns_names[i]);
201 			failure = 1;
202 			goto done;
203 		}
204 	}
205  done:
206 	return failure;
207 }
208 
209 static int
210 test_valid_domain_constraints(void)
211 {
212 	int i, failure = 0;
213 	for (i = 0; valid_domain_constraints[i] != NULL; i++) {
214 		CBS cbs;
215 		CBS_init(&cbs, valid_domain_constraints[i],
216 		    strlen(valid_domain_constraints[i]));
217 		if (!x509_constraints_valid_domain_constraint(&cbs)) {
218 			FAIL("Valid dnsname '%s' rejected\n",
219 			    valid_domain_constraints[i]);
220 			failure = 1;
221 			goto done;
222 		}
223 	}
224  done:
225 	return failure;
226 }
227 
228 static int
229 test_valid_mbox_names(void)
230 {
231 	struct x509_constraints_name name = {0};
232 	int i, failure = 0;
233 	for (i = 0; valid_mbox_names[i] != NULL; i++) {
234 		CBS cbs;
235 		CBS_init(&cbs, valid_mbox_names[i],
236 		    strlen(valid_mbox_names[i]));
237 		if (!x509_constraints_parse_mailbox(&cbs, &name)) {
238 			FAIL("Valid mailbox name '%s' rejected\n",
239 			    valid_mbox_names[i]);
240 			failure = 1;
241 			goto done;
242 		}
243 		free(name.name);
244 		name.name = NULL;
245 		free(name.local);
246 		name.local = NULL;
247 	}
248  done:
249 	return failure;
250 }
251 
252 static int
253 test_invalid_hostnames(void)
254 {
255 	int i, failure = 0;
256 	char *nulhost = "www.openbsd.org\0";
257 	CBS cbs;
258 
259 	for (i = 0; invalid_hostnames[i] != NULL; i++) {
260 		CBS_init(&cbs, invalid_hostnames[i],
261 		    strlen(invalid_hostnames[i]));
262 		if (x509_constraints_valid_host(&cbs)) {
263 			FAIL("Invalid hostname '%s' accepted\n",
264 			    invalid_hostnames[i]);
265 			failure = 1;
266 			goto done;
267 		}
268 	}
269 	CBS_init(&cbs, nulhost, strlen(nulhost) + 1);
270 	if (x509_constraints_valid_host(&cbs)) {
271 		FAIL("hostname with NUL byte accepted\n");
272 		failure = 1;
273 		goto done;
274 	}
275 	CBS_init(&cbs, nulhost, strlen(nulhost) + 1);
276 	if (x509_constraints_valid_sandns(&cbs)) {
277 		FAIL("sandns with NUL byte accepted\n");
278 		failure = 1;
279 		goto done;
280 	}
281  done:
282 	return failure;
283 }
284 
285 static int
286 test_invalid_sandns_names(void)
287 {
288 	int i, failure = 0;
289 	for (i = 0; invalid_sandns_names[i] != NULL; i++) {
290 		CBS cbs;
291 		CBS_init(&cbs, invalid_sandns_names[i],
292 		    strlen(invalid_sandns_names[i]));
293 		if (x509_constraints_valid_sandns(&cbs)) {
294 			FAIL("Valid dnsname '%s' rejected\n",
295 			    invalid_sandns_names[i]);
296 			failure = 1;
297 			goto done;
298 		}
299 	}
300  done:
301 	return failure;
302 }
303 
304 static int
305 test_invalid_mbox_names(void)
306 {
307 	int i, failure = 0;
308 	struct x509_constraints_name name = {0};
309 	for (i = 0; invalid_mbox_names[i] != NULL; i++) {
310 		CBS cbs;
311 		CBS_init(&cbs, invalid_mbox_names[i],
312 		    strlen(invalid_mbox_names[i]));
313 		if (x509_constraints_parse_mailbox(&cbs, &name)) {
314 			FAIL("invalid mailbox name '%s' accepted\n",
315 			    invalid_mbox_names[i]);
316 			failure = 1;
317 			goto done;
318 		}
319 		free(name.name);
320 		name.name = NULL;
321 		free(name.local);
322 		name.local = NULL;
323 	}
324  done:
325 	return failure;
326 }
327 
328 static int
329 test_invalid_domain_constraints(void)
330 {
331 	int i, failure = 0;
332 	for (i = 0; invalid_domain_constraints[i] != NULL; i++) {
333 		CBS cbs;
334 		CBS_init(&cbs, invalid_domain_constraints[i],
335 		    strlen(invalid_domain_constraints[i]));
336 		if (x509_constraints_valid_domain_constraint(&cbs)) {
337 			FAIL("invalid dnsname '%s' accepted\n",
338 			    invalid_domain_constraints[i]);
339 			failure = 1;
340 			goto done;
341 		}
342 	}
343  done:
344 	return failure;
345 }
346 
347 static int
348 test_invalid_uri(void)
349 {
350 	int j, failure = 0;
351 	char *hostpart = NULL;
352 
353 	for (j = 0; invaliduri[j] != NULL; j++) {
354 		if (x509_constraints_uri_host(invaliduri[j],
355 		    strlen(invaliduri[j]), &hostpart) != 0) {
356 			FAIL("invalid URI '%s' accepted\n",
357 			    invaliduri[j]);
358 			failure = 1;
359 			goto done;
360 		}
361 		free(hostpart);
362 		hostpart = NULL;
363 	}
364 
365  done:
366 	return failure;
367 }
368 
369 static int
370 test_constraints1(void)
371 {
372 	char *c;
373 	size_t cl;
374 	char *d;
375 	size_t dl;
376 	int failure = 0;
377 	int error = 0;
378 	int i, j;
379 	unsigned char *constraints[] = {
380 		".org",
381 		".openbsd.org",
382 		"www.openbsd.org",
383 		NULL,
384 	};
385 	unsigned char *failing[] = {
386 		".ca",
387 		"openbsd.ca",
388 		"org",
389 		NULL,
390 	};
391 	unsigned char *matching[] = {
392 		"www.openbsd.org",
393 		NULL,
394 	};
395 	unsigned char *matchinguri[] = {
396 		"https://www.openbsd.org",
397 		"https://www.openbsd.org/",
398 		"https://www.openbsd.org?",
399 		"https://www.openbsd.org#",
400 		"herp://beck@www.openbsd.org:",
401 		"spiffe://beck@www.openbsd.org/this/is/so/spiffe/",
402 		NULL,
403 	};
404 	unsigned char *failinguri[] = {
405 		"https://www.openbsd.ca",
406 		"https://www.freebsd.com/",
407 		"https://www.openbsd.net?",
408 		"https://org#",
409 		"herp://beck@org:",
410 		"///",
411 		"//",
412 		"/",
413 		"",
414 		NULL,
415 	};
416 	unsigned char *noauthority[] = {
417 		"urn:open62541.server.application",
418 		NULL,
419 	};
420 	for (i = 0; constraints[i] != NULL; i++) {
421 		char *constraint = constraints[i];
422 		size_t clen = strlen(constraints[i]);
423 		for (j = 0; matching[j] != NULL; j++) {
424 			if (!x509_constraints_domain(matching[j],
425 			    strlen(matching[j]), constraint, clen)) {
426 				FAIL("constraint '%s' should have matched"
427 				    " '%s'\n",
428 				    constraint, matching[j]);
429 				failure = 1;
430 				goto done;
431 			}
432 		}
433 		for (j = 0; matchinguri[j] != NULL; j++) {
434 			error = 0;
435 			if (!x509_constraints_uri(matchinguri[j],
436 			    strlen(matchinguri[j]), constraint, clen, &error)) {
437 				FAIL("constraint '%s' should have matched URI"
438 				    " '%s' (error %d)\n",
439 				    constraint, matchinguri[j], error);
440 				failure = 1;
441 				goto done;
442 			}
443 		}
444 		for (j = 0; failing[j] != NULL; j++) {
445 			if (x509_constraints_domain(failing[j],
446 			    strlen(failing[j]), constraint, clen)) {
447 				FAIL("constraint '%s' should not have matched"
448 				    " '%s'\n",
449 				    constraint, failing[j]);
450 				failure = 1;
451 				goto done;
452 			}
453 		}
454 		for (j = 0; failinguri[j] != NULL; j++) {
455 			error = 0;
456 			if (x509_constraints_uri(failinguri[j],
457 			    strlen(failinguri[j]), constraint, clen, &error)) {
458 				FAIL("constraint '%s' should not have matched URI"
459 				    " '%s' (error %d)\n",
460 				    constraint, failinguri[j], error);
461 				failure = 1;
462 				goto done;
463 			}
464 		}
465 		for (j = 0; noauthority[j] != NULL; j++) {
466 			char *hostpart = NULL;
467 			error = 0;
468 			if (!x509_constraints_uri_host(noauthority[j],
469 			    strlen(noauthority[j]), NULL) ||
470 			    !x509_constraints_uri_host(noauthority[j],
471 			    strlen(noauthority[j]), &hostpart)) {
472 				FAIL("name '%s' should parse as a URI",
473 				    noauthority[j]);
474 				failure = 1;
475 				free(hostpart);
476 				goto done;
477 			}
478 			free(hostpart);
479 
480 			if (x509_constraints_uri(noauthority[j],
481 			    strlen(noauthority[j]), constraint, clen, &error)) {
482 				FAIL("constraint '%s' should not have matched URI"
483 				    " '%s' (error %d)\n",
484 				    constraint, failinguri[j], error);
485 				failure = 1;
486 				goto done;
487 			}
488 		}
489 	}
490 	c = ".openbsd.org";
491 	cl = strlen(".openbsd.org");
492 	d = "*.openbsd.org";
493 	dl = strlen("*.openbsd.org");
494 	if (!x509_constraints_domain(d, dl, c, cl)) {
495 		FAIL("constraint '%s' should have matched '%s'\n",
496 		    c, d);
497 		failure = 1;
498 		goto done;
499 	}
500 	c = "www.openbsd.org";
501 	cl = strlen("www.openbsd.org");
502 	if (x509_constraints_domain(d, dl, c, cl)) {
503 		FAIL("constraint '%s' should not have matched '%s'\n",
504 		    c, d);
505 		failure = 1;
506 		goto done;
507 	}
508 	c = "";
509 	cl = 0;
510 	if (!x509_constraints_domain(d, dl, c, cl)) {
511 		FAIL("constraint '%s' should have matched '%s'\n",
512 		    c, d);
513 		failure = 1;
514 		goto done;
515 	}
516  done:
517 	return failure;
518 }
519 
520 int
521 main(int argc, char **argv)
522 {
523 	int failed = 0;
524 
525 	failed |= test_valid_hostnames();
526 	failed |= test_invalid_hostnames();
527 	failed |= test_valid_sandns_names();
528 	failed |= test_invalid_sandns_names();
529 	failed |= test_valid_mbox_names();
530 	failed |= test_invalid_mbox_names();
531 	failed |= test_valid_domain_constraints();
532 	failed |= test_invalid_domain_constraints();
533 	failed |= test_invalid_uri();
534 	failed |= test_constraints1();
535 
536 	return (failed);
537 }
538