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