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