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