xref: /openbsd-src/usr.sbin/rpki-client/validate.c (revision 381ee5995072b060a824e758e1421220f03cdcc0)
1*381ee599Stb /*	$OpenBSD: validate.c,v 1.78 2024/11/12 09:23:07 tb Exp $ */
29a7e9e7fSjob /*
39a7e9e7fSjob  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
49a7e9e7fSjob  *
59a7e9e7fSjob  * Permission to use, copy, modify, and distribute this software for any
69a7e9e7fSjob  * purpose with or without fee is hereby granted, provided that the above
79a7e9e7fSjob  * copyright notice and this permission notice appear in all copies.
89a7e9e7fSjob  *
99a7e9e7fSjob  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109a7e9e7fSjob  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119a7e9e7fSjob  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129a7e9e7fSjob  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139a7e9e7fSjob  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149a7e9e7fSjob  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159a7e9e7fSjob  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169a7e9e7fSjob  */
179a7e9e7fSjob 
189a7e9e7fSjob #include <arpa/inet.h>
199a7e9e7fSjob #include <assert.h>
208c2eb288Sclaudio #include <ctype.h>
219a7e9e7fSjob #include <err.h>
2267d45509Sclaudio #include <fcntl.h>
239a7e9e7fSjob #include <inttypes.h>
249a7e9e7fSjob #include <stdlib.h>
259a7e9e7fSjob #include <string.h>
269a7e9e7fSjob #include <unistd.h>
279a7e9e7fSjob 
289a7e9e7fSjob #include "extern.h"
299a7e9e7fSjob 
30c4a9443cSclaudio extern ASN1_OBJECT	*certpol_oid;
31c4a9443cSclaudio 
329a7e9e7fSjob /*
339a7e9e7fSjob  * Walk up the chain of certificates trying to match our AS number to
349a7e9e7fSjob  * one of the allocations in that chain.
35a079bbf8Sclaudio  * Returns 1 if covered or 0 if not.
369a7e9e7fSjob  */
37a079bbf8Sclaudio static int
38a079bbf8Sclaudio valid_as(struct auth *a, uint32_t min, uint32_t max)
399a7e9e7fSjob {
409a7e9e7fSjob 	int	 c;
419a7e9e7fSjob 
42a079bbf8Sclaudio 	if (a == NULL)
43a079bbf8Sclaudio 		return 0;
449a7e9e7fSjob 
459a7e9e7fSjob 	/* Does this certificate cover our AS number? */
46*381ee599Stb 	c = as_check_covered(min, max, a->cert->ases, a->cert->num_ases);
479a7e9e7fSjob 	if (c > 0)
48a079bbf8Sclaudio 		return 1;
4986cba02dSclaudio 	else if (c < 0)
5086cba02dSclaudio 		return 0;
519a7e9e7fSjob 
5260c4be4aStb 	/* If it inherits, walk up the chain. */
53335482abStb 	return valid_as(a->issuer, min, max);
549a7e9e7fSjob }
559a7e9e7fSjob 
569a7e9e7fSjob /*
579a7e9e7fSjob  * Walk up the chain of certificates (really just the last one, but in
586822deefStb  * the case of inheritance, the ones before) making sure that our IP
599a7e9e7fSjob  * prefix is covered in the first non-inheriting specification.
60a079bbf8Sclaudio  * Returns 1 if covered or 0 if not.
619a7e9e7fSjob  */
62a079bbf8Sclaudio static int
63a079bbf8Sclaudio valid_ip(struct auth *a, enum afi afi,
64a079bbf8Sclaudio     const unsigned char *min, const unsigned char *max)
659a7e9e7fSjob {
669a7e9e7fSjob 	int	 c;
679a7e9e7fSjob 
68a079bbf8Sclaudio 	if (a == NULL)
69a079bbf8Sclaudio 		return 0;
709a7e9e7fSjob 
719a7e9e7fSjob 	/* Does this certificate cover our IP prefix? */
72*381ee599Stb 	c = ip_addr_check_covered(afi, min, max, a->cert->ips,
73*381ee599Stb 	    a->cert->num_ips);
749a7e9e7fSjob 	if (c > 0)
75a079bbf8Sclaudio 		return 1;
769a7e9e7fSjob 	else if (c < 0)
77a079bbf8Sclaudio 		return 0;
789a7e9e7fSjob 
7960c4be4aStb 	/* If it inherits, walk up the chain. */
80335482abStb 	return valid_ip(a->issuer, afi, min, max);
819a7e9e7fSjob }
829a7e9e7fSjob 
839a7e9e7fSjob /*
849a7e9e7fSjob  * Validate a non-TA certificate: make sure its IP and AS resources are
859a7e9e7fSjob  * fully covered by those in the authority key (which must exist).
86a079bbf8Sclaudio  * Returns 1 if valid, 0 otherwise.
879a7e9e7fSjob  */
88a079bbf8Sclaudio int
8924069af1Sclaudio valid_cert(const char *fn, struct auth *a, const struct cert *cert)
909a7e9e7fSjob {
919a7e9e7fSjob 	size_t		 i;
929a7e9e7fSjob 	uint32_t	 min, max;
939a7e9e7fSjob 
94*381ee599Stb 	for (i = 0; i < cert->num_ases; i++) {
95*381ee599Stb 		if (cert->ases[i].type == CERT_AS_INHERIT)
969a7e9e7fSjob 			continue;
97782a58ffSjob 
98*381ee599Stb 		if (cert->ases[i].type == CERT_AS_ID) {
99*381ee599Stb 			min = cert->ases[i].id;
100*381ee599Stb 			max = cert->ases[i].id;
101782a58ffSjob 		} else {
102*381ee599Stb 			min = cert->ases[i].range.min;
103*381ee599Stb 			max = cert->ases[i].range.max;
104782a58ffSjob 		}
105782a58ffSjob 
106a079bbf8Sclaudio 		if (valid_as(a, min, max))
1079a7e9e7fSjob 			continue;
108782a58ffSjob 
109*381ee599Stb 		as_warn(fn, "RFC 6487: uncovered resource", &cert->ases[i]);
110a079bbf8Sclaudio 		return 0;
1119a7e9e7fSjob 	}
1129a7e9e7fSjob 
113*381ee599Stb 	for (i = 0; i < cert->num_ips; i++) {
114ea3b4836Sclaudio 		if (cert->ips[i].type == CERT_IP_INHERIT)
115ea3b4836Sclaudio 			continue;
116782a58ffSjob 
117a079bbf8Sclaudio 		if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min,
118a079bbf8Sclaudio 		    cert->ips[i].max))
1199a7e9e7fSjob 			continue;
120782a58ffSjob 
1214032f119Stb 		ip_warn(fn, "RFC 6487: uncovered resource", &cert->ips[i]);
122a079bbf8Sclaudio 		return 0;
1239a7e9e7fSjob 	}
1249a7e9e7fSjob 
125a079bbf8Sclaudio 	return 1;
1269a7e9e7fSjob }
1279a7e9e7fSjob 
1289a7e9e7fSjob /*
12936f16a56Sjob  * Validate our ROA: check that the prefixes (ipAddrBlocks) are contained.
130a079bbf8Sclaudio  * Returns 1 if valid, 0 otherwise.
1319a7e9e7fSjob  */
132a079bbf8Sclaudio int
13399dbdb7fStb valid_roa(const char *fn, struct cert *cert, struct roa *roa)
1349a7e9e7fSjob {
1359a7e9e7fSjob 	size_t	 i;
1369a7e9e7fSjob 	char	 buf[64];
1379a7e9e7fSjob 
138*381ee599Stb 	for (i = 0; i < roa->num_ips; i++) {
13999dbdb7fStb 		if (ip_addr_check_covered(roa->ips[i].afi, roa->ips[i].min,
140*381ee599Stb 		    roa->ips[i].max, cert->ips, cert->num_ips) > 0)
1419a7e9e7fSjob 			continue;
14299dbdb7fStb 
14399dbdb7fStb 		ip_addr_print(&roa->ips[i].addr, roa->ips[i].afi, buf,
14499dbdb7fStb 		    sizeof(buf));
14599dbdb7fStb 		warnx("%s: RFC 6482: uncovered IP: %s", fn, buf);
146a079bbf8Sclaudio 		return 0;
1479a7e9e7fSjob 	}
1489a7e9e7fSjob 
149a079bbf8Sclaudio 	return 1;
1509a7e9e7fSjob }
15167d45509Sclaudio 
15267d45509Sclaudio /*
153d4be4cdeSjob  * Validate our SPL: check that the asID is contained in the end-entity
154d4be4cdeSjob  * certificate's resources.
155d4be4cdeSjob  * Returns 1 if valid, 0 otherwise.
156d4be4cdeSjob  */
157d4be4cdeSjob int
158d4be4cdeSjob valid_spl(const char *fn, struct cert *cert, struct spl *spl)
159d4be4cdeSjob {
160*381ee599Stb 	if (as_check_covered(spl->asid, spl->asid, cert->ases,
161*381ee599Stb 	    cert->num_ases) > 0)
162d4be4cdeSjob 		return 1;
163d4be4cdeSjob 
164d4be4cdeSjob 	warnx("%s: SPL: uncovered ASID: %u", fn, spl->asid);
165d4be4cdeSjob 
166d4be4cdeSjob 	return 0;
167d4be4cdeSjob }
168d4be4cdeSjob 
169d4be4cdeSjob /*
17067d45509Sclaudio  * Validate a file by verifying the SHA256 hash of that file.
17187c7c78dSclaudio  * The file to check is passed as a file descriptor.
17287c7c78dSclaudio  * Returns 1 if hash matched, 0 otherwise. Closes fd when done.
17367d45509Sclaudio  */
17467d45509Sclaudio int
17587c7c78dSclaudio valid_filehash(int fd, const char *hash, size_t hlen)
17667d45509Sclaudio {
17767d45509Sclaudio 	SHA256_CTX	ctx;
17867d45509Sclaudio 	char		filehash[SHA256_DIGEST_LENGTH];
17967d45509Sclaudio 	char		buffer[8192];
18067d45509Sclaudio 	ssize_t		nr;
18167d45509Sclaudio 
18267d45509Sclaudio 	if (hlen != sizeof(filehash))
18367d45509Sclaudio 		errx(1, "bad hash size");
18467d45509Sclaudio 
18587c7c78dSclaudio 	if (fd == -1)
18667d45509Sclaudio 		return 0;
18767d45509Sclaudio 
18867d45509Sclaudio 	SHA256_Init(&ctx);
18967d45509Sclaudio 	while ((nr = read(fd, buffer, sizeof(buffer))) > 0)
19067d45509Sclaudio 		SHA256_Update(&ctx, buffer, nr);
19167d45509Sclaudio 	close(fd);
19267d45509Sclaudio 	SHA256_Final(filehash, &ctx);
19387c7c78dSclaudio 
19467d45509Sclaudio 	if (memcmp(hash, filehash, sizeof(filehash)) != 0)
19567d45509Sclaudio 		return 0;
19623bc08f8Sclaudio 	return 1;
19723bc08f8Sclaudio }
19867d45509Sclaudio 
19923bc08f8Sclaudio /*
20023bc08f8Sclaudio  * Same as above but with a buffer instead of a fd.
20123bc08f8Sclaudio  */
20223bc08f8Sclaudio int
20323bc08f8Sclaudio valid_hash(unsigned char *buf, size_t len, const char *hash, size_t hlen)
20423bc08f8Sclaudio {
20523bc08f8Sclaudio 	char	filehash[SHA256_DIGEST_LENGTH];
20623bc08f8Sclaudio 
20723bc08f8Sclaudio 	if (hlen != sizeof(filehash))
20823bc08f8Sclaudio 		errx(1, "bad hash size");
20923bc08f8Sclaudio 
21023bc08f8Sclaudio 	if (buf == NULL || len == 0)
21123bc08f8Sclaudio 		return 0;
21223bc08f8Sclaudio 
21323bc08f8Sclaudio 	if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL))
21423bc08f8Sclaudio 		errx(1, "EVP_Digest failed");
21523bc08f8Sclaudio 
21623bc08f8Sclaudio 	if (memcmp(hash, filehash, sizeof(filehash)) != 0)
21723bc08f8Sclaudio 		return 0;
21867d45509Sclaudio 	return 1;
21967d45509Sclaudio }
2208c2eb288Sclaudio 
2218c2eb288Sclaudio /*
222203dfefcStb  * Validate that a filename only contains characters from the POSIX portable
223203dfefcStb  * filename character set [A-Za-z0-9._-], see IEEE Std 1003.1-2013, 3.278.
224203dfefcStb  */
225203dfefcStb int
226203dfefcStb valid_filename(const char *fn, size_t len)
227203dfefcStb {
228203dfefcStb 	const unsigned char *c;
229203dfefcStb 	size_t i;
230203dfefcStb 
231203dfefcStb 	for (c = fn, i = 0; i < len; i++, c++)
232203dfefcStb 		if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.')
233203dfefcStb 			return 0;
234203dfefcStb 	return 1;
235203dfefcStb }
236203dfefcStb 
237203dfefcStb /*
2388c2eb288Sclaudio  * Validate a URI to make sure it is pure ASCII and does not point backwards
2398c2eb288Sclaudio  * or doing some other silly tricks. To enforce the protocol pass either
2408c2eb288Sclaudio  * https:// or rsync:// as proto, if NULL is passed no protocol is enforced.
2418c2eb288Sclaudio  * Returns 1 if valid, 0 otherwise.
2428c2eb288Sclaudio  */
2438c2eb288Sclaudio int
2448c2eb288Sclaudio valid_uri(const char *uri, size_t usz, const char *proto)
2458c2eb288Sclaudio {
2468c2eb288Sclaudio 	size_t s;
2478c2eb288Sclaudio 
2481c699626Sbeck 	if (usz > MAX_URI_LENGTH)
24958f449faSjob 		return 0;
25058f449faSjob 
2518c2eb288Sclaudio 	for (s = 0; s < usz; s++)
2528c2eb288Sclaudio 		if (!isalnum((unsigned char)uri[s]) &&
2538c2eb288Sclaudio 		    !ispunct((unsigned char)uri[s]))
2548c2eb288Sclaudio 			return 0;
2558c2eb288Sclaudio 
2568c2eb288Sclaudio 	if (proto != NULL) {
2578c2eb288Sclaudio 		s = strlen(proto);
2583b405428Stb 		if (s >= usz)
2593b405428Stb 			return 0;
2608c2eb288Sclaudio 		if (strncasecmp(uri, proto, s) != 0)
2618c2eb288Sclaudio 			return 0;
2628c2eb288Sclaudio 	}
2638c2eb288Sclaudio 
2648c2eb288Sclaudio 	/* do not allow files or directories to start with a '.' */
2658c2eb288Sclaudio 	if (strstr(uri, "/.") != NULL)
2668c2eb288Sclaudio 		return 0;
2678c2eb288Sclaudio 
2688c2eb288Sclaudio 	return 1;
2698c2eb288Sclaudio }
27093d9375cSclaudio 
27193d9375cSclaudio /*
27293d9375cSclaudio  * Validate that a URI has the same host as the URI passed in proto.
27393d9375cSclaudio  * Returns 1 if valid, 0 otherwise.
27493d9375cSclaudio  */
27593d9375cSclaudio int
27693d9375cSclaudio valid_origin(const char *uri, const char *proto)
27793d9375cSclaudio {
27893d9375cSclaudio 	const char *to;
27993d9375cSclaudio 
28093d9375cSclaudio 	/* extract end of host from proto URI */
28193d9375cSclaudio 	to = strstr(proto, "://");
28293d9375cSclaudio 	if (to == NULL)
28393d9375cSclaudio 		return 0;
28493d9375cSclaudio 	to += strlen("://");
28593d9375cSclaudio 	if ((to = strchr(to, '/')) == NULL)
28693d9375cSclaudio 		return 0;
28793d9375cSclaudio 
28893d9375cSclaudio 	/* compare hosts including the / for the start of the path section */
28993d9375cSclaudio 	if (strncasecmp(uri, proto, to - proto + 1) != 0)
29093d9375cSclaudio 		return 0;
29193d9375cSclaudio 
29293d9375cSclaudio 	return 1;
29393d9375cSclaudio }
294c4a9443cSclaudio 
295c4a9443cSclaudio /*
296967224c8Stb  * Walk the tree of known valid CA certificates until we find a certificate that
297967224c8Stb  * doesn't inherit. Build a chain of intermediates and use the non-inheriting
298967224c8Stb  * certificate as a trusted root by virtue of X509_V_FLAG_PARTIAL_CHAIN. The
299967224c8Stb  * RFC 3779 path validation needs a non-inheriting trust root to ensure that
300967224c8Stb  * all delegated resources are covered.
301c4a9443cSclaudio  */
302c4a9443cSclaudio static void
303967224c8Stb build_chain(const struct auth *a, STACK_OF(X509) **intermediates,
304967224c8Stb     STACK_OF(X509) **root)
305c4a9443cSclaudio {
306967224c8Stb 	*intermediates = NULL;
307967224c8Stb 	*root = NULL;
308c4a9443cSclaudio 
309f8e924bcStb 	/* XXX - this should be removed, but filemode relies on it. */
310f8e924bcStb 	if (a == NULL)
311f8e924bcStb 		return;
312f8e924bcStb 
313967224c8Stb 	if ((*intermediates = sk_X509_new_null()) == NULL)
314967224c8Stb 		err(1, "sk_X509_new_null");
315967224c8Stb 	if ((*root = sk_X509_new_null()) == NULL)
316c4a9443cSclaudio 		err(1, "sk_X509_new_null");
317335482abStb 	for (; a != NULL; a = a->issuer) {
318c4a9443cSclaudio 		assert(a->cert->x509 != NULL);
319967224c8Stb 		if (!a->any_inherits) {
320967224c8Stb 			if (!sk_X509_push(*root, a->cert->x509))
321967224c8Stb 				errx(1, "sk_X509_push");
322967224c8Stb 			break;
323967224c8Stb 		}
324967224c8Stb 		if (!sk_X509_push(*intermediates, a->cert->x509))
325c4a9443cSclaudio 			errx(1, "sk_X509_push");
326c4a9443cSclaudio 	}
327967224c8Stb 	assert(sk_X509_num(*root) == 1);
328c4a9443cSclaudio }
329c4a9443cSclaudio 
330c4a9443cSclaudio /*
331c4a9443cSclaudio  * Add the CRL based on the certs SKI value.
332c4a9443cSclaudio  * No need to insert any other CRL since those were already checked.
333c4a9443cSclaudio  */
334c4a9443cSclaudio static void
335c4a9443cSclaudio build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls)
336c4a9443cSclaudio {
337c4a9443cSclaudio 	*crls = NULL;
338c4a9443cSclaudio 
339c4a9443cSclaudio 	if (crl == NULL)
340c4a9443cSclaudio 		return;
341c4a9443cSclaudio 	if ((*crls = sk_X509_CRL_new_null()) == NULL)
342c4a9443cSclaudio 		errx(1, "sk_X509_CRL_new_null");
343c4a9443cSclaudio 	if (!sk_X509_CRL_push(*crls, crl->x509_crl))
344c4a9443cSclaudio 		err(1, "sk_X509_CRL_push");
345c4a9443cSclaudio }
346c4a9443cSclaudio 
347c4a9443cSclaudio /*
34865827fe9Stb  * Attempt to upgrade the generic 'certificate revoked' message to include
34965827fe9Stb  * a timestamp.
35065827fe9Stb  */
35165827fe9Stb static void
35265827fe9Stb pretty_revocation_time(X509 *x509, X509_CRL *crl, const char **errstr)
35365827fe9Stb {
35465827fe9Stb 	static char		 buf[64];
35565827fe9Stb 	X509_REVOKED		*revoked;
35665827fe9Stb 	const ASN1_TIME		*atime;
35765827fe9Stb 	time_t			 t;
35865827fe9Stb 
35965827fe9Stb 	if (X509_CRL_get0_by_cert(crl, &revoked, x509) != 1)
36065827fe9Stb 		return;
36165827fe9Stb 	if ((atime = X509_REVOKED_get0_revocationDate(revoked)) == NULL)
36265827fe9Stb 		return;
36365827fe9Stb 	if (!x509_get_time(atime, &t))
36465827fe9Stb 		return;
36565827fe9Stb 
36665827fe9Stb 	snprintf(buf, sizeof(buf), "certificate revoked on %s", time2str(t));
36765827fe9Stb 	*errstr = buf;
36865827fe9Stb }
36965827fe9Stb 
37065827fe9Stb /*
371e0cb527bSclaudio  * Validate the X509 certificate. Returns 1 for valid certificates,
372e0cb527bSclaudio  * returns 0 if there is a verify error and sets *errstr to the error
373e0cb527bSclaudio  * returned by X509_verify_cert_error_string().
374c4a9443cSclaudio  */
375c4a9443cSclaudio int
376c4a9443cSclaudio valid_x509(char *file, X509_STORE_CTX *store_ctx, X509 *x509, struct auth *a,
377fd7a2857Sclaudio     struct crl *crl, const char **errstr)
378c4a9443cSclaudio {
379c4a9443cSclaudio 	X509_VERIFY_PARAM	*params;
380c4a9443cSclaudio 	ASN1_OBJECT		*cp_oid;
381967224c8Stb 	STACK_OF(X509)		*intermediates, *root;
382c4a9443cSclaudio 	STACK_OF(X509_CRL)	*crls = NULL;
383c4a9443cSclaudio 	unsigned long		 flags;
384fd7a2857Sclaudio 	int			 error;
385c4a9443cSclaudio 
386fd7a2857Sclaudio 	*errstr = NULL;
387967224c8Stb 	build_chain(a, &intermediates, &root);
388c4a9443cSclaudio 	build_crls(crl, &crls);
389c4a9443cSclaudio 
390c4a9443cSclaudio 	assert(store_ctx != NULL);
391c4a9443cSclaudio 	assert(x509 != NULL);
392c4a9443cSclaudio 	if (!X509_STORE_CTX_init(store_ctx, NULL, x509, NULL))
393c0528901Stb 		err(1, "X509_STORE_CTX_init");
394c4a9443cSclaudio 
395c4a9443cSclaudio 	if ((params = X509_STORE_CTX_get0_param(store_ctx)) == NULL)
396c0528901Stb 		errx(1, "X509_STORE_CTX_get0_param");
397c4a9443cSclaudio 	if ((cp_oid = OBJ_dup(certpol_oid)) == NULL)
398c0528901Stb 		err(1, "OBJ_dup");
399c4a9443cSclaudio 	if (!X509_VERIFY_PARAM_add0_policy(params, cp_oid))
400c0528901Stb 		err(1, "X509_VERIFY_PARAM_add0_policy");
4010876134dSclaudio 	X509_VERIFY_PARAM_set_time(params, get_current_time());
402c4a9443cSclaudio 
403c4a9443cSclaudio 	flags = X509_V_FLAG_CRL_CHECK;
404967224c8Stb 	flags |= X509_V_FLAG_PARTIAL_CHAIN;
405bc7ae01aStb 	flags |= X509_V_FLAG_POLICY_CHECK;
406c4a9443cSclaudio 	flags |= X509_V_FLAG_EXPLICIT_POLICY;
407c4a9443cSclaudio 	flags |= X509_V_FLAG_INHIBIT_MAP;
408c4a9443cSclaudio 	X509_STORE_CTX_set_flags(store_ctx, flags);
409c4a9443cSclaudio 	X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH);
410967224c8Stb 	/*
411967224c8Stb 	 * See the comment above build_chain() for details on what's happening
412967224c8Stb 	 * here. The nomenclature in this API is dubious and poorly documented.
413967224c8Stb 	 */
414967224c8Stb 	X509_STORE_CTX_set0_untrusted(store_ctx, intermediates);
415967224c8Stb 	X509_STORE_CTX_set0_trusted_stack(store_ctx, root);
416c4a9443cSclaudio 	X509_STORE_CTX_set0_crls(store_ctx, crls);
417c4a9443cSclaudio 
418c4a9443cSclaudio 	if (X509_verify_cert(store_ctx) <= 0) {
419fd7a2857Sclaudio 		error = X509_STORE_CTX_get_error(store_ctx);
420fd7a2857Sclaudio 		*errstr = X509_verify_cert_error_string(error);
42165827fe9Stb 		if (filemode && error == X509_V_ERR_CERT_REVOKED)
42265827fe9Stb 			pretty_revocation_time(x509, crl->x509_crl, errstr);
423c4a9443cSclaudio 		X509_STORE_CTX_cleanup(store_ctx);
424967224c8Stb 		sk_X509_free(intermediates);
425967224c8Stb 		sk_X509_free(root);
426c4a9443cSclaudio 		sk_X509_CRL_free(crls);
427c4a9443cSclaudio 		return 0;
428c4a9443cSclaudio 	}
429c4a9443cSclaudio 
430c4a9443cSclaudio 	X509_STORE_CTX_cleanup(store_ctx);
431967224c8Stb 	sk_X509_free(intermediates);
432967224c8Stb 	sk_X509_free(root);
433c4a9443cSclaudio 	sk_X509_CRL_free(crls);
434c4a9443cSclaudio 	return 1;
435c4a9443cSclaudio }
436e6c729cdSjob 
437e6c729cdSjob /*
438e6c729cdSjob  * Validate our RSC: check that all items in the ResourceBlock are contained.
439e6c729cdSjob  * Returns 1 if valid, 0 otherwise.
440e6c729cdSjob  */
441e6c729cdSjob int
44299dbdb7fStb valid_rsc(const char *fn, struct cert *cert, struct rsc *rsc)
443e6c729cdSjob {
444e6c729cdSjob 	size_t		i;
445e6c729cdSjob 	uint32_t	min, max;
446e6c729cdSjob 
447*381ee599Stb 	for (i = 0; i < rsc->num_ases; i++) {
448*381ee599Stb 		if (rsc->ases[i].type == CERT_AS_ID) {
449*381ee599Stb 			min = rsc->ases[i].id;
450*381ee599Stb 			max = rsc->ases[i].id;
451782a58ffSjob 		} else {
452*381ee599Stb 			min = rsc->ases[i].range.min;
453*381ee599Stb 			max = rsc->ases[i].range.max;
454782a58ffSjob 		}
455e6c729cdSjob 
456*381ee599Stb 		if (as_check_covered(min, max, cert->ases, cert->num_ases) > 0)
457e6c729cdSjob 			continue;
458e6c729cdSjob 
459*381ee599Stb 		as_warn(fn, "RSC ResourceBlock uncovered", &rsc->ases[i]);
460e6c729cdSjob 		return 0;
461e6c729cdSjob 	}
462e6c729cdSjob 
463*381ee599Stb 	for (i = 0; i < rsc->num_ips; i++) {
46499dbdb7fStb 		if (ip_addr_check_covered(rsc->ips[i].afi, rsc->ips[i].min,
465*381ee599Stb 		    rsc->ips[i].max, cert->ips, cert->num_ips) > 0)
466e6c729cdSjob 			continue;
467e6c729cdSjob 
4684032f119Stb 		ip_warn(fn, "RSC ResourceBlock uncovered", &rsc->ips[i]);
469e6c729cdSjob 		return 0;
470e6c729cdSjob 	}
471e6c729cdSjob 
472e6c729cdSjob 	return 1;
473e6c729cdSjob }
4748e5b9839Stb 
4758e5b9839Stb int
476e1686bd7Sjob valid_econtent_version(const char *fn, const ASN1_INTEGER *aint,
477e1686bd7Sjob     uint64_t expected)
4788e5b9839Stb {
479331e816cStb 	uint64_t version;
4808e5b9839Stb 
48176b14750Stb 	if (aint == NULL) {
48276b14750Stb 		if (expected == 0)
4838e5b9839Stb 			return 1;
48476b14750Stb 		warnx("%s: unexpected version 0", fn);
48576b14750Stb 		return 0;
48676b14750Stb 	}
4878e5b9839Stb 
488331e816cStb 	if (!ASN1_INTEGER_get_uint64(&version, aint)) {
489331e816cStb 		warnx("%s: ASN1_INTEGER_get_uint64 failed", fn);
4908e5b9839Stb 		return 0;
4918e5b9839Stb 	}
4928e5b9839Stb 
493e1686bd7Sjob 	if (version == 0) {
4948e5b9839Stb 		warnx("%s: incorrect encoding for version 0", fn);
4958e5b9839Stb 		return 0;
496e1686bd7Sjob 	}
497e1686bd7Sjob 
498e1686bd7Sjob 	if (version != expected) {
499e1686bd7Sjob 		warnx("%s: unexpected version (expected %llu, got %llu)", fn,
500e1686bd7Sjob 		    (unsigned long long)expected, (unsigned long long)version);
5018e5b9839Stb 		return 0;
5028e5b9839Stb 	}
503e1686bd7Sjob 
504e1686bd7Sjob 	return 1;
5058e5b9839Stb }
506a29ddfd5Sjob 
507a29ddfd5Sjob /*
508a29ddfd5Sjob  * Validate the ASPA: check that the customerASID is contained.
509a29ddfd5Sjob  * Returns 1 if valid, 0 otherwise.
510a29ddfd5Sjob  */
511a29ddfd5Sjob int
512a29ddfd5Sjob valid_aspa(const char *fn, struct cert *cert, struct aspa *aspa)
513a29ddfd5Sjob {
514a29ddfd5Sjob 
515a29ddfd5Sjob 	if (as_check_covered(aspa->custasid, aspa->custasid,
516*381ee599Stb 	    cert->ases, cert->num_ases) > 0)
517a29ddfd5Sjob 		return 1;
518a29ddfd5Sjob 
519a29ddfd5Sjob 	warnx("%s: ASPA: uncovered Customer ASID: %u", fn, aspa->custasid);
520a29ddfd5Sjob 
521a29ddfd5Sjob 	return 0;
522a29ddfd5Sjob }
523ef3f6f56Sjob 
524ef3f6f56Sjob /*
525ef3f6f56Sjob  * Validate Geofeed prefixes: check that the prefixes are contained.
526ef3f6f56Sjob  * Returns 1 if valid, 0 otherwise.
527ef3f6f56Sjob  */
528ef3f6f56Sjob int
529ef3f6f56Sjob valid_geofeed(const char *fn, struct cert *cert, struct geofeed *g)
530ef3f6f56Sjob {
531ef3f6f56Sjob 	size_t	 i;
532ef3f6f56Sjob 	char	 buf[64];
533ef3f6f56Sjob 
534*381ee599Stb 	for (i = 0; i < g->num_geoips; i++) {
535ef3f6f56Sjob 		if (ip_addr_check_covered(g->geoips[i].ip->afi,
536ef3f6f56Sjob 		    g->geoips[i].ip->min, g->geoips[i].ip->max, cert->ips,
537*381ee599Stb 		    cert->num_ips) > 0)
538ef3f6f56Sjob 			continue;
539ef3f6f56Sjob 
540ef3f6f56Sjob 		ip_addr_print(&g->geoips[i].ip->ip, g->geoips[i].ip->afi, buf,
541ef3f6f56Sjob 		    sizeof(buf));
542ef3f6f56Sjob 		warnx("%s: Geofeed: uncovered IP: %s", fn, buf);
543ef3f6f56Sjob 		return 0;
544ef3f6f56Sjob 	}
545ef3f6f56Sjob 
546ef3f6f56Sjob 	return 1;
547ef3f6f56Sjob }
54845735addSclaudio 
54945735addSclaudio /*
55045735addSclaudio  * Validate whether a given string is a valid UUID.
55145735addSclaudio  * Returns 1 if valid, 0 otherwise.
55245735addSclaudio  */
55345735addSclaudio int
55445735addSclaudio valid_uuid(const char *s)
55545735addSclaudio {
55645735addSclaudio 	int n = 0;
55745735addSclaudio 
55845735addSclaudio 	while (1) {
55945735addSclaudio 		switch (n) {
56045735addSclaudio 		case 8:
56145735addSclaudio 		case 13:
56245735addSclaudio 		case 18:
56345735addSclaudio 		case 23:
56445735addSclaudio 			if (s[n] != '-')
56545735addSclaudio 				return 0;
56645735addSclaudio 			break;
56745735addSclaudio 		/* Check UUID is version 4 */
56845735addSclaudio 		case 14:
56945735addSclaudio 			if (s[n] != '4')
57045735addSclaudio 				return 0;
57145735addSclaudio 			break;
57245735addSclaudio 		/* Check UUID variant is 1 */
57345735addSclaudio 		case 19:
57445735addSclaudio 			if (s[n] != '8' && s[n] != '9' && s[n] != 'a' &&
57545735addSclaudio 			    s[n] != 'A' && s[n] != 'b' && s[n] != 'B')
57645735addSclaudio 				return 0;
57745735addSclaudio 			break;
57845735addSclaudio 		case 36:
57945735addSclaudio 			return s[n] == '\0';
58045735addSclaudio 		default:
58145735addSclaudio 			if (!isxdigit((unsigned char)s[n]))
58245735addSclaudio 				return 0;
58345735addSclaudio 			break;
58445735addSclaudio 		}
58545735addSclaudio 		n++;
58645735addSclaudio 	}
58745735addSclaudio }
58845735addSclaudio 
589ec1cc732Sjob static int
590ec1cc732Sjob valid_ca_pkey_rsa(const char *fn, EVP_PKEY *pkey)
591ae36eebeSjob {
5929a67f0c9Stb 	const RSA	*rsa;
593ae36eebeSjob 	const BIGNUM	*rsa_e;
594ae36eebeSjob 	int		 key_bits;
595ae36eebeSjob 
596ae36eebeSjob 	if ((key_bits = EVP_PKEY_bits(pkey)) != 2048) {
597ae36eebeSjob 		warnx("%s: RFC 7935: expected 2048-bit modulus, got %d bits",
598ae36eebeSjob 		    fn, key_bits);
599ae36eebeSjob 		return 0;
600ae36eebeSjob 	}
601ae36eebeSjob 
602ae36eebeSjob 	if ((rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
603ae36eebeSjob 		warnx("%s: failed to extract RSA public key", fn);
604ae36eebeSjob 		return 0;
605ae36eebeSjob 	}
606ae36eebeSjob 
607ae36eebeSjob 	if ((rsa_e = RSA_get0_e(rsa)) == NULL) {
608ae36eebeSjob 		warnx("%s: failed to get RSA exponent", fn);
609ae36eebeSjob 		return 0;
610ae36eebeSjob 	}
611ae36eebeSjob 
612ae36eebeSjob 	if (!BN_is_word(rsa_e, 65537)) {
613ae36eebeSjob 		warnx("%s: incorrect exponent (e) in RSA public key", fn);
614ae36eebeSjob 		return 0;
615ae36eebeSjob 	}
616ae36eebeSjob 
617ae36eebeSjob 	return 1;
618ae36eebeSjob }
619ec1cc732Sjob 
620ec1cc732Sjob static int
621ec1cc732Sjob valid_ca_pkey_ec(const char *fn, EVP_PKEY *pkey)
622ec1cc732Sjob {
6239a67f0c9Stb 	const EC_KEY	*ec;
624ec1cc732Sjob 	const EC_GROUP	*group;
625ec1cc732Sjob 	int		 nid;
626ec1cc732Sjob 	const char	*cname;
627ec1cc732Sjob 
628ec1cc732Sjob 	if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
629ec1cc732Sjob 		warnx("%s: failed to extract ECDSA public key", fn);
630ec1cc732Sjob 		return 0;
631ec1cc732Sjob 	}
632ec1cc732Sjob 
633ec1cc732Sjob 	if ((group = EC_KEY_get0_group(ec)) == NULL) {
634ec1cc732Sjob 		warnx("%s: EC_KEY_get0_group failed", fn);
635ec1cc732Sjob 		return 0;
636ec1cc732Sjob 	}
637ec1cc732Sjob 
638ec1cc732Sjob 	nid = EC_GROUP_get_curve_name(group);
639ec1cc732Sjob 	if (nid != NID_X9_62_prime256v1) {
640ec1cc732Sjob 		if ((cname = EC_curve_nid2nist(nid)) == NULL)
64178de3577Stb 			cname = nid2str(nid);
642ec1cc732Sjob 		warnx("%s: Expected P-256, got %s", fn, cname);
643ec1cc732Sjob 		return 0;
644ec1cc732Sjob 	}
645ec1cc732Sjob 
646ec1cc732Sjob 	if (!EC_KEY_check_key(ec)) {
647ec1cc732Sjob 		warnx("%s: EC_KEY_check_key failed", fn);
648ec1cc732Sjob 		return 0;
649ec1cc732Sjob 	}
650ec1cc732Sjob 
651ec1cc732Sjob 	return 1;
652ec1cc732Sjob }
653ec1cc732Sjob 
654ec1cc732Sjob int
655ec1cc732Sjob valid_ca_pkey(const char *fn, EVP_PKEY *pkey)
656ec1cc732Sjob {
657ec1cc732Sjob 	if (pkey == NULL) {
658ec1cc732Sjob 		warnx("%s: failure, pkey is NULL", fn);
659ec1cc732Sjob 		return 0;
660ec1cc732Sjob 	}
661ec1cc732Sjob 
662ec1cc732Sjob 	if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA)
663ec1cc732Sjob 		return valid_ca_pkey_rsa(fn, pkey);
664ec1cc732Sjob 
665ec1cc732Sjob 	if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC)
666ec1cc732Sjob 		return valid_ca_pkey_ec(fn, pkey);
667ec1cc732Sjob 
668ec1cc732Sjob 	warnx("%s: unsupported public key algorithm", fn);
669ec1cc732Sjob 	return 0;
670ec1cc732Sjob }
671