xref: /openbsd/usr.sbin/rpki-client/validate.c (revision 9a67f0c9)
1 /*	$OpenBSD: validate.c,v 1.77 2024/10/16 06:09:45 tb Exp $ */
2 /*
3  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
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 <arpa/inet.h>
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "extern.h"
29 
30 extern ASN1_OBJECT	*certpol_oid;
31 
32 /*
33  * Walk up the chain of certificates trying to match our AS number to
34  * one of the allocations in that chain.
35  * Returns 1 if covered or 0 if not.
36  */
37 static int
valid_as(struct auth * a,uint32_t min,uint32_t max)38 valid_as(struct auth *a, uint32_t min, uint32_t max)
39 {
40 	int	 c;
41 
42 	if (a == NULL)
43 		return 0;
44 
45 	/* Does this certificate cover our AS number? */
46 	c = as_check_covered(min, max, a->cert->as, a->cert->asz);
47 	if (c > 0)
48 		return 1;
49 	else if (c < 0)
50 		return 0;
51 
52 	/* If it inherits, walk up the chain. */
53 	return valid_as(a->issuer, min, max);
54 }
55 
56 /*
57  * Walk up the chain of certificates (really just the last one, but in
58  * the case of inheritance, the ones before) making sure that our IP
59  * prefix is covered in the first non-inheriting specification.
60  * Returns 1 if covered or 0 if not.
61  */
62 static int
valid_ip(struct auth * a,enum afi afi,const unsigned char * min,const unsigned char * max)63 valid_ip(struct auth *a, enum afi afi,
64     const unsigned char *min, const unsigned char *max)
65 {
66 	int	 c;
67 
68 	if (a == NULL)
69 		return 0;
70 
71 	/* Does this certificate cover our IP prefix? */
72 	c = ip_addr_check_covered(afi, min, max, a->cert->ips, a->cert->ipsz);
73 	if (c > 0)
74 		return 1;
75 	else if (c < 0)
76 		return 0;
77 
78 	/* If it inherits, walk up the chain. */
79 	return valid_ip(a->issuer, afi, min, max);
80 }
81 
82 /*
83  * Validate a non-TA certificate: make sure its IP and AS resources are
84  * fully covered by those in the authority key (which must exist).
85  * Returns 1 if valid, 0 otherwise.
86  */
87 int
valid_cert(const char * fn,struct auth * a,const struct cert * cert)88 valid_cert(const char *fn, struct auth *a, const struct cert *cert)
89 {
90 	size_t		 i;
91 	uint32_t	 min, max;
92 
93 	for (i = 0; i < cert->asz; i++) {
94 		if (cert->as[i].type == CERT_AS_INHERIT)
95 			continue;
96 
97 		if (cert->as[i].type == CERT_AS_ID) {
98 			min = cert->as[i].id;
99 			max = cert->as[i].id;
100 		} else {
101 			min = cert->as[i].range.min;
102 			max = cert->as[i].range.max;
103 		}
104 
105 		if (valid_as(a, min, max))
106 			continue;
107 
108 		as_warn(fn, "RFC 6487: uncovered resource", &cert->as[i]);
109 		return 0;
110 	}
111 
112 	for (i = 0; i < cert->ipsz; i++) {
113 		if (cert->ips[i].type == CERT_IP_INHERIT)
114 			continue;
115 
116 		if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min,
117 		    cert->ips[i].max))
118 			continue;
119 
120 		ip_warn(fn, "RFC 6487: uncovered resource", &cert->ips[i]);
121 		return 0;
122 	}
123 
124 	return 1;
125 }
126 
127 /*
128  * Validate our ROA: check that the prefixes (ipAddrBlocks) are contained.
129  * Returns 1 if valid, 0 otherwise.
130  */
131 int
valid_roa(const char * fn,struct cert * cert,struct roa * roa)132 valid_roa(const char *fn, struct cert *cert, struct roa *roa)
133 {
134 	size_t	 i;
135 	char	 buf[64];
136 
137 	for (i = 0; i < roa->ipsz; i++) {
138 		if (ip_addr_check_covered(roa->ips[i].afi, roa->ips[i].min,
139 		    roa->ips[i].max, cert->ips, cert->ipsz) > 0)
140 			continue;
141 
142 		ip_addr_print(&roa->ips[i].addr, roa->ips[i].afi, buf,
143 		    sizeof(buf));
144 		warnx("%s: RFC 6482: uncovered IP: %s", fn, buf);
145 		return 0;
146 	}
147 
148 	return 1;
149 }
150 
151 /*
152  * Validate our SPL: check that the asID is contained in the end-entity
153  * certificate's resources.
154  * Returns 1 if valid, 0 otherwise.
155  */
156 int
valid_spl(const char * fn,struct cert * cert,struct spl * spl)157 valid_spl(const char *fn, struct cert *cert, struct spl *spl)
158 {
159 	if (as_check_covered(spl->asid, spl->asid, cert->as, cert->asz) > 0)
160 		return 1;
161 
162 	warnx("%s: SPL: uncovered ASID: %u", fn, spl->asid);
163 
164 	return 0;
165 }
166 
167 /*
168  * Validate a file by verifying the SHA256 hash of that file.
169  * The file to check is passed as a file descriptor.
170  * Returns 1 if hash matched, 0 otherwise. Closes fd when done.
171  */
172 int
valid_filehash(int fd,const char * hash,size_t hlen)173 valid_filehash(int fd, const char *hash, size_t hlen)
174 {
175 	SHA256_CTX	ctx;
176 	char		filehash[SHA256_DIGEST_LENGTH];
177 	char		buffer[8192];
178 	ssize_t		nr;
179 
180 	if (hlen != sizeof(filehash))
181 		errx(1, "bad hash size");
182 
183 	if (fd == -1)
184 		return 0;
185 
186 	SHA256_Init(&ctx);
187 	while ((nr = read(fd, buffer, sizeof(buffer))) > 0)
188 		SHA256_Update(&ctx, buffer, nr);
189 	close(fd);
190 	SHA256_Final(filehash, &ctx);
191 
192 	if (memcmp(hash, filehash, sizeof(filehash)) != 0)
193 		return 0;
194 	return 1;
195 }
196 
197 /*
198  * Same as above but with a buffer instead of a fd.
199  */
200 int
valid_hash(unsigned char * buf,size_t len,const char * hash,size_t hlen)201 valid_hash(unsigned char *buf, size_t len, const char *hash, size_t hlen)
202 {
203 	char	filehash[SHA256_DIGEST_LENGTH];
204 
205 	if (hlen != sizeof(filehash))
206 		errx(1, "bad hash size");
207 
208 	if (buf == NULL || len == 0)
209 		return 0;
210 
211 	if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL))
212 		errx(1, "EVP_Digest failed");
213 
214 	if (memcmp(hash, filehash, sizeof(filehash)) != 0)
215 		return 0;
216 	return 1;
217 }
218 
219 /*
220  * Validate that a filename only contains characters from the POSIX portable
221  * filename character set [A-Za-z0-9._-], see IEEE Std 1003.1-2013, 3.278.
222  */
223 int
valid_filename(const char * fn,size_t len)224 valid_filename(const char *fn, size_t len)
225 {
226 	const unsigned char *c;
227 	size_t i;
228 
229 	for (c = fn, i = 0; i < len; i++, c++)
230 		if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.')
231 			return 0;
232 	return 1;
233 }
234 
235 /*
236  * Validate a URI to make sure it is pure ASCII and does not point backwards
237  * or doing some other silly tricks. To enforce the protocol pass either
238  * https:// or rsync:// as proto, if NULL is passed no protocol is enforced.
239  * Returns 1 if valid, 0 otherwise.
240  */
241 int
valid_uri(const char * uri,size_t usz,const char * proto)242 valid_uri(const char *uri, size_t usz, const char *proto)
243 {
244 	size_t s;
245 
246 	if (usz > MAX_URI_LENGTH)
247 		return 0;
248 
249 	for (s = 0; s < usz; s++)
250 		if (!isalnum((unsigned char)uri[s]) &&
251 		    !ispunct((unsigned char)uri[s]))
252 			return 0;
253 
254 	if (proto != NULL) {
255 		s = strlen(proto);
256 		if (s >= usz)
257 			return 0;
258 		if (strncasecmp(uri, proto, s) != 0)
259 			return 0;
260 	}
261 
262 	/* do not allow files or directories to start with a '.' */
263 	if (strstr(uri, "/.") != NULL)
264 		return 0;
265 
266 	return 1;
267 }
268 
269 /*
270  * Validate that a URI has the same host as the URI passed in proto.
271  * Returns 1 if valid, 0 otherwise.
272  */
273 int
valid_origin(const char * uri,const char * proto)274 valid_origin(const char *uri, const char *proto)
275 {
276 	const char *to;
277 
278 	/* extract end of host from proto URI */
279 	to = strstr(proto, "://");
280 	if (to == NULL)
281 		return 0;
282 	to += strlen("://");
283 	if ((to = strchr(to, '/')) == NULL)
284 		return 0;
285 
286 	/* compare hosts including the / for the start of the path section */
287 	if (strncasecmp(uri, proto, to - proto + 1) != 0)
288 		return 0;
289 
290 	return 1;
291 }
292 
293 /*
294  * Walk the tree of known valid CA certificates until we find a certificate that
295  * doesn't inherit. Build a chain of intermediates and use the non-inheriting
296  * certificate as a trusted root by virtue of X509_V_FLAG_PARTIAL_CHAIN. The
297  * RFC 3779 path validation needs a non-inheriting trust root to ensure that
298  * all delegated resources are covered.
299  */
300 static void
build_chain(const struct auth * a,STACK_OF (X509)** intermediates,STACK_OF (X509)** root)301 build_chain(const struct auth *a, STACK_OF(X509) **intermediates,
302     STACK_OF(X509) **root)
303 {
304 	*intermediates = NULL;
305 	*root = NULL;
306 
307 	/* XXX - this should be removed, but filemode relies on it. */
308 	if (a == NULL)
309 		return;
310 
311 	if ((*intermediates = sk_X509_new_null()) == NULL)
312 		err(1, "sk_X509_new_null");
313 	if ((*root = sk_X509_new_null()) == NULL)
314 		err(1, "sk_X509_new_null");
315 	for (; a != NULL; a = a->issuer) {
316 		assert(a->cert->x509 != NULL);
317 		if (!a->any_inherits) {
318 			if (!sk_X509_push(*root, a->cert->x509))
319 				errx(1, "sk_X509_push");
320 			break;
321 		}
322 		if (!sk_X509_push(*intermediates, a->cert->x509))
323 			errx(1, "sk_X509_push");
324 	}
325 	assert(sk_X509_num(*root) == 1);
326 }
327 
328 /*
329  * Add the CRL based on the certs SKI value.
330  * No need to insert any other CRL since those were already checked.
331  */
332 static void
build_crls(const struct crl * crl,STACK_OF (X509_CRL)** crls)333 build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls)
334 {
335 	*crls = NULL;
336 
337 	if (crl == NULL)
338 		return;
339 	if ((*crls = sk_X509_CRL_new_null()) == NULL)
340 		errx(1, "sk_X509_CRL_new_null");
341 	if (!sk_X509_CRL_push(*crls, crl->x509_crl))
342 		err(1, "sk_X509_CRL_push");
343 }
344 
345 /*
346  * Attempt to upgrade the generic 'certificate revoked' message to include
347  * a timestamp.
348  */
349 static void
pretty_revocation_time(X509 * x509,X509_CRL * crl,const char ** errstr)350 pretty_revocation_time(X509 *x509, X509_CRL *crl, const char **errstr)
351 {
352 	static char		 buf[64];
353 	X509_REVOKED		*revoked;
354 	const ASN1_TIME		*atime;
355 	time_t			 t;
356 
357 	if (X509_CRL_get0_by_cert(crl, &revoked, x509) != 1)
358 		return;
359 	if ((atime = X509_REVOKED_get0_revocationDate(revoked)) == NULL)
360 		return;
361 	if (!x509_get_time(atime, &t))
362 		return;
363 
364 	snprintf(buf, sizeof(buf), "certificate revoked on %s", time2str(t));
365 	*errstr = buf;
366 }
367 
368 /*
369  * Validate the X509 certificate. Returns 1 for valid certificates,
370  * returns 0 if there is a verify error and sets *errstr to the error
371  * returned by X509_verify_cert_error_string().
372  */
373 int
valid_x509(char * file,X509_STORE_CTX * store_ctx,X509 * x509,struct auth * a,struct crl * crl,const char ** errstr)374 valid_x509(char *file, X509_STORE_CTX *store_ctx, X509 *x509, struct auth *a,
375     struct crl *crl, const char **errstr)
376 {
377 	X509_VERIFY_PARAM	*params;
378 	ASN1_OBJECT		*cp_oid;
379 	STACK_OF(X509)		*intermediates, *root;
380 	STACK_OF(X509_CRL)	*crls = NULL;
381 	unsigned long		 flags;
382 	int			 error;
383 
384 	*errstr = NULL;
385 	build_chain(a, &intermediates, &root);
386 	build_crls(crl, &crls);
387 
388 	assert(store_ctx != NULL);
389 	assert(x509 != NULL);
390 	if (!X509_STORE_CTX_init(store_ctx, NULL, x509, NULL))
391 		err(1, "X509_STORE_CTX_init");
392 
393 	if ((params = X509_STORE_CTX_get0_param(store_ctx)) == NULL)
394 		errx(1, "X509_STORE_CTX_get0_param");
395 	if ((cp_oid = OBJ_dup(certpol_oid)) == NULL)
396 		err(1, "OBJ_dup");
397 	if (!X509_VERIFY_PARAM_add0_policy(params, cp_oid))
398 		err(1, "X509_VERIFY_PARAM_add0_policy");
399 	X509_VERIFY_PARAM_set_time(params, get_current_time());
400 
401 	flags = X509_V_FLAG_CRL_CHECK;
402 	flags |= X509_V_FLAG_PARTIAL_CHAIN;
403 	flags |= X509_V_FLAG_POLICY_CHECK;
404 	flags |= X509_V_FLAG_EXPLICIT_POLICY;
405 	flags |= X509_V_FLAG_INHIBIT_MAP;
406 	X509_STORE_CTX_set_flags(store_ctx, flags);
407 	X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH);
408 	/*
409 	 * See the comment above build_chain() for details on what's happening
410 	 * here. The nomenclature in this API is dubious and poorly documented.
411 	 */
412 	X509_STORE_CTX_set0_untrusted(store_ctx, intermediates);
413 	X509_STORE_CTX_set0_trusted_stack(store_ctx, root);
414 	X509_STORE_CTX_set0_crls(store_ctx, crls);
415 
416 	if (X509_verify_cert(store_ctx) <= 0) {
417 		error = X509_STORE_CTX_get_error(store_ctx);
418 		*errstr = X509_verify_cert_error_string(error);
419 		if (filemode && error == X509_V_ERR_CERT_REVOKED)
420 			pretty_revocation_time(x509, crl->x509_crl, errstr);
421 		X509_STORE_CTX_cleanup(store_ctx);
422 		sk_X509_free(intermediates);
423 		sk_X509_free(root);
424 		sk_X509_CRL_free(crls);
425 		return 0;
426 	}
427 
428 	X509_STORE_CTX_cleanup(store_ctx);
429 	sk_X509_free(intermediates);
430 	sk_X509_free(root);
431 	sk_X509_CRL_free(crls);
432 	return 1;
433 }
434 
435 /*
436  * Validate our RSC: check that all items in the ResourceBlock are contained.
437  * Returns 1 if valid, 0 otherwise.
438  */
439 int
valid_rsc(const char * fn,struct cert * cert,struct rsc * rsc)440 valid_rsc(const char *fn, struct cert *cert, struct rsc *rsc)
441 {
442 	size_t		i;
443 	uint32_t	min, max;
444 
445 	for (i = 0; i < rsc->asz; i++) {
446 		if (rsc->as[i].type == CERT_AS_ID) {
447 			min = rsc->as[i].id;
448 			max = rsc->as[i].id;
449 		} else {
450 			min = rsc->as[i].range.min;
451 			max = rsc->as[i].range.max;
452 		}
453 
454 		if (as_check_covered(min, max, cert->as, cert->asz) > 0)
455 			continue;
456 
457 		as_warn(fn, "RSC ResourceBlock uncovered", &rsc->as[i]);
458 		return 0;
459 	}
460 
461 	for (i = 0; i < rsc->ipsz; i++) {
462 		if (ip_addr_check_covered(rsc->ips[i].afi, rsc->ips[i].min,
463 		    rsc->ips[i].max, cert->ips, cert->ipsz) > 0)
464 			continue;
465 
466 		ip_warn(fn, "RSC ResourceBlock uncovered", &rsc->ips[i]);
467 		return 0;
468 	}
469 
470 	return 1;
471 }
472 
473 int
valid_econtent_version(const char * fn,const ASN1_INTEGER * aint,uint64_t expected)474 valid_econtent_version(const char *fn, const ASN1_INTEGER *aint,
475     uint64_t expected)
476 {
477 	uint64_t version;
478 
479 	if (aint == NULL) {
480 		if (expected == 0)
481 			return 1;
482 		warnx("%s: unexpected version 0", fn);
483 		return 0;
484 	}
485 
486 	if (!ASN1_INTEGER_get_uint64(&version, aint)) {
487 		warnx("%s: ASN1_INTEGER_get_uint64 failed", fn);
488 		return 0;
489 	}
490 
491 	if (version == 0) {
492 		warnx("%s: incorrect encoding for version 0", fn);
493 		return 0;
494 	}
495 
496 	if (version != expected) {
497 		warnx("%s: unexpected version (expected %llu, got %llu)", fn,
498 		    (unsigned long long)expected, (unsigned long long)version);
499 		return 0;
500 	}
501 
502 	return 1;
503 }
504 
505 /*
506  * Validate the ASPA: check that the customerASID is contained.
507  * Returns 1 if valid, 0 otherwise.
508  */
509 int
valid_aspa(const char * fn,struct cert * cert,struct aspa * aspa)510 valid_aspa(const char *fn, struct cert *cert, struct aspa *aspa)
511 {
512 
513 	if (as_check_covered(aspa->custasid, aspa->custasid,
514 	    cert->as, cert->asz) > 0)
515 		return 1;
516 
517 	warnx("%s: ASPA: uncovered Customer ASID: %u", fn, aspa->custasid);
518 
519 	return 0;
520 }
521 
522 /*
523  * Validate Geofeed prefixes: check that the prefixes are contained.
524  * Returns 1 if valid, 0 otherwise.
525  */
526 int
valid_geofeed(const char * fn,struct cert * cert,struct geofeed * g)527 valid_geofeed(const char *fn, struct cert *cert, struct geofeed *g)
528 {
529 	size_t	 i;
530 	char	 buf[64];
531 
532 	for (i = 0; i < g->geoipsz; i++) {
533 		if (ip_addr_check_covered(g->geoips[i].ip->afi,
534 		    g->geoips[i].ip->min, g->geoips[i].ip->max, cert->ips,
535 		    cert->ipsz) > 0)
536 			continue;
537 
538 		ip_addr_print(&g->geoips[i].ip->ip, g->geoips[i].ip->afi, buf,
539 		    sizeof(buf));
540 		warnx("%s: Geofeed: uncovered IP: %s", fn, buf);
541 		return 0;
542 	}
543 
544 	return 1;
545 }
546 
547 /*
548  * Validate whether a given string is a valid UUID.
549  * Returns 1 if valid, 0 otherwise.
550  */
551 int
valid_uuid(const char * s)552 valid_uuid(const char *s)
553 {
554 	int n = 0;
555 
556 	while (1) {
557 		switch (n) {
558 		case 8:
559 		case 13:
560 		case 18:
561 		case 23:
562 			if (s[n] != '-')
563 				return 0;
564 			break;
565 		/* Check UUID is version 4 */
566 		case 14:
567 			if (s[n] != '4')
568 				return 0;
569 			break;
570 		/* Check UUID variant is 1 */
571 		case 19:
572 			if (s[n] != '8' && s[n] != '9' && s[n] != 'a' &&
573 			    s[n] != 'A' && s[n] != 'b' && s[n] != 'B')
574 				return 0;
575 			break;
576 		case 36:
577 			return s[n] == '\0';
578 		default:
579 			if (!isxdigit((unsigned char)s[n]))
580 				return 0;
581 			break;
582 		}
583 		n++;
584 	}
585 }
586 
587 static int
valid_ca_pkey_rsa(const char * fn,EVP_PKEY * pkey)588 valid_ca_pkey_rsa(const char *fn, EVP_PKEY *pkey)
589 {
590 	const RSA	*rsa;
591 	const BIGNUM	*rsa_e;
592 	int		 key_bits;
593 
594 	if ((key_bits = EVP_PKEY_bits(pkey)) != 2048) {
595 		warnx("%s: RFC 7935: expected 2048-bit modulus, got %d bits",
596 		    fn, key_bits);
597 		return 0;
598 	}
599 
600 	if ((rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
601 		warnx("%s: failed to extract RSA public key", fn);
602 		return 0;
603 	}
604 
605 	if ((rsa_e = RSA_get0_e(rsa)) == NULL) {
606 		warnx("%s: failed to get RSA exponent", fn);
607 		return 0;
608 	}
609 
610 	if (!BN_is_word(rsa_e, 65537)) {
611 		warnx("%s: incorrect exponent (e) in RSA public key", fn);
612 		return 0;
613 	}
614 
615 	return 1;
616 }
617 
618 static int
valid_ca_pkey_ec(const char * fn,EVP_PKEY * pkey)619 valid_ca_pkey_ec(const char *fn, EVP_PKEY *pkey)
620 {
621 	const EC_KEY	*ec;
622 	const EC_GROUP	*group;
623 	int		 nid;
624 	const char	*cname;
625 
626 	if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
627 		warnx("%s: failed to extract ECDSA public key", fn);
628 		return 0;
629 	}
630 
631 	if ((group = EC_KEY_get0_group(ec)) == NULL) {
632 		warnx("%s: EC_KEY_get0_group failed", fn);
633 		return 0;
634 	}
635 
636 	nid = EC_GROUP_get_curve_name(group);
637 	if (nid != NID_X9_62_prime256v1) {
638 		if ((cname = EC_curve_nid2nist(nid)) == NULL)
639 			cname = nid2str(nid);
640 		warnx("%s: Expected P-256, got %s", fn, cname);
641 		return 0;
642 	}
643 
644 	if (!EC_KEY_check_key(ec)) {
645 		warnx("%s: EC_KEY_check_key failed", fn);
646 		return 0;
647 	}
648 
649 	return 1;
650 }
651 
652 int
valid_ca_pkey(const char * fn,EVP_PKEY * pkey)653 valid_ca_pkey(const char *fn, EVP_PKEY *pkey)
654 {
655 	if (pkey == NULL) {
656 		warnx("%s: failure, pkey is NULL", fn);
657 		return 0;
658 	}
659 
660 	if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA)
661 		return valid_ca_pkey_rsa(fn, pkey);
662 
663 	if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC)
664 		return valid_ca_pkey_ec(fn, pkey);
665 
666 	warnx("%s: unsupported public key algorithm", fn);
667 	return 0;
668 }
669