1 /* $OpenBSD: validate.c,v 1.74 2024/05/20 15:51:43 claudio 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 if (a == NULL)
308 return;
309
310 if ((*intermediates = sk_X509_new_null()) == NULL)
311 err(1, "sk_X509_new_null");
312 if ((*root = sk_X509_new_null()) == NULL)
313 err(1, "sk_X509_new_null");
314 for (; a != NULL; a = a->issuer) {
315 assert(a->cert->x509 != NULL);
316 if (!a->any_inherits) {
317 if (!sk_X509_push(*root, a->cert->x509))
318 errx(1, "sk_X509_push");
319 break;
320 }
321 if (!sk_X509_push(*intermediates, a->cert->x509))
322 errx(1, "sk_X509_push");
323 }
324 assert(sk_X509_num(*root) == 1);
325 }
326
327 /*
328 * Add the CRL based on the certs SKI value.
329 * No need to insert any other CRL since those were already checked.
330 */
331 static void
build_crls(const struct crl * crl,STACK_OF (X509_CRL)** crls)332 build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls)
333 {
334 *crls = NULL;
335
336 if (crl == NULL)
337 return;
338 if ((*crls = sk_X509_CRL_new_null()) == NULL)
339 errx(1, "sk_X509_CRL_new_null");
340 if (!sk_X509_CRL_push(*crls, crl->x509_crl))
341 err(1, "sk_X509_CRL_push");
342 }
343
344 /*
345 * Attempt to upgrade the generic 'certificate revoked' message to include
346 * a timestamp.
347 */
348 static void
pretty_revocation_time(X509 * x509,X509_CRL * crl,const char ** errstr)349 pretty_revocation_time(X509 *x509, X509_CRL *crl, const char **errstr)
350 {
351 static char buf[64];
352 X509_REVOKED *revoked;
353 const ASN1_TIME *atime;
354 time_t t;
355
356 if (X509_CRL_get0_by_cert(crl, &revoked, x509) != 1)
357 return;
358 if ((atime = X509_REVOKED_get0_revocationDate(revoked)) == NULL)
359 return;
360 if (!x509_get_time(atime, &t))
361 return;
362
363 snprintf(buf, sizeof(buf), "certificate revoked on %s", time2str(t));
364 *errstr = buf;
365 }
366
367 /*
368 * Validate the X509 certificate. Returns 1 for valid certificates,
369 * returns 0 if there is a verify error and sets *errstr to the error
370 * returned by X509_verify_cert_error_string().
371 */
372 int
valid_x509(char * file,X509_STORE_CTX * store_ctx,X509 * x509,struct auth * a,struct crl * crl,const char ** errstr)373 valid_x509(char *file, X509_STORE_CTX *store_ctx, X509 *x509, struct auth *a,
374 struct crl *crl, const char **errstr)
375 {
376 X509_VERIFY_PARAM *params;
377 ASN1_OBJECT *cp_oid;
378 STACK_OF(X509) *intermediates, *root;
379 STACK_OF(X509_CRL) *crls = NULL;
380 unsigned long flags;
381 int error;
382
383 *errstr = NULL;
384 build_chain(a, &intermediates, &root);
385 build_crls(crl, &crls);
386
387 assert(store_ctx != NULL);
388 assert(x509 != NULL);
389 if (!X509_STORE_CTX_init(store_ctx, NULL, x509, NULL))
390 err(1, "X509_STORE_CTX_init");
391
392 if ((params = X509_STORE_CTX_get0_param(store_ctx)) == NULL)
393 errx(1, "X509_STORE_CTX_get0_param");
394 if ((cp_oid = OBJ_dup(certpol_oid)) == NULL)
395 err(1, "OBJ_dup");
396 if (!X509_VERIFY_PARAM_add0_policy(params, cp_oid))
397 err(1, "X509_VERIFY_PARAM_add0_policy");
398 X509_VERIFY_PARAM_set_time(params, get_current_time());
399
400 flags = X509_V_FLAG_CRL_CHECK;
401 flags |= X509_V_FLAG_PARTIAL_CHAIN;
402 flags |= X509_V_FLAG_POLICY_CHECK;
403 flags |= X509_V_FLAG_EXPLICIT_POLICY;
404 flags |= X509_V_FLAG_INHIBIT_MAP;
405 X509_STORE_CTX_set_flags(store_ctx, flags);
406 X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH);
407 /*
408 * See the comment above build_chain() for details on what's happening
409 * here. The nomenclature in this API is dubious and poorly documented.
410 */
411 X509_STORE_CTX_set0_untrusted(store_ctx, intermediates);
412 X509_STORE_CTX_set0_trusted_stack(store_ctx, root);
413 X509_STORE_CTX_set0_crls(store_ctx, crls);
414
415 if (X509_verify_cert(store_ctx) <= 0) {
416 error = X509_STORE_CTX_get_error(store_ctx);
417 *errstr = X509_verify_cert_error_string(error);
418 if (filemode && error == X509_V_ERR_CERT_REVOKED)
419 pretty_revocation_time(x509, crl->x509_crl, errstr);
420 X509_STORE_CTX_cleanup(store_ctx);
421 sk_X509_free(intermediates);
422 sk_X509_free(root);
423 sk_X509_CRL_free(crls);
424 return 0;
425 }
426
427 X509_STORE_CTX_cleanup(store_ctx);
428 sk_X509_free(intermediates);
429 sk_X509_free(root);
430 sk_X509_CRL_free(crls);
431 return 1;
432 }
433
434 /*
435 * Validate our RSC: check that all items in the ResourceBlock are contained.
436 * Returns 1 if valid, 0 otherwise.
437 */
438 int
valid_rsc(const char * fn,struct cert * cert,struct rsc * rsc)439 valid_rsc(const char *fn, struct cert *cert, struct rsc *rsc)
440 {
441 size_t i;
442 uint32_t min, max;
443
444 for (i = 0; i < rsc->asz; i++) {
445 if (rsc->as[i].type == CERT_AS_ID) {
446 min = rsc->as[i].id;
447 max = rsc->as[i].id;
448 } else {
449 min = rsc->as[i].range.min;
450 max = rsc->as[i].range.max;
451 }
452
453 if (as_check_covered(min, max, cert->as, cert->asz) > 0)
454 continue;
455
456 as_warn(fn, "RSC ResourceBlock uncovered", &rsc->as[i]);
457 return 0;
458 }
459
460 for (i = 0; i < rsc->ipsz; i++) {
461 if (ip_addr_check_covered(rsc->ips[i].afi, rsc->ips[i].min,
462 rsc->ips[i].max, cert->ips, cert->ipsz) > 0)
463 continue;
464
465 ip_warn(fn, "RSC ResourceBlock uncovered", &rsc->ips[i]);
466 return 0;
467 }
468
469 return 1;
470 }
471
472 int
valid_econtent_version(const char * fn,const ASN1_INTEGER * aint,uint64_t expected)473 valid_econtent_version(const char *fn, const ASN1_INTEGER *aint,
474 uint64_t expected)
475 {
476 uint64_t version;
477
478 if (aint == NULL) {
479 if (expected == 0)
480 return 1;
481 warnx("%s: unexpected version 0", fn);
482 return 0;
483 }
484
485 if (!ASN1_INTEGER_get_uint64(&version, aint)) {
486 warnx("%s: ASN1_INTEGER_get_uint64 failed", fn);
487 return 0;
488 }
489
490 if (version == 0) {
491 warnx("%s: incorrect encoding for version 0", fn);
492 return 0;
493 }
494
495 if (version != expected) {
496 warnx("%s: unexpected version (expected %llu, got %llu)", fn,
497 (unsigned long long)expected, (unsigned long long)version);
498 return 0;
499 }
500
501 return 1;
502 }
503
504 /*
505 * Validate the ASPA: check that the customerASID is contained.
506 * Returns 1 if valid, 0 otherwise.
507 */
508 int
valid_aspa(const char * fn,struct cert * cert,struct aspa * aspa)509 valid_aspa(const char *fn, struct cert *cert, struct aspa *aspa)
510 {
511
512 if (as_check_covered(aspa->custasid, aspa->custasid,
513 cert->as, cert->asz) > 0)
514 return 1;
515
516 warnx("%s: ASPA: uncovered Customer ASID: %u", fn, aspa->custasid);
517
518 return 0;
519 }
520
521 /*
522 * Validate Geofeed prefixes: check that the prefixes are contained.
523 * Returns 1 if valid, 0 otherwise.
524 */
525 int
valid_geofeed(const char * fn,struct cert * cert,struct geofeed * g)526 valid_geofeed(const char *fn, struct cert *cert, struct geofeed *g)
527 {
528 size_t i;
529 char buf[64];
530
531 for (i = 0; i < g->geoipsz; i++) {
532 if (ip_addr_check_covered(g->geoips[i].ip->afi,
533 g->geoips[i].ip->min, g->geoips[i].ip->max, cert->ips,
534 cert->ipsz) > 0)
535 continue;
536
537 ip_addr_print(&g->geoips[i].ip->ip, g->geoips[i].ip->afi, buf,
538 sizeof(buf));
539 warnx("%s: Geofeed: uncovered IP: %s", fn, buf);
540 return 0;
541 }
542
543 return 1;
544 }
545
546 /*
547 * Validate whether a given string is a valid UUID.
548 * Returns 1 if valid, 0 otherwise.
549 */
550 int
valid_uuid(const char * s)551 valid_uuid(const char *s)
552 {
553 int n = 0;
554
555 while (1) {
556 switch (n) {
557 case 8:
558 case 13:
559 case 18:
560 case 23:
561 if (s[n] != '-')
562 return 0;
563 break;
564 /* Check UUID is version 4 */
565 case 14:
566 if (s[n] != '4')
567 return 0;
568 break;
569 /* Check UUID variant is 1 */
570 case 19:
571 if (s[n] != '8' && s[n] != '9' && s[n] != 'a' &&
572 s[n] != 'A' && s[n] != 'b' && s[n] != 'B')
573 return 0;
574 break;
575 case 36:
576 return s[n] == '\0';
577 default:
578 if (!isxdigit((unsigned char)s[n]))
579 return 0;
580 break;
581 }
582 n++;
583 }
584 }
585
586 static int
valid_ca_pkey_rsa(const char * fn,EVP_PKEY * pkey)587 valid_ca_pkey_rsa(const char *fn, EVP_PKEY *pkey)
588 {
589 RSA *rsa;
590 const BIGNUM *rsa_e;
591 int key_bits;
592
593 if ((key_bits = EVP_PKEY_bits(pkey)) != 2048) {
594 warnx("%s: RFC 7935: expected 2048-bit modulus, got %d bits",
595 fn, key_bits);
596 return 0;
597 }
598
599 if ((rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
600 warnx("%s: failed to extract RSA public key", fn);
601 return 0;
602 }
603
604 if ((rsa_e = RSA_get0_e(rsa)) == NULL) {
605 warnx("%s: failed to get RSA exponent", fn);
606 return 0;
607 }
608
609 if (!BN_is_word(rsa_e, 65537)) {
610 warnx("%s: incorrect exponent (e) in RSA public key", fn);
611 return 0;
612 }
613
614 return 1;
615 }
616
617 static int
valid_ca_pkey_ec(const char * fn,EVP_PKEY * pkey)618 valid_ca_pkey_ec(const char *fn, EVP_PKEY *pkey)
619 {
620 EC_KEY *ec;
621 const EC_GROUP *group;
622 int nid;
623 const char *cname;
624
625 if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
626 warnx("%s: failed to extract ECDSA public key", fn);
627 return 0;
628 }
629
630 if ((group = EC_KEY_get0_group(ec)) == NULL) {
631 warnx("%s: EC_KEY_get0_group failed", fn);
632 return 0;
633 }
634
635 nid = EC_GROUP_get_curve_name(group);
636 if (nid != NID_X9_62_prime256v1) {
637 if ((cname = EC_curve_nid2nist(nid)) == NULL)
638 cname = nid2str(nid);
639 warnx("%s: Expected P-256, got %s", fn, cname);
640 return 0;
641 }
642
643 if (!EC_KEY_check_key(ec)) {
644 warnx("%s: EC_KEY_check_key failed", fn);
645 return 0;
646 }
647
648 return 1;
649 }
650
651 int
valid_ca_pkey(const char * fn,EVP_PKEY * pkey)652 valid_ca_pkey(const char *fn, EVP_PKEY *pkey)
653 {
654 if (pkey == NULL) {
655 warnx("%s: failure, pkey is NULL", fn);
656 return 0;
657 }
658
659 if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA)
660 return valid_ca_pkey_rsa(fn, pkey);
661
662 if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC)
663 return valid_ca_pkey_ec(fn, pkey);
664
665 warnx("%s: unsupported public key algorithm", fn);
666 return 0;
667 }
668