1*904d9c60Stb /* $OpenBSD: crl.c,v 1.43 2024/09/12 10:33:25 tb Exp $ */ 29a7e9e7fSjob /* 3c79b8c8bStb * Copyright (c) 2024 Theo Buehler <tb@openbsd.org> 49a7e9e7fSjob * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 59a7e9e7fSjob * 69a7e9e7fSjob * Permission to use, copy, modify, and distribute this software for any 79a7e9e7fSjob * purpose with or without fee is hereby granted, provided that the above 89a7e9e7fSjob * copyright notice and this permission notice appear in all copies. 99a7e9e7fSjob * 109a7e9e7fSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 119a7e9e7fSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 129a7e9e7fSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 139a7e9e7fSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 149a7e9e7fSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 159a7e9e7fSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 169a7e9e7fSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 179a7e9e7fSjob */ 189a7e9e7fSjob 199a7e9e7fSjob #include <err.h> 209a7e9e7fSjob #include <stdlib.h> 219a7e9e7fSjob #include <string.h> 229a7e9e7fSjob #include <unistd.h> 239a7e9e7fSjob 247c9af4b7Sjob #include <openssl/x509.h> 257c9af4b7Sjob 269a7e9e7fSjob #include "extern.h" 279a7e9e7fSjob 2809383accStb /* 29*904d9c60Stb * Check CRL Number is present, non-critical and in [0, 2^159-1]. 3009383accStb * Otherwise ignore it per draft-spaghetti-sidrops-rpki-crl-numbers. 3109383accStb */ 3209383accStb static int 33*904d9c60Stb crl_check_crl_number(const char *fn, const X509_CRL *x509_crl) 3409383accStb { 35*904d9c60Stb ASN1_INTEGER *aint = NULL; 36*904d9c60Stb int crit; 37*904d9c60Stb int ret = 0; 3809383accStb 39*904d9c60Stb aint = X509_CRL_get_ext_d2i(x509_crl, NID_crl_number, &crit, NULL); 40*904d9c60Stb if (aint == NULL) { 41*904d9c60Stb if (crit != -1) 42*904d9c60Stb warnx("%s: RFC 6487, section 5: " 43*904d9c60Stb "failed to parse CRL number", fn); 44*904d9c60Stb else 45*904d9c60Stb warnx("%s: RFC 6487, section 5: missing CRL number", 46*904d9c60Stb fn); 47*904d9c60Stb goto out; 4809383accStb } 49*904d9c60Stb if (crit != 0) { 5009383accStb warnx("%s: RFC 6487, section 5: CRL number not non-critical", 5109383accStb fn); 52*904d9c60Stb goto out; 5309383accStb } 5409383accStb 55*904d9c60Stb ret = x509_valid_seqnum(fn, "CRL number", aint); 56*904d9c60Stb 57*904d9c60Stb out: 58*904d9c60Stb ASN1_INTEGER_free(aint); 59*904d9c60Stb return ret; 6009383accStb } 6109383accStb 6209383accStb /* 6309383accStb * Parse X509v3 authority key identifier (AKI) from the CRL. 6409383accStb * Returns the AKI or NULL if it could not be parsed. 6509383accStb * The AKI is formatted as a hex string. 6609383accStb */ 6709383accStb static char * 6809383accStb crl_get_aki(const char *fn, X509_CRL *x509_crl) 6909383accStb { 7009383accStb AUTHORITY_KEYID *akid = NULL; 7109383accStb ASN1_OCTET_STRING *os; 7209383accStb const unsigned char *d; 7309383accStb int dsz, crit; 7409383accStb char *res = NULL; 7509383accStb 7609383accStb if ((akid = X509_CRL_get_ext_d2i(x509_crl, NID_authority_key_identifier, 7709383accStb &crit, NULL)) == NULL) { 7809383accStb if (crit != -1) 7909383accStb warnx("%s: RFC 6487 section 4.8.3: AKI: " 8009383accStb "failed to parse CRL extension", fn); 8109383accStb else 8209383accStb warnx("%s: RFC 6487 section 4.8.3: AKI: " 8309383accStb "CRL extension missing", fn); 8409383accStb goto out; 8509383accStb } 8609383accStb if (crit != 0) { 8709383accStb warnx("%s: RFC 6487 section 4.8.3: " 8809383accStb "AKI: extension not non-critical", fn); 8909383accStb goto out; 9009383accStb } 9109383accStb if (akid->issuer != NULL || akid->serial != NULL) { 9209383accStb warnx("%s: RFC 6487 section 4.8.3: AKI: " 9309383accStb "authorityCertIssuer or authorityCertSerialNumber present", 9409383accStb fn); 9509383accStb goto out; 9609383accStb } 9709383accStb 9809383accStb os = akid->keyid; 9909383accStb if (os == NULL) { 10009383accStb warnx("%s: RFC 6487 section 4.8.3: AKI: " 10109383accStb "Key Identifier missing", fn); 10209383accStb goto out; 10309383accStb } 10409383accStb 10509383accStb d = os->data; 10609383accStb dsz = os->length; 10709383accStb 10809383accStb if (dsz != SHA_DIGEST_LENGTH) { 10909383accStb warnx("%s: RFC 6487 section 4.8.3: AKI: " 11009383accStb "want %d bytes SHA1 hash, have %d bytes", 11109383accStb fn, SHA_DIGEST_LENGTH, dsz); 11209383accStb goto out; 11309383accStb } 11409383accStb 11509383accStb res = hex_encode(d, dsz); 11609383accStb out: 11709383accStb AUTHORITY_KEYID_free(akid); 11809383accStb return res; 11909383accStb } 12009383accStb 12109383accStb /* 12209383accStb * Check that the list of revoked certificates contains only the specified 12309383accStb * two fields, Serial Number and Revocation Date, and that no extensions are 12409383accStb * present. 12509383accStb */ 12609383accStb static int 12709383accStb crl_check_revoked(const char *fn, X509_CRL *x509_crl) 12809383accStb { 12909383accStb STACK_OF(X509_REVOKED) *list; 13009383accStb X509_REVOKED *revoked; 13109383accStb int count, i; 13209383accStb 13309383accStb /* If there are no revoked certificates, there's nothing to check. */ 13409383accStb if ((list = X509_CRL_get_REVOKED(x509_crl)) == NULL) 13509383accStb return 1; 13609383accStb 13709383accStb if ((count = sk_X509_REVOKED_num(list)) <= 0) { 13809383accStb /* 13909383accStb * XXX - as of May 2024, ~15% of RPKI CRLs fail this check due 14009383accStb * to a bug in rpki-rs/Krill. So silently accept this for now. 14109383accStb * https://github.com/NLnetLabs/krill/issues/1197 142e5e2c7b1Stb * https://github.com/NLnetLabs/rpki-rs/pull/295 14309383accStb */ 144741b0d5fStb if (verbose > 1) 14509383accStb warnx("%s: RFC 5280, section 5.1.2.6: revoked " 14609383accStb "certificate list without entries disallowed", fn); 14709383accStb return 1; 14809383accStb } 14909383accStb 15009383accStb for (i = 0; i < count; i++) { 15109383accStb revoked = sk_X509_REVOKED_value(list, i); 15209383accStb 15309383accStb /* 15409383accStb * serialNumber and revocationDate are mandatory in the ASN.1 15509383accStb * template, so no need to check their presence. 15609383accStb * 15709383accStb * XXX - due to an old bug in Krill, we can't enforce that 15809383accStb * revocationDate is in the past until at least mid-2025: 15909383accStb * https://github.com/NLnetLabs/krill/issues/788. 16009383accStb */ 16109383accStb 16209383accStb if (X509_REVOKED_get0_extensions(revoked) != NULL) { 16309383accStb warnx("%s: RFC 6487, section 5: CRL entry extensions " 16409383accStb "disallowed", fn); 16509383accStb return 0; 16609383accStb } 16709383accStb } 16809383accStb 16909383accStb return 1; 17009383accStb } 17109383accStb 1724bd8ba3aStb struct crl * 173cabf3a3bSclaudio crl_parse(const char *fn, const unsigned char *der, size_t len) 1749a7e9e7fSjob { 175797cceeeStb const unsigned char *oder; 1764bd8ba3aStb struct crl *crl; 1770466b83fStb const X509_NAME *name; 1784bd8ba3aStb const ASN1_TIME *at; 1794b65a085Stb int count, nid, rc = 0; 1809a7e9e7fSjob 181cabf3a3bSclaudio /* just fail for empty buffers, the warning was printed elsewhere */ 182cabf3a3bSclaudio if (der == NULL) 1835ff8d7beSclaudio return NULL; 1845ff8d7beSclaudio 1854bd8ba3aStb if ((crl = calloc(1, sizeof(*crl))) == NULL) 1864bd8ba3aStb err(1, NULL); 1874bd8ba3aStb 188797cceeeStb oder = der; 1894bd8ba3aStb if ((crl->x509_crl = d2i_X509_CRL(NULL, &der, len)) == NULL) { 190c0528901Stb warnx("%s: d2i_X509_CRL", fn); 1919a7e9e7fSjob goto out; 1929a7e9e7fSjob } 193797cceeeStb if (der != oder + len) { 194797cceeeStb warnx("%s: %td bytes trailing garbage", fn, oder + len - der); 195797cceeeStb goto out; 196797cceeeStb } 1979a7e9e7fSjob 198660fcdefSjob if (X509_CRL_get_version(crl->x509_crl) != 1) { 199660fcdefSjob warnx("%s: RFC 6487 section 5: version 2 expected", fn); 200660fcdefSjob goto out; 201660fcdefSjob } 202660fcdefSjob 2030466b83fStb if ((name = X509_CRL_get_issuer(crl->x509_crl)) == NULL) { 2040466b83fStb warnx("%s: X509_CRL_get_issuer", fn); 2050466b83fStb goto out; 2060466b83fStb } 2070466b83fStb if (!x509_valid_name(fn, "issuer", name)) 2080466b83fStb goto out; 2090466b83fStb 210073e107aStb if ((nid = X509_CRL_get_signature_nid(crl->x509_crl)) == NID_undef) { 211073e107aStb warnx("%s: unknown signature type", fn); 2127c9af4b7Sjob goto out; 2137c9af4b7Sjob } 21481a06611Sclaudio if (experimental && nid == NID_ecdsa_with_SHA256) { 215ec1cc732Sjob if (verbose) 2168fcc9cc2Sjob warnx("%s: P-256 support is experimental", fn); 217ec1cc732Sjob } else if (nid != NID_sha256WithRSAEncryption) { 2187c9af4b7Sjob warnx("%s: RFC 7935: wrong signature algorithm %s, want %s", 21978de3577Stb fn, nid2str(nid), LN_sha256WithRSAEncryption); 2207c9af4b7Sjob goto out; 2217c9af4b7Sjob } 2227c9af4b7Sjob 2234b65a085Stb /* 2244b65a085Stb * RFC 6487, section 5: AKI and crlNumber MUST be present, no other 2254b65a085Stb * CRL extensions are allowed. 2264b65a085Stb */ 2274b65a085Stb if ((count = X509_CRL_get_ext_count(crl->x509_crl)) != 2) { 2284b65a085Stb warnx("%s: RFC 6487 section 5: unexpected number of extensions " 2294b65a085Stb "%d != 2", fn, count); 2304bd8ba3aStb goto out; 2314bd8ba3aStb } 232*904d9c60Stb if (!crl_check_crl_number(fn, crl->x509_crl)) 23309383accStb goto out; 23409383accStb if ((crl->aki = crl_get_aki(fn, crl->x509_crl)) == NULL) 23509383accStb goto out; 2364bd8ba3aStb 237d7e95037Stb at = X509_CRL_get0_lastUpdate(crl->x509_crl); 238d7e95037Stb if (at == NULL) { 239d7e95037Stb warnx("%s: X509_CRL_get0_lastUpdate failed", fn); 240d7e95037Stb goto out; 241d7e95037Stb } 242c527cc7aSjob if (!x509_get_time(at, &crl->thisupdate)) { 2435abefff6Stb warnx("%s: ASN1_TIME_to_tm failed", fn); 244d7e95037Stb goto out; 245d7e95037Stb } 246d7e95037Stb 2474bd8ba3aStb at = X509_CRL_get0_nextUpdate(crl->x509_crl); 2484bd8ba3aStb if (at == NULL) { 2494bd8ba3aStb warnx("%s: X509_CRL_get0_nextUpdate failed", fn); 2504bd8ba3aStb goto out; 2514bd8ba3aStb } 2529f544822Sjob if (!x509_get_time(at, &crl->nextupdate)) { 2535abefff6Stb warnx("%s: ASN1_TIME_to_tm failed", fn); 2544bd8ba3aStb goto out; 2554bd8ba3aStb } 2564bd8ba3aStb 25709383accStb if (!crl_check_revoked(fn, crl->x509_crl)) 25809383accStb goto out; 25909383accStb 2609a7e9e7fSjob rc = 1; 2619a7e9e7fSjob out: 2629a7e9e7fSjob if (rc == 0) { 2634bd8ba3aStb crl_free(crl); 2644bd8ba3aStb crl = NULL; 2659a7e9e7fSjob } 2664bd8ba3aStb return crl; 2679a7e9e7fSjob } 2689a7e9e7fSjob 26951b3988bSbenno static inline int 27051b3988bSbenno crlcmp(struct crl *a, struct crl *b) 27151b3988bSbenno { 272c207abadSjob int cmp; 273c207abadSjob 274c207abadSjob cmp = strcmp(a->aki, b->aki); 275c207abadSjob if (cmp > 0) 276c207abadSjob return 1; 277c207abadSjob if (cmp < 0) 278c207abadSjob return -1; 279c207abadSjob 280c207abadSjob /* 281c207abadSjob * In filemode the mftpath cannot be determined easily, 282c207abadSjob * but it is always set in normal top-down validation. 283c207abadSjob */ 284c207abadSjob if (a->mftpath == NULL || b->mftpath == NULL) 285c207abadSjob return 0; 286c207abadSjob 287c207abadSjob cmp = strcmp(a->mftpath, b->mftpath); 288c207abadSjob if (cmp > 0) 289c207abadSjob return 1; 290c207abadSjob if (cmp < 0) 291c207abadSjob return -1; 292c207abadSjob 293c207abadSjob return 0; 29451b3988bSbenno } 29551b3988bSbenno 296c4a9443cSclaudio RB_GENERATE_STATIC(crl_tree, crl, entry, crlcmp); 297c4a9443cSclaudio 298c4a9443cSclaudio /* 299c4a9443cSclaudio * Find a CRL based on the auth SKI value. 300c4a9443cSclaudio */ 301c4a9443cSclaudio struct crl * 302c4a9443cSclaudio crl_get(struct crl_tree *crlt, const struct auth *a) 303c4a9443cSclaudio { 304c4a9443cSclaudio struct crl find; 305c4a9443cSclaudio 306f8e924bcStb /* XXX - this should be removed, but filemode relies on it. */ 307f8e924bcStb if (a == NULL) 308f8e924bcStb return NULL; 309f8e924bcStb 310c4a9443cSclaudio find.aki = a->cert->ski; 311c207abadSjob find.mftpath = a->cert->mft; 312c207abadSjob 313c4a9443cSclaudio return RB_FIND(crl_tree, crlt, &find); 314c4a9443cSclaudio } 315c4a9443cSclaudio 316c4a9443cSclaudio int 317c4a9443cSclaudio crl_insert(struct crl_tree *crlt, struct crl *crl) 318c4a9443cSclaudio { 319c4a9443cSclaudio return RB_INSERT(crl_tree, crlt, crl) == NULL; 320c4a9443cSclaudio } 32151b3988bSbenno 32251b3988bSbenno void 3234bd8ba3aStb crl_free(struct crl *crl) 32451b3988bSbenno { 3254bd8ba3aStb if (crl == NULL) 3264bd8ba3aStb return; 327e669621fSclaudio free(crl->aki); 328c207abadSjob free(crl->mftpath); 32951b3988bSbenno X509_CRL_free(crl->x509_crl); 33051b3988bSbenno free(crl); 33151b3988bSbenno } 33291176c18Sjob 33391176c18Sjob void 33491176c18Sjob crl_tree_free(struct crl_tree *crlt) 33591176c18Sjob { 33691176c18Sjob struct crl *crl, *tcrl; 33791176c18Sjob 33891176c18Sjob RB_FOREACH_SAFE(crl, crl_tree, crlt, tcrl) { 33991176c18Sjob RB_REMOVE(crl_tree, crlt, crl); 34091176c18Sjob crl_free(crl); 34191176c18Sjob } 34291176c18Sjob } 343