1 /* $OpenBSD: x509.c,v 1.87 2024/04/21 09:03:22 job Exp $ */
2 /*
3 * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
5 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <err.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include <openssl/evp.h>
26 #include <openssl/x509v3.h>
27
28 #include "extern.h"
29
30 ASN1_OBJECT *certpol_oid; /* id-cp-ipAddr-asNumber cert policy */
31 ASN1_OBJECT *carepo_oid; /* 1.3.6.1.5.5.7.48.5 (caRepository) */
32 ASN1_OBJECT *manifest_oid; /* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
33 ASN1_OBJECT *signedobj_oid; /* 1.3.6.1.5.5.7.48.11 (signedObject) */
34 ASN1_OBJECT *notify_oid; /* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */
35 ASN1_OBJECT *roa_oid; /* id-ct-routeOriginAuthz CMS content type */
36 ASN1_OBJECT *mft_oid; /* id-ct-rpkiManifest CMS content type */
37 ASN1_OBJECT *gbr_oid; /* id-ct-rpkiGhostbusters CMS content type */
38 ASN1_OBJECT *bgpsec_oid; /* id-kp-bgpsec-router Key Purpose */
39 ASN1_OBJECT *cnt_type_oid; /* pkcs-9 id-contentType */
40 ASN1_OBJECT *msg_dgst_oid; /* pkcs-9 id-messageDigest */
41 ASN1_OBJECT *sign_time_oid; /* pkcs-9 id-signingTime */
42 ASN1_OBJECT *rsc_oid; /* id-ct-signedChecklist */
43 ASN1_OBJECT *aspa_oid; /* id-ct-ASPA */
44 ASN1_OBJECT *tak_oid; /* id-ct-SignedTAL */
45 ASN1_OBJECT *geofeed_oid; /* id-ct-geofeedCSVwithCRLF */
46 ASN1_OBJECT *spl_oid; /* id-ct-signedPrefixList */
47
48 static const struct {
49 const char *oid;
50 ASN1_OBJECT **ptr;
51 } oid_table[] = {
52 {
53 .oid = "1.3.6.1.5.5.7.14.2",
54 .ptr = &certpol_oid,
55 },
56 {
57 .oid = "1.3.6.1.5.5.7.48.5",
58 .ptr = &carepo_oid,
59 },
60 {
61 .oid = "1.3.6.1.5.5.7.48.10",
62 .ptr = &manifest_oid,
63 },
64 {
65 .oid = "1.3.6.1.5.5.7.48.11",
66 .ptr = &signedobj_oid,
67 },
68 {
69 .oid = "1.3.6.1.5.5.7.48.13",
70 .ptr = ¬ify_oid,
71 },
72 {
73 .oid = "1.2.840.113549.1.9.16.1.24",
74 .ptr = &roa_oid,
75 },
76 {
77 .oid = "1.2.840.113549.1.9.16.1.26",
78 .ptr = &mft_oid,
79 },
80 {
81 .oid = "1.2.840.113549.1.9.16.1.35",
82 .ptr = &gbr_oid,
83 },
84 {
85 .oid = "1.3.6.1.5.5.7.3.30",
86 .ptr = &bgpsec_oid,
87 },
88 {
89 .oid = "1.2.840.113549.1.9.3",
90 .ptr = &cnt_type_oid,
91 },
92 {
93 .oid = "1.2.840.113549.1.9.4",
94 .ptr = &msg_dgst_oid,
95 },
96 {
97 .oid = "1.2.840.113549.1.9.5",
98 .ptr = &sign_time_oid,
99 },
100 {
101 .oid = "1.2.840.113549.1.9.16.1.47",
102 .ptr = &geofeed_oid,
103 },
104 {
105 .oid = "1.2.840.113549.1.9.16.1.48",
106 .ptr = &rsc_oid,
107 },
108 {
109 .oid = "1.2.840.113549.1.9.16.1.49",
110 .ptr = &aspa_oid,
111 },
112 {
113 .oid = "1.2.840.113549.1.9.16.1.50",
114 .ptr = &tak_oid,
115 },
116 {
117 .oid = "1.2.840.113549.1.9.16.1.51",
118 .ptr = &spl_oid,
119 },
120 };
121
122 void
x509_init_oid(void)123 x509_init_oid(void)
124 {
125 size_t i;
126
127 for (i = 0; i < sizeof(oid_table) / sizeof(oid_table[0]); i++) {
128 *oid_table[i].ptr = OBJ_txt2obj(oid_table[i].oid, 1);
129 if (*oid_table[i].ptr == NULL)
130 errx(1, "OBJ_txt2obj for %s failed", oid_table[i].oid);
131 }
132 }
133
134 /*
135 * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3.
136 * Returns the AKI or NULL if it could not be parsed.
137 * The AKI is formatted as a hex string.
138 */
139 int
x509_get_aki(X509 * x,const char * fn,char ** aki)140 x509_get_aki(X509 *x, const char *fn, char **aki)
141 {
142 const unsigned char *d;
143 AUTHORITY_KEYID *akid;
144 ASN1_OCTET_STRING *os;
145 int dsz, crit, rc = 0;
146
147 *aki = NULL;
148 akid = X509_get_ext_d2i(x, NID_authority_key_identifier, &crit, NULL);
149 if (akid == NULL) {
150 if (crit != -1) {
151 warnx("%s: RFC 6487 section 4.8.3: error parsing AKI",
152 fn);
153 return 0;
154 }
155 return 1;
156 }
157 if (crit != 0) {
158 warnx("%s: RFC 6487 section 4.8.3: "
159 "AKI: extension not non-critical", fn);
160 goto out;
161 }
162 if (akid->issuer != NULL || akid->serial != NULL) {
163 warnx("%s: RFC 6487 section 4.8.3: AKI: "
164 "authorityCertIssuer or authorityCertSerialNumber present",
165 fn);
166 goto out;
167 }
168
169 os = akid->keyid;
170 if (os == NULL) {
171 warnx("%s: RFC 6487 section 4.8.3: AKI: "
172 "Key Identifier missing", fn);
173 goto out;
174 }
175
176 d = os->data;
177 dsz = os->length;
178
179 if (dsz != SHA_DIGEST_LENGTH) {
180 warnx("%s: RFC 6487 section 4.8.2: AKI: "
181 "want %d bytes SHA1 hash, have %d bytes",
182 fn, SHA_DIGEST_LENGTH, dsz);
183 goto out;
184 }
185
186 *aki = hex_encode(d, dsz);
187 rc = 1;
188 out:
189 AUTHORITY_KEYID_free(akid);
190 return rc;
191 }
192
193 /*
194 * Validate the X509v3 subject key identifier (SKI), RFC 6487 section 4.8.2:
195 * "The SKI is a SHA-1 hash of the value of the DER-encoded ASN.1 BIT STRING of
196 * the Subject Public Key, as described in Section 4.2.1.2 of RFC 5280."
197 * Returns the SKI formatted as hex string, or NULL if it couldn't be parsed.
198 */
199 int
x509_get_ski(X509 * x,const char * fn,char ** ski)200 x509_get_ski(X509 *x, const char *fn, char **ski)
201 {
202 ASN1_OCTET_STRING *os;
203 unsigned char md[EVP_MAX_MD_SIZE];
204 unsigned int md_len = EVP_MAX_MD_SIZE;
205 int crit, rc = 0;
206
207 *ski = NULL;
208 os = X509_get_ext_d2i(x, NID_subject_key_identifier, &crit, NULL);
209 if (os == NULL) {
210 if (crit != -1) {
211 warnx("%s: RFC 6487 section 4.8.2: error parsing SKI",
212 fn);
213 return 0;
214 }
215 return 1;
216 }
217 if (crit != 0) {
218 warnx("%s: RFC 6487 section 4.8.2: "
219 "SKI: extension not non-critical", fn);
220 goto out;
221 }
222
223 if (!X509_pubkey_digest(x, EVP_sha1(), md, &md_len)) {
224 warnx("%s: X509_pubkey_digest", fn);
225 goto out;
226 }
227
228 if (os->length < 0 || md_len != (size_t)os->length) {
229 warnx("%s: RFC 6487 section 4.8.2: SKI: "
230 "want %u bytes SHA1 hash, have %d bytes",
231 fn, md_len, os->length);
232 goto out;
233 }
234
235 if (memcmp(os->data, md, md_len) != 0) {
236 warnx("%s: SKI does not match SHA1 hash of SPK", fn);
237 goto out;
238 }
239
240 *ski = hex_encode(md, md_len);
241 rc = 1;
242 out:
243 ASN1_OCTET_STRING_free(os);
244 return rc;
245 }
246
247 /*
248 * Check the certificate's purpose: CA or BGPsec Router.
249 * Return a member of enum cert_purpose.
250 */
251 enum cert_purpose
x509_get_purpose(X509 * x,const char * fn)252 x509_get_purpose(X509 *x, const char *fn)
253 {
254 BASIC_CONSTRAINTS *bc = NULL;
255 EXTENDED_KEY_USAGE *eku = NULL;
256 int crit;
257 enum cert_purpose purpose = CERT_PURPOSE_INVALID;
258
259 if (X509_check_ca(x) == 1) {
260 bc = X509_get_ext_d2i(x, NID_basic_constraints, &crit, NULL);
261 if (bc == NULL) {
262 if (crit != -1)
263 warnx("%s: RFC 6487 section 4.8.1: "
264 "error parsing basic constraints", fn);
265 else
266 warnx("%s: RFC 6487 section 4.8.1: "
267 "missing basic constraints", fn);
268 goto out;
269 }
270 if (crit != 1) {
271 warnx("%s: RFC 6487 section 4.8.1: Basic Constraints "
272 "must be marked critical", fn);
273 goto out;
274 }
275 if (bc->pathlen != NULL) {
276 warnx("%s: RFC 6487 section 4.8.1: Path Length "
277 "Constraint must be absent", fn);
278 goto out;
279 }
280 purpose = CERT_PURPOSE_CA;
281 goto out;
282 }
283
284 if (X509_get_extension_flags(x) & EXFLAG_BCONS) {
285 warnx("%s: Basic Constraints ext in non-CA cert", fn);
286 goto out;
287 }
288
289 eku = X509_get_ext_d2i(x, NID_ext_key_usage, &crit, NULL);
290 if (eku == NULL) {
291 if (crit != -1)
292 warnx("%s: error parsing EKU", fn);
293 else
294 warnx("%s: EKU: extension missing", fn);
295 goto out;
296 }
297 if (crit != 0) {
298 warnx("%s: EKU: extension must not be marked critical", fn);
299 goto out;
300 }
301 if (sk_ASN1_OBJECT_num(eku) != 1) {
302 warnx("%s: EKU: expected 1 purpose, have %d", fn,
303 sk_ASN1_OBJECT_num(eku));
304 goto out;
305 }
306
307 if (OBJ_cmp(bgpsec_oid, sk_ASN1_OBJECT_value(eku, 0)) == 0) {
308 purpose = CERT_PURPOSE_BGPSEC_ROUTER;
309 goto out;
310 }
311
312 out:
313 BASIC_CONSTRAINTS_free(bc);
314 EXTENDED_KEY_USAGE_free(eku);
315 return purpose;
316 }
317
318 /*
319 * Extract Subject Public Key Info (SPKI) from BGPsec X.509 Certificate.
320 * Returns NULL on failure, on success return the SPKI as base64 encoded pubkey
321 */
322 char *
x509_get_pubkey(X509 * x,const char * fn)323 x509_get_pubkey(X509 *x, const char *fn)
324 {
325 EVP_PKEY *pkey;
326 EC_KEY *eckey;
327 int nid;
328 const char *cname;
329 uint8_t *pubkey = NULL;
330 char *res = NULL;
331 int len;
332
333 pkey = X509_get0_pubkey(x);
334 if (pkey == NULL) {
335 warnx("%s: X509_get0_pubkey failed in %s", fn, __func__);
336 goto out;
337 }
338 if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
339 warnx("%s: Expected EVP_PKEY_EC, got %d", fn,
340 EVP_PKEY_base_id(pkey));
341 goto out;
342 }
343
344 eckey = EVP_PKEY_get0_EC_KEY(pkey);
345 if (eckey == NULL) {
346 warnx("%s: Incorrect key type", fn);
347 goto out;
348 }
349
350 nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey));
351 if (nid != NID_X9_62_prime256v1) {
352 if ((cname = EC_curve_nid2nist(nid)) == NULL)
353 cname = nid2str(nid);
354 warnx("%s: Expected P-256, got %s", fn, cname);
355 goto out;
356 }
357
358 if (!EC_KEY_check_key(eckey)) {
359 warnx("%s: EC_KEY_check_key failed in %s", fn, __func__);
360 goto out;
361 }
362
363 len = i2d_PUBKEY(pkey, &pubkey);
364 if (len <= 0) {
365 warnx("%s: i2d_PUBKEY failed in %s", fn, __func__);
366 goto out;
367 }
368
369 if (base64_encode(pubkey, len, &res) == -1)
370 errx(1, "base64_encode failed in %s", __func__);
371
372 out:
373 free(pubkey);
374 return res;
375 }
376
377 /*
378 * Compute the SKI of an RSA public key in an X509_PUBKEY using SHA-1.
379 * Returns allocated hex-encoded SKI on success, NULL on failure.
380 */
381 char *
x509_pubkey_get_ski(X509_PUBKEY * pubkey,const char * fn)382 x509_pubkey_get_ski(X509_PUBKEY *pubkey, const char *fn)
383 {
384 ASN1_OBJECT *obj;
385 const unsigned char *der;
386 int der_len, nid;
387 unsigned char md[EVP_MAX_MD_SIZE];
388 unsigned int md_len = EVP_MAX_MD_SIZE;
389
390 if (!X509_PUBKEY_get0_param(&obj, &der, &der_len, NULL, pubkey)) {
391 warnx("%s: X509_PUBKEY_get0_param failed", fn);
392 return NULL;
393 }
394
395 if ((nid = OBJ_obj2nid(obj)) != NID_rsaEncryption) {
396 warnx("%s: RFC 7935: wrong signature algorithm %s, want %s",
397 fn, nid2str(nid), LN_rsaEncryption);
398 return NULL;
399 }
400
401 if (!EVP_Digest(der, der_len, md, &md_len, EVP_sha1(), NULL)) {
402 warnx("%s: EVP_Digest failed", fn);
403 return NULL;
404 }
405
406 return hex_encode(md, md_len);
407 }
408
409 /*
410 * Parse the Authority Information Access (AIA) extension
411 * See RFC 6487, section 4.8.7 for details.
412 * Returns NULL on failure, on success returns the AIA URI
413 * (which has to be freed after use).
414 */
415 int
x509_get_aia(X509 * x,const char * fn,char ** aia)416 x509_get_aia(X509 *x, const char *fn, char **aia)
417 {
418 ACCESS_DESCRIPTION *ad;
419 AUTHORITY_INFO_ACCESS *info;
420 int crit, rc = 0;
421
422 *aia = NULL;
423 info = X509_get_ext_d2i(x, NID_info_access, &crit, NULL);
424 if (info == NULL) {
425 if (crit != -1) {
426 warnx("%s: RFC 6487 section 4.8.7: error parsing AIA",
427 fn);
428 return 0;
429 }
430 return 1;
431 }
432
433 if (crit != 0) {
434 warnx("%s: RFC 6487 section 4.8.7: "
435 "AIA: extension not non-critical", fn);
436 goto out;
437 }
438
439 if ((X509_get_extension_flags(x) & EXFLAG_SS) != 0) {
440 warnx("%s: RFC 6487 section 4.8.7: AIA must be absent from "
441 "a self-signed certificate", fn);
442 goto out;
443 }
444
445 if (sk_ACCESS_DESCRIPTION_num(info) != 1) {
446 warnx("%s: RFC 6487 section 4.8.7: AIA: "
447 "want 1 element, have %d", fn,
448 sk_ACCESS_DESCRIPTION_num(info));
449 goto out;
450 }
451
452 ad = sk_ACCESS_DESCRIPTION_value(info, 0);
453 if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers) {
454 warnx("%s: RFC 6487 section 4.8.7: AIA: "
455 "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method));
456 goto out;
457 }
458
459 if (!x509_location(fn, "AIA: caIssuers", NULL, ad->location, aia))
460 goto out;
461
462 rc = 1;
463
464 out:
465 AUTHORITY_INFO_ACCESS_free(info);
466 return rc;
467 }
468
469 /*
470 * Parse the Subject Information Access (SIA) extension
471 * See RFC 6487, section 4.8.8 for details.
472 * Returns NULL on failure, on success returns the SIA signedObject URI
473 * (which has to be freed after use).
474 */
475 int
x509_get_sia(X509 * x,const char * fn,char ** sia)476 x509_get_sia(X509 *x, const char *fn, char **sia)
477 {
478 ACCESS_DESCRIPTION *ad;
479 AUTHORITY_INFO_ACCESS *info;
480 ASN1_OBJECT *oid;
481 int i, crit, rsync_found = 0;
482
483 *sia = NULL;
484
485 info = X509_get_ext_d2i(x, NID_sinfo_access, &crit, NULL);
486 if (info == NULL) {
487 if (crit != -1) {
488 warnx("%s: error parsing SIA", fn);
489 return 0;
490 }
491 return 1;
492 }
493
494 if (crit != 0) {
495 warnx("%s: RFC 6487 section 4.8.8: "
496 "SIA: extension not non-critical", fn);
497 goto out;
498 }
499
500 for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) {
501 ad = sk_ACCESS_DESCRIPTION_value(info, i);
502 oid = ad->method;
503
504 /*
505 * XXX: RFC 6487 4.8.8.2 states that the accessMethod MUST be
506 * signedObject. However, rpkiNotify accessMethods currently
507 * exist in the wild. Consider removing this special case.
508 * See also https://www.rfc-editor.org/errata/eid7239.
509 */
510 if (OBJ_cmp(oid, notify_oid) == 0) {
511 if (verbose > 1)
512 warnx("%s: RFC 6487 section 4.8.8.2: SIA should"
513 " not contain rpkiNotify accessMethod", fn);
514 continue;
515 }
516 if (OBJ_cmp(oid, signedobj_oid) != 0) {
517 char buf[128];
518
519 OBJ_obj2txt(buf, sizeof(buf), oid, 0);
520 warnx("%s: RFC 6487 section 4.8.8.2: unexpected"
521 " accessMethod: %s", fn, buf);
522 goto out;
523 }
524
525 /* Don't fail on non-rsync URI, so check this afterward. */
526 if (!x509_location(fn, "SIA: signedObject", NULL, ad->location,
527 sia))
528 goto out;
529
530 if (rsync_found)
531 continue;
532
533 if (strncasecmp(*sia, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
534 const char *p = *sia + RSYNC_PROTO_LEN;
535 size_t fnlen, plen;
536
537 rsync_found = 1;
538
539 if (filemode)
540 continue;
541
542 fnlen = strlen(fn);
543 plen = strlen(p);
544
545 if (fnlen < plen || strcmp(p, fn + fnlen - plen) != 0) {
546 warnx("%s: mismatch between pathname and SIA "
547 "(%s)", fn, *sia);
548 goto out;
549 }
550
551 continue;
552 }
553
554 free(*sia);
555 *sia = NULL;
556 }
557
558 if (!rsync_found) {
559 warnx("%s: RFC 6487 section 4.8.8.2: "
560 "SIA without rsync accessLocation", fn);
561 goto out;
562 }
563
564 AUTHORITY_INFO_ACCESS_free(info);
565 return 1;
566
567 out:
568 free(*sia);
569 *sia = NULL;
570 AUTHORITY_INFO_ACCESS_free(info);
571 return 0;
572 }
573
574 /*
575 * Extract the notBefore of a certificate.
576 */
577 int
x509_get_notbefore(X509 * x,const char * fn,time_t * tt)578 x509_get_notbefore(X509 *x, const char *fn, time_t *tt)
579 {
580 const ASN1_TIME *at;
581
582 at = X509_get0_notBefore(x);
583 if (at == NULL) {
584 warnx("%s: X509_get0_notBefore failed", fn);
585 return 0;
586 }
587 if (!x509_get_time(at, tt)) {
588 warnx("%s: ASN1_TIME_to_tm failed", fn);
589 return 0;
590 }
591 return 1;
592 }
593
594 /*
595 * Extract the notAfter from a certificate.
596 */
597 int
x509_get_notafter(X509 * x,const char * fn,time_t * tt)598 x509_get_notafter(X509 *x, const char *fn, time_t *tt)
599 {
600 const ASN1_TIME *at;
601
602 at = X509_get0_notAfter(x);
603 if (at == NULL) {
604 warnx("%s: X509_get0_notafter failed", fn);
605 return 0;
606 }
607 if (!x509_get_time(at, tt)) {
608 warnx("%s: ASN1_TIME_to_tm failed", fn);
609 return 0;
610 }
611 return 1;
612 }
613
614 /*
615 * Check whether all RFC 3779 extensions are set to inherit.
616 * Return 1 if both AS & IP are set to inherit.
617 * Return 0 on failure (such as missing extensions or no inheritance).
618 */
619 int
x509_inherits(X509 * x)620 x509_inherits(X509 *x)
621 {
622 STACK_OF(IPAddressFamily) *addrblk = NULL;
623 ASIdentifiers *asidentifiers = NULL;
624 const IPAddressFamily *af;
625 int crit, i, rc = 0;
626
627 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL);
628 if (addrblk == NULL) {
629 if (crit != -1)
630 warnx("error parsing ipAddrBlock");
631 goto out;
632 }
633
634 /*
635 * Check by hand, since X509v3_addr_inherits() success only means that
636 * at least one address family inherits, not all of them.
637 */
638 for (i = 0; i < sk_IPAddressFamily_num(addrblk); i++) {
639 af = sk_IPAddressFamily_value(addrblk, i);
640 if (af->ipAddressChoice->type != IPAddressChoice_inherit)
641 goto out;
642 }
643
644 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, NULL,
645 NULL);
646 if (asidentifiers == NULL) {
647 if (crit != -1)
648 warnx("error parsing asIdentifiers");
649 goto out;
650 }
651
652 /* We need to have AS numbers and don't want RDIs. */
653 if (asidentifiers->asnum == NULL || asidentifiers->rdi != NULL)
654 goto out;
655 if (!X509v3_asid_inherits(asidentifiers))
656 goto out;
657
658 rc = 1;
659 out:
660 ASIdentifiers_free(asidentifiers);
661 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free);
662 return rc;
663 }
664
665 /*
666 * Check whether at least one RFC 3779 extension is set to inherit.
667 * Return 1 if an inherit element is encountered in AS or IP.
668 * Return 0 otherwise.
669 */
670 int
x509_any_inherits(X509 * x)671 x509_any_inherits(X509 *x)
672 {
673 STACK_OF(IPAddressFamily) *addrblk = NULL;
674 ASIdentifiers *asidentifiers = NULL;
675 int crit, rc = 0;
676
677 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL);
678 if (addrblk == NULL && crit != -1)
679 warnx("error parsing ipAddrBlock");
680 if (X509v3_addr_inherits(addrblk))
681 rc = 1;
682
683 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, &crit,
684 NULL);
685 if (asidentifiers == NULL && crit != -1)
686 warnx("error parsing asIdentifiers");
687 if (X509v3_asid_inherits(asidentifiers))
688 rc = 1;
689
690 ASIdentifiers_free(asidentifiers);
691 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free);
692 return rc;
693 }
694
695 /*
696 * Parse the very specific subset of information in the CRL distribution
697 * point extension.
698 * See RFC 6487, section 4.8.6 for details.
699 * Returns NULL on failure, the crl URI on success which has to be freed
700 * after use.
701 */
702 int
x509_get_crl(X509 * x,const char * fn,char ** crl)703 x509_get_crl(X509 *x, const char *fn, char **crl)
704 {
705 CRL_DIST_POINTS *crldp;
706 DIST_POINT *dp;
707 GENERAL_NAMES *names;
708 GENERAL_NAME *name;
709 int i, crit, rsync_found = 0;
710
711 *crl = NULL;
712 crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &crit, NULL);
713 if (crldp == NULL) {
714 if (crit != -1) {
715 warnx("%s: RFC 6487 section 4.8.6: failed to parse "
716 "CRL distribution points", fn);
717 return 0;
718 }
719 return 1;
720 }
721
722 if (crit != 0) {
723 warnx("%s: RFC 6487 section 4.8.6: "
724 "CRL distribution point: extension not non-critical", fn);
725 goto out;
726 }
727
728 if (sk_DIST_POINT_num(crldp) != 1) {
729 warnx("%s: RFC 6487 section 4.8.6: CRL: "
730 "want 1 element, have %d", fn,
731 sk_DIST_POINT_num(crldp));
732 goto out;
733 }
734
735 dp = sk_DIST_POINT_value(crldp, 0);
736 if (dp->CRLissuer != NULL) {
737 warnx("%s: RFC 6487 section 4.8.6: CRL CRLIssuer field"
738 " disallowed", fn);
739 goto out;
740 }
741 if (dp->reasons != NULL) {
742 warnx("%s: RFC 6487 section 4.8.6: CRL Reasons field"
743 " disallowed", fn);
744 goto out;
745 }
746 if (dp->distpoint == NULL) {
747 warnx("%s: RFC 6487 section 4.8.6: CRL: "
748 "no distribution point name", fn);
749 goto out;
750 }
751 if (dp->distpoint->dpname != NULL) {
752 warnx("%s: RFC 6487 section 4.8.6: nameRelativeToCRLIssuer"
753 " disallowed", fn);
754 goto out;
755 }
756 /* Need to hardcode the alternative 0 due to missing macros or enum. */
757 if (dp->distpoint->type != 0) {
758 warnx("%s: RFC 6487 section 4.8.6: CRL DistributionPointName:"
759 " expected fullName, have %d", fn, dp->distpoint->type);
760 goto out;
761 }
762
763 names = dp->distpoint->name.fullname;
764 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
765 name = sk_GENERAL_NAME_value(names, i);
766
767 /* Don't fail on non-rsync URI, so check this afterward. */
768 if (!x509_location(fn, "CRL distribution point", NULL, name,
769 crl))
770 goto out;
771
772 if (strncasecmp(*crl, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
773 rsync_found = 1;
774 goto out;
775 }
776
777 free(*crl);
778 *crl = NULL;
779 }
780
781 warnx("%s: RFC 6487 section 4.8.6: no rsync URI "
782 "in CRL distributionPoint", fn);
783
784 out:
785 CRL_DIST_POINTS_free(crldp);
786 return rsync_found;
787 }
788
789 /*
790 * Parse X509v3 authority key identifier (AKI) from the CRL.
791 * This is matched against the string from x509_get_ski() above.
792 * Returns the AKI or NULL if it could not be parsed.
793 * The AKI is formatted as a hex string.
794 */
795 char *
x509_crl_get_aki(X509_CRL * crl,const char * fn)796 x509_crl_get_aki(X509_CRL *crl, const char *fn)
797 {
798 const unsigned char *d;
799 AUTHORITY_KEYID *akid;
800 ASN1_OCTET_STRING *os;
801 int dsz, crit;
802 char *res = NULL;
803
804 akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, &crit,
805 NULL);
806 if (akid == NULL) {
807 warnx("%s: RFC 6487 section 4.8.3: AKI: extension missing", fn);
808 return NULL;
809 }
810 if (crit != 0) {
811 warnx("%s: RFC 6487 section 4.8.3: "
812 "AKI: extension not non-critical", fn);
813 goto out;
814 }
815 if (akid->issuer != NULL || akid->serial != NULL) {
816 warnx("%s: RFC 6487 section 4.8.3: AKI: "
817 "authorityCertIssuer or authorityCertSerialNumber present",
818 fn);
819 goto out;
820 }
821
822 os = akid->keyid;
823 if (os == NULL) {
824 warnx("%s: RFC 6487 section 4.8.3: AKI: "
825 "Key Identifier missing", fn);
826 goto out;
827 }
828
829 d = os->data;
830 dsz = os->length;
831
832 if (dsz != SHA_DIGEST_LENGTH) {
833 warnx("%s: RFC 6487 section 4.8.2: AKI: "
834 "want %d bytes SHA1 hash, have %d bytes",
835 fn, SHA_DIGEST_LENGTH, dsz);
836 goto out;
837 }
838
839 res = hex_encode(d, dsz);
840 out:
841 AUTHORITY_KEYID_free(akid);
842 return res;
843 }
844
845 /*
846 * Retrieve CRL Number extension. Returns a printable hexadecimal representation
847 * of the number which has to be freed after use.
848 */
849 char *
x509_crl_get_number(X509_CRL * crl,const char * fn)850 x509_crl_get_number(X509_CRL *crl, const char *fn)
851 {
852 ASN1_INTEGER *aint;
853 int crit;
854 char *res = NULL;
855
856 aint = X509_CRL_get_ext_d2i(crl, NID_crl_number, &crit, NULL);
857 if (aint == NULL) {
858 warnx("%s: RFC 6487 section 5: CRL Number missing", fn);
859 return NULL;
860 }
861 if (crit != 0) {
862 warnx("%s: RFC 5280, section 5.2.3: "
863 "CRL Number not non-critical", fn);
864 goto out;
865 }
866
867 /* This checks that the number is non-negative and <= 20 bytes. */
868 res = x509_convert_seqnum(fn, aint);
869
870 out:
871 ASN1_INTEGER_free(aint);
872 return res;
873 }
874
875 /*
876 * Convert passed ASN1_TIME to time_t *t.
877 * Returns 1 on success and 0 on failure.
878 */
879 int
x509_get_time(const ASN1_TIME * at,time_t * t)880 x509_get_time(const ASN1_TIME *at, time_t *t)
881 {
882 struct tm tm;
883
884 *t = 0;
885 memset(&tm, 0, sizeof(tm));
886 /* Fail instead of silently falling back to the current time. */
887 if (at == NULL)
888 return 0;
889 if (!ASN1_TIME_to_tm(at, &tm))
890 return 0;
891 if ((*t = timegm(&tm)) == -1)
892 errx(1, "timegm failed");
893 return 1;
894 }
895
896 /*
897 * Extract and validate an accessLocation, RFC 6487, 4.8 and RFC 8182, 3.2.
898 * Returns 0 on failure and 1 on success.
899 */
900 int
x509_location(const char * fn,const char * descr,const char * proto,GENERAL_NAME * location,char ** out)901 x509_location(const char *fn, const char *descr, const char *proto,
902 GENERAL_NAME *location, char **out)
903 {
904 ASN1_IA5STRING *uri;
905
906 if (location->type != GEN_URI) {
907 warnx("%s: RFC 6487 section 4.8: %s not URI", fn, descr);
908 return 0;
909 }
910
911 uri = location->d.uniformResourceIdentifier;
912
913 if (!valid_uri(uri->data, uri->length, proto)) {
914 warnx("%s: RFC 6487 section 4.8: %s bad location", fn, descr);
915 return 0;
916 }
917
918 if (*out != NULL) {
919 warnx("%s: RFC 6487 section 4.8: multiple %s specified, "
920 "using the first one", fn, descr);
921 return 1;
922 }
923
924 if ((*out = strndup(uri->data, uri->length)) == NULL)
925 err(1, NULL);
926
927 return 1;
928 }
929
930 /*
931 * Check that the subject only contains commonName and serialNumber.
932 * Return 0 on failure.
933 */
934 int
x509_valid_subject(const char * fn,const X509 * x)935 x509_valid_subject(const char *fn, const X509 *x)
936 {
937 const X509_NAME *xn;
938 const X509_NAME_ENTRY *ne;
939 const ASN1_OBJECT *ao;
940 const ASN1_STRING *as;
941 int cn = 0, sn = 0;
942 int i, nid;
943
944 if ((xn = X509_get_subject_name(x)) == NULL) {
945 warnx("%s: X509_get_subject_name", fn);
946 return 0;
947 }
948
949 for (i = 0; i < X509_NAME_entry_count(xn); i++) {
950 if ((ne = X509_NAME_get_entry(xn, i)) == NULL) {
951 warnx("%s: X509_NAME_get_entry", fn);
952 return 0;
953 }
954 if ((ao = X509_NAME_ENTRY_get_object(ne)) == NULL) {
955 warnx("%s: X509_NAME_ENTRY_get_object", fn);
956 return 0;
957 }
958
959 nid = OBJ_obj2nid(ao);
960 switch (nid) {
961 case NID_commonName:
962 if (cn++ > 0) {
963 warnx("%s: duplicate commonName in subject",
964 fn);
965 return 0;
966 }
967 if ((as = X509_NAME_ENTRY_get_data(ne)) == NULL) {
968 warnx("%s: X509_NAME_ENTRY_get_data failed",
969 fn);
970 return 0;
971 }
972 /*
973 * The following check can be enabled after AFRINIC re-issues CA certs.
974 * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html
975 */
976 #if 0
977 if (ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING) {
978 warnx("%s: RFC 6487 section 4.5: commonName is"
979 " not PrintableString", fn);
980 return 0;
981 }
982 #endif
983 break;
984 case NID_serialNumber:
985 if (sn++ > 0) {
986 warnx("%s: duplicate serialNumber in subject",
987 fn);
988 return 0;
989 }
990 break;
991 case NID_undef:
992 warnx("%s: OBJ_obj2nid failed", fn);
993 return 0;
994 default:
995 warnx("%s: RFC 6487 section 4.5: unexpected attribute"
996 " %s", fn, nid2str(nid));
997 return 0;
998 }
999 }
1000
1001 if (cn == 0) {
1002 warnx("%s: RFC 6487 section 4.5: subject missing commonName",
1003 fn);
1004 return 0;
1005 }
1006
1007 return 1;
1008 }
1009
1010 /*
1011 * Convert an ASN1_INTEGER into a hexstring.
1012 * Returned string needs to be freed by the caller.
1013 */
1014 char *
x509_convert_seqnum(const char * fn,const ASN1_INTEGER * i)1015 x509_convert_seqnum(const char *fn, const ASN1_INTEGER *i)
1016 {
1017 BIGNUM *seqnum = NULL;
1018 char *s = NULL;
1019
1020 if (i == NULL)
1021 goto out;
1022
1023 if (ASN1_STRING_length(i) > 20) {
1024 warnx("%s: %s: want 20 octets or fewer, have more.",
1025 __func__, fn);
1026 goto out;
1027 }
1028
1029 seqnum = ASN1_INTEGER_to_BN(i, NULL);
1030 if (seqnum == NULL) {
1031 warnx("%s: ASN1_INTEGER_to_BN error", fn);
1032 goto out;
1033 }
1034
1035 if (BN_is_negative(seqnum)) {
1036 warnx("%s: %s: want positive integer, have negative.",
1037 __func__, fn);
1038 goto out;
1039 }
1040
1041 s = BN_bn2hex(seqnum);
1042 if (s == NULL)
1043 warnx("%s: BN_bn2hex error", fn);
1044
1045 out:
1046 BN_free(seqnum);
1047 return s;
1048 }
1049
1050 /*
1051 * Find the closest expiry moment by walking the chain of authorities.
1052 */
1053 time_t
x509_find_expires(time_t notafter,struct auth * a,struct crl_tree * crlt)1054 x509_find_expires(time_t notafter, struct auth *a, struct crl_tree *crlt)
1055 {
1056 struct crl *crl;
1057 time_t expires;
1058
1059 expires = notafter;
1060
1061 for (; a != NULL; a = a->issuer) {
1062 if (expires > a->cert->notafter)
1063 expires = a->cert->notafter;
1064 crl = crl_get(crlt, a);
1065 if (crl != NULL && expires > crl->nextupdate)
1066 expires = crl->nextupdate;
1067 }
1068
1069 return expires;
1070 }
1071