From f9f55a97ffd0d7bd289af50a191b2df27cfd73a6 Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 4 Feb 2022 16:28:20 +0000 Subject: [PATCH] Ensure that certificate policies follow RFC 7318 RFC 7318 makes requirements on the certificate policy extension imposed by RFC 6487 a bit stricter. It requires that exactly one policy OID is present and that it be id-cp-ipAddr-asNumber and if there is a policy qualifier it must be id-qt-cps. These are requirements that the X.509 verifier's policy code can't enforce, so unpack the certificate policy extension by hand and check that it matches expectations. ok claudio --- usr.sbin/rpki-client/cert.c | 81 ++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/usr.sbin/rpki-client/cert.c b/usr.sbin/rpki-client/cert.c index b2391fb3d25..b5fc2e0fdc8 100644 --- a/usr.sbin/rpki-client/cert.c +++ b/usr.sbin/rpki-client/cert.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cert.c,v 1.53 2022/01/20 16:36:19 claudio Exp $ */ +/* $OpenBSD: cert.c,v 1.54 2022/02/04 16:28:20 tb Exp $ */ /* * Copyright (c) 2021 Job Snijders * Copyright (c) 2019 Kristaps Dzonsons @@ -29,6 +29,7 @@ #include #include +#include #include "extern.h" @@ -47,6 +48,7 @@ struct parse { const char *fn; /* currently-parsed file */ }; +extern ASN1_OBJECT *certpol_oid; /* id-cp-ipAddr-asNumber cert policy */ extern ASN1_OBJECT *carepo_oid; /* 1.3.6.1.5.5.7.48.5 (caRepository) */ extern ASN1_OBJECT *manifest_oid; /* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */ extern ASN1_OBJECT *notify_oid; /* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */ @@ -969,6 +971,80 @@ out: return rc; } +static int +certificate_policies(struct parse *p, X509_EXTENSION *ext) +{ + STACK_OF(POLICYINFO) *policies = NULL; + POLICYINFO *policy; + STACK_OF(POLICYQUALINFO) *qualifiers; + POLICYQUALINFO *qualifier; + int nid; + int rc = 0; + + if (!X509_EXTENSION_get_critical(ext)) { + cryptowarnx("%s: RFC 6487 section 4.8.9: certificatePolicies: " + "extension not critical", p->fn); + goto out; + } + + if ((policies = X509V3_EXT_d2i(ext)) == NULL) { + cryptowarnx("%s: RFC 6487 section 4.8.9: certificatePolicies: " + "failed extension parse", p->fn); + goto out; + } + + if (sk_POLICYINFO_num(policies) != 1) { + warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: " + "want 1 policy, got %d", p->fn, + sk_POLICYINFO_num(policies)); + goto out; + } + + policy = sk_POLICYINFO_value(policies, 0); + assert(policy != NULL && policy->policyid != NULL); + + if (OBJ_cmp(policy->policyid, certpol_oid) != 0) { + char pbuf[128], cbuf[128]; + + OBJ_obj2txt(pbuf, sizeof(pbuf), policy->policyid, 1); + OBJ_obj2txt(cbuf, sizeof(cbuf), certpol_oid, 1); + warnx("%s: RFC 7318 section 2: certificatePolicies: " + "unexpected OID: %s, want %s", p->fn, pbuf, cbuf); + goto out; + } + + /* Policy qualifiers are optional. If they're absent, we're done. */ + if ((qualifiers = policy->qualifiers) == NULL) { + rc = 1; + goto out; + } + + if (sk_POLICYQUALINFO_num(qualifiers) != 1) { + warnx("%s: RFC 7318 section 2: certificatePolicies: " + "want 1 policy qualifier, got %d", p->fn, + sk_POLICYQUALINFO_num(qualifiers)); + goto out; + } + + qualifier = sk_POLICYQUALINFO_value(qualifiers, 0); + assert(qualifier != NULL && qualifier->pqualid != NULL); + + if ((nid = OBJ_obj2nid(qualifier->pqualid)) != NID_id_qt_cps) { + warnx("%s: RFC 7318 section 2: certificatePolicies: " + "want CPS, got %d (%s)", p->fn, nid, OBJ_nid2sn(nid)); + goto out; + } + + if (verbose > 1) + warnx("%s: CPS %.*s", p->fn, qualifier->d.cpsuri->length, + qualifier->d.cpsuri->data); + + rc = 1; + out: + sk_POLICYINFO_pop_free(policies, POLICYINFO_free); + return rc; +} + /* * Parse and partially validate an RPKI X509 certificate (either a trust * anchor or a certificate) as defined in RFC 6487. @@ -1025,6 +1101,9 @@ cert_parse_inner(const char *fn, const unsigned char *der, size_t len, int ta) sia_present = 1; c = sbgp_sia(&p, ext); break; + case NID_certificate_policies: + c = certificate_policies(&p, ext); + break; case NID_crl_distribution_points: /* ignored here, handled later */ break; -- 2.20.1