-/* $OpenBSD: cert.c,v 1.126 2024/02/16 11:55:42 tb Exp $ */
+/* $OpenBSD: cert.c,v 1.127 2024/02/16 14:48:47 tb Exp $ */
/*
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
* Copyright (c) 2021 Job Snijders <job@openbsd.org>
#include "extern.h"
-/*
- * A parsing sequence of a file (which may just be <stdin>).
- */
-struct parse {
- struct cert *res; /* result */
- 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) */
* Returns zero on failure, non-zero on success.
*/
static int
-sbgp_assysnum(struct parse *p, X509_EXTENSION *ext)
+sbgp_assysnum(const char *fn, struct cert *cert, X509_EXTENSION *ext)
{
ASIdentifiers *asidentifiers = NULL;
int rc = 0;
if (!X509_EXTENSION_get_critical(ext)) {
warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
- "extension not critical", p->fn);
+ "extension not critical", fn);
goto out;
}
if ((asidentifiers = X509V3_EXT_d2i(ext)) == NULL) {
warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
- "failed extension parse", p->fn);
+ "failed extension parse", fn);
goto out;
}
- if (!sbgp_parse_assysnum(p->fn, asidentifiers,
- &p->res->as, &p->res->asz))
+ if (!sbgp_parse_assysnum(fn, asidentifiers, &cert->as, &cert->asz))
goto out;
rc = 1;
* Returns zero on failure, non-zero on success.
*/
static int
-sbgp_ipaddrblk(struct parse *p, X509_EXTENSION *ext)
+sbgp_ipaddrblk(const char *fn, struct cert *cert, X509_EXTENSION *ext)
{
IPAddrBlocks *addrblk = NULL;
int rc = 0;
if (!X509_EXTENSION_get_critical(ext)) {
warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: "
- "extension not critical", p->fn);
+ "extension not critical", fn);
goto out;
}
if ((addrblk = X509V3_EXT_d2i(ext)) == NULL) {
warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: "
- "failed extension parse", p->fn);
+ "failed extension parse", fn);
goto out;
}
- if (!sbgp_parse_ipaddrblk(p->fn, addrblk, &p->res->ips, &p->res->ipsz))
+ if (!sbgp_parse_ipaddrblk(fn, addrblk, &cert->ips, &cert->ipsz))
goto out;
- if (p->res->ipsz == 0) {
- warnx("%s: RFC 6487 section 4.8.10: empty ipAddrBlock", p->fn);
+ if (cert->ipsz == 0) {
+ warnx("%s: RFC 6487 section 4.8.10: empty ipAddrBlock", fn);
goto out;
}
* Returns zero on failure, non-zero on success.
*/
static int
-sbgp_sia(struct parse *p, X509_EXTENSION *ext)
+sbgp_sia(const char *fn, struct cert *cert, X509_EXTENSION *ext)
{
AUTHORITY_INFO_ACCESS *sia = NULL;
ACCESS_DESCRIPTION *ad;
if (X509_EXTENSION_get_critical(ext)) {
warnx("%s: RFC 6487 section 4.8.8: SIA: "
- "extension not non-critical", p->fn);
+ "extension not non-critical", fn);
goto out;
}
if ((sia = X509V3_EXT_d2i(ext)) == NULL) {
warnx("%s: RFC 6487 section 4.8.8: SIA: failed extension parse",
- p->fn);
+ fn);
goto out;
}
oid = ad->method;
if (OBJ_cmp(oid, carepo_oid) == 0) {
- if (!x509_location(p->fn, "SIA: caRepository",
- "rsync://", ad->location, &p->res->repo))
+ if (!x509_location(fn, "SIA: caRepository",
+ "rsync://", ad->location, &cert->repo))
goto out;
} else if (OBJ_cmp(oid, manifest_oid) == 0) {
- if (!x509_location(p->fn, "SIA: rpkiManifest",
- "rsync://", ad->location, &p->res->mft))
+ if (!x509_location(fn, "SIA: rpkiManifest",
+ "rsync://", ad->location, &cert->mft))
goto out;
} else if (OBJ_cmp(oid, notify_oid) == 0) {
- if (!x509_location(p->fn, "SIA: rpkiNotify",
- "https://", ad->location, &p->res->notify))
+ if (!x509_location(fn, "SIA: rpkiNotify",
+ "https://", ad->location, &cert->notify))
goto out;
}
}
- if (p->res->mft == NULL || p->res->repo == NULL) {
+ if (cert->mft == NULL || cert->repo == NULL) {
warnx("%s: RFC 6487 section 4.8.8: SIA: missing caRepository "
- "or rpkiManifest", p->fn);
+ "or rpkiManifest", fn);
goto out;
}
- mftfilename = strrchr(p->res->mft, '/');
+ mftfilename = strrchr(cert->mft, '/');
if (mftfilename == NULL) {
- warnx("%s: SIA: invalid rpkiManifest entry", p->fn);
+ warnx("%s: SIA: invalid rpkiManifest entry", fn);
goto out;
}
mftfilename++;
if (!valid_filename(mftfilename, strlen(mftfilename))) {
warnx("%s: SIA: rpkiManifest filename contains invalid "
- "characters", p->fn);
+ "characters", fn);
goto out;
}
- if (strstr(p->res->mft, p->res->repo) != p->res->mft) {
+ if (strstr(cert->mft, cert->repo) != cert->mft) {
warnx("%s: RFC 6487 section 4.8.8: SIA: "
- "conflicting URIs for caRepository and rpkiManifest",
- p->fn);
+ "conflicting URIs for caRepository and rpkiManifest", fn);
goto out;
}
- if (rtype_from_file_extension(p->res->mft) != RTYPE_MFT) {
- warnx("%s: RFC 6487 section 4.8.8: SIA: "
- "not an MFT file", p->fn);
+ if (rtype_from_file_extension(cert->mft) != RTYPE_MFT) {
+ warnx("%s: RFC 6487 section 4.8.8: SIA: not an MFT file", fn);
goto out;
}
* Returns zero on failure, non-zero on success.
*/
static int
-certificate_policies(struct parse *p, X509_EXTENSION *ext)
+certificate_policies(const char *fn, struct cert *cert, X509_EXTENSION *ext)
{
STACK_OF(POLICYINFO) *policies = NULL;
POLICYINFO *policy;
if (!X509_EXTENSION_get_critical(ext)) {
warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
- "extension not critical", p->fn);
+ "extension not critical", fn);
goto out;
}
if ((policies = X509V3_EXT_d2i(ext)) == NULL) {
warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
- "failed extension parse", p->fn);
+ "failed extension parse", 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));
+ "want 1 policy, got %d", fn, sk_POLICYINFO_num(policies));
goto out;
}
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);
+ "unexpected OID: %s, want %s", fn, pbuf, cbuf);
goto out;
}
if (sk_POLICYQUALINFO_num(qualifiers) != 1) {
warnx("%s: RFC 7318 section 2: certificatePolicies: "
- "want 1 policy qualifier, got %d", p->fn,
+ "want 1 policy qualifier, got %d", fn,
sk_POLICYQUALINFO_num(qualifiers));
goto out;
}
if ((nid = OBJ_obj2nid(qualifier->pqualid)) != NID_id_qt_cps) {
warnx("%s: RFC 7318 section 2: certificatePolicies: "
- "want CPS, got %s", p->fn, nid2str(nid));
+ "want CPS, got %s", fn, nid2str(nid));
goto out;
}
if (verbose > 1 && !filemode)
- warnx("%s: CPS %.*s", p->fn, qualifier->d.cpsuri->length,
+ warnx("%s: CPS %.*s", fn, qualifier->d.cpsuri->length,
qualifier->d.cpsuri->data);
rc = 1;
struct cert *
cert_parse_ee_cert(const char *fn, int talid, X509 *x)
{
- struct parse p;
+ struct cert *cert;
X509_EXTENSION *ext;
int index;
- memset(&p, 0, sizeof(struct parse));
- p.fn = fn;
- if ((p.res = calloc(1, sizeof(struct cert))) == NULL)
+ if ((cert = calloc(1, sizeof(struct cert))) == NULL)
err(1, NULL);
if (X509_get_version(x) != 2) {
index = X509_get_ext_by_NID(x, NID_sbgp_ipAddrBlock, -1);
if ((ext = X509_get_ext(x, index)) != NULL) {
- if (!sbgp_ipaddrblk(&p, ext))
+ if (!sbgp_ipaddrblk(fn, cert, ext))
goto out;
}
index = X509_get_ext_by_NID(x, NID_sbgp_autonomousSysNum, -1);
if ((ext = X509_get_ext(x, index)) != NULL) {
- if (!sbgp_assysnum(&p, ext))
+ if (!sbgp_assysnum(fn, cert, ext))
goto out;
}
goto out;
}
- p.res->x509 = x;
- p.res->talid = talid;
+ cert->x509 = x;
+ cert->talid = talid;
- if (!constraints_validate(fn, p.res))
+ if (!constraints_validate(fn, cert))
goto out;
- return p.res;
+ return cert;
out:
- cert_free(p.res);
+ cert_free(cert);
return NULL;
}
struct cert *
cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
{
+ struct cert *cert;
const unsigned char *oder;
size_t j;
int i, extsz;
const ASN1_OBJECT *cobj;
ASN1_OBJECT *obj;
EVP_PKEY *pkey;
- struct parse p;
int nid, ip, as, sia, cp, crldp, aia, aki, ski,
eku, bc, ku;
if (der == NULL)
return NULL;
- memset(&p, 0, sizeof(struct parse));
- p.fn = fn;
- if ((p.res = calloc(1, sizeof(struct cert))) == NULL)
+ if ((cert = calloc(1, sizeof(struct cert))) == NULL)
err(1, NULL);
oder = der;
if ((x = d2i_X509(NULL, &der, len)) == NULL) {
- warnx("%s: d2i_X509", p.fn);
+ warnx("%s: d2i_X509", fn);
goto out;
}
if (der != oder + len) {
/* Cache X509v3 extensions, see X509_check_ca(3). */
if (X509_check_purpose(x, -1, -1) <= 0) {
- warnx("%s: could not cache X509v3 extensions", p.fn);
+ warnx("%s: could not cache X509v3 extensions", fn);
goto out;
}
X509_get0_signature(NULL, &palg, x);
if (palg == NULL) {
- warnx("%s: X509_get0_signature", p.fn);
+ warnx("%s: X509_get0_signature", fn);
goto out;
}
X509_ALGOR_get0(&cobj, NULL, NULL, palg);
goto out;
}
- if (!x509_valid_subject(p.fn, x))
+ if (!x509_valid_subject(fn, x))
goto out;
/* Look for X509v3 extensions. */
case NID_sbgp_ipAddrBlock:
if (ip++ > 0)
goto dup;
- if (!sbgp_ipaddrblk(&p, ext))
+ if (!sbgp_ipaddrblk(fn, cert, ext))
goto out;
break;
case NID_sbgp_autonomousSysNum:
if (as++ > 0)
goto dup;
- if (!sbgp_assysnum(&p, ext))
+ if (!sbgp_assysnum(fn, cert, ext))
goto out;
break;
case NID_sinfo_access:
if (sia++ > 0)
goto dup;
- if (!sbgp_sia(&p, ext))
+ if (!sbgp_sia(fn, cert, ext))
goto out;
break;
case NID_certificate_policies:
if (cp++ > 0)
goto dup;
- if (!certificate_policies(&p, ext))
+ if (!certificate_policies(fn, cert, ext))
goto out;
break;
case NID_crl_distribution_points:
char objn[64];
OBJ_obj2txt(objn, sizeof(objn), obj, 0);
warnx("%s: ignoring %s (NID %d)",
- p.fn, objn, OBJ_obj2nid(obj));
+ fn, objn, OBJ_obj2nid(obj));
}
break;
}
}
- if (!x509_get_aki(x, p.fn, &p.res->aki))
+ if (!x509_get_aki(x, fn, &cert->aki))
goto out;
- if (!x509_get_ski(x, p.fn, &p.res->ski))
+ if (!x509_get_ski(x, fn, &cert->ski))
goto out;
- if (!x509_get_aia(x, p.fn, &p.res->aia))
+ if (!x509_get_aia(x, fn, &cert->aia))
goto out;
- if (!x509_get_crl(x, p.fn, &p.res->crl))
+ if (!x509_get_crl(x, fn, &cert->crl))
goto out;
- if (!x509_get_notbefore(x, p.fn, &p.res->notbefore))
+ if (!x509_get_notbefore(x, fn, &cert->notbefore))
goto out;
- if (!x509_get_notafter(x, p.fn, &p.res->notafter))
+ if (!x509_get_notafter(x, fn, &cert->notafter))
goto out;
- p.res->purpose = x509_get_purpose(x, p.fn);
+ cert->purpose = x509_get_purpose(x, fn);
/* Validation on required fields. */
- switch (p.res->purpose) {
+ switch (cert->purpose) {
case CERT_PURPOSE_CA:
if ((pkey = X509_get0_pubkey(x)) == NULL) {
- warnx("%s: X509_get0_pubkey failed", p.fn);
+ warnx("%s: X509_get0_pubkey failed", fn);
goto out;
}
- if (!valid_ca_pkey(p.fn, pkey))
+ if (!valid_ca_pkey(fn, pkey))
goto out;
if (X509_get_key_usage(x) != (KU_KEY_CERT_SIGN | KU_CRL_SIGN)) {
warnx("%s: RFC 6487 section 4.8.4: key usage violation",
- p.fn);
+ fn);
goto out;
}
goto out;
}
- if (p.res->mft == NULL) {
- warnx("%s: RFC 6487 section 4.8.8: missing SIA", p.fn);
+ if (cert->mft == NULL) {
+ warnx("%s: RFC 6487 section 4.8.8: missing SIA", fn);
goto out;
}
- if (p.res->asz == 0 && p.res->ipsz == 0) {
- warnx("%s: missing IP or AS resources", p.fn);
+ if (cert->asz == 0 && cert->ipsz == 0) {
+ warnx("%s: missing IP or AS resources", fn);
goto out;
}
break;
case CERT_PURPOSE_BGPSEC_ROUTER:
- p.res->pubkey = x509_get_pubkey(x, p.fn);
- if (p.res->pubkey == NULL) {
- warnx("%s: x509_get_pubkey failed", p.fn);
+ cert->pubkey = x509_get_pubkey(x, fn);
+ if (cert->pubkey == NULL) {
+ warnx("%s: x509_get_pubkey failed", fn);
goto out;
}
- if (p.res->ipsz > 0) {
- warnx("%s: unexpected IP resources in BGPsec cert",
- p.fn);
+ if (cert->ipsz > 0) {
+ warnx("%s: unexpected IP resources in BGPsec cert", fn);
goto out;
}
- for (j = 0; j < p.res->asz; j++) {
- if (p.res->as[j].type == CERT_AS_INHERIT) {
+ for (j = 0; j < cert->asz; j++) {
+ if (cert->as[j].type == CERT_AS_INHERIT) {
warnx("%s: inherit elements not allowed in EE"
- " cert", p.fn);
+ " cert", fn);
goto out;
}
}
if (sia) {
warnx("%s: unexpected SIA extension in BGPsec cert",
- p.fn);
+ fn);
goto out;
}
break;
default:
- warnx("%s: x509_get_purpose failed in %s", p.fn, __func__);
+ warnx("%s: x509_get_purpose failed in %s", fn, __func__);
goto out;
}
- if (p.res->ski == NULL) {
- warnx("%s: RFC 6487 section 8.4.2: missing SKI", p.fn);
+ if (cert->ski == NULL) {
+ warnx("%s: RFC 6487 section 8.4.2: missing SKI", fn);
goto out;
}
- p.res->x509 = x;
- return p.res;
+ cert->x509 = x;
+ return cert;
dup:
warnx("%s: RFC 5280 section 4.2: duplicate extension: %s", fn,
nid2str(nid));
out:
- cert_free(p.res);
+ cert_free(cert);
X509_free(x);
return NULL;
}