-/* $OpenBSD: cert.c,v 1.142 2024/06/08 13:28:35 tb Exp $ */
+/* $OpenBSD: cert.c,v 1.143 2024/06/08 13:31:37 tb Exp $ */
/*
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
* Copyright (c) 2021 Job Snijders <job@openbsd.org>
goto out;
if (!x509_get_notafter(x, fn, &cert->notafter))
goto out;
- cert->purpose = x509_get_purpose(x, fn);
/* Validation on required fields. */
-
+ cert->purpose = x509_get_purpose(x, fn);
switch (cert->purpose) {
+ case CERT_PURPOSE_TA:
+ /* XXX - caller should indicate if it expects TA or CA cert */
case CERT_PURPOSE_CA:
if ((pkey = X509_get0_pubkey(x)) == NULL) {
warnx("%s: X509_get0_pubkey failed", fn);
goto out;
}
break;
+ case CERT_PURPOSE_EE:
+ warn("%s: unexpected EE cert", fn);
+ goto out;
default:
warnx("%s: x509_get_purpose failed in %s", fn, __func__);
goto out;
-/* $OpenBSD: filemode.c,v 1.43 2024/06/06 07:19:10 tb Exp $ */
+/* $OpenBSD: filemode.c,v 1.44 2024/06/08 13:31:38 tb Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
if (cert == NULL)
goto done;
if (cert->purpose != CERT_PURPOSE_CA) {
- warnx("AIA reference to bgpsec cert %s", uri);
+ warnx("AIA reference to %s in %s",
+ purpose2str(cert->purpose), uri);
goto done;
}
/* try to load the CRL of this cert */
-/* $OpenBSD: main.c,v 1.259 2024/06/07 08:22:53 claudio Exp $ */
+/* $OpenBSD: main.c,v 1.260 2024/06/08 13:31:38 tb Exp $ */
/*
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
}
cert = cert_read(b);
switch (cert->purpose) {
+ case CERT_PURPOSE_TA:
case CERT_PURPOSE_CA:
queue_add_from_cert(cert);
break;
repo_stat_inc(rp, talid, type, STYPE_BGPSEC);
break;
default:
- errx(1, "unexpected cert purpose received");
+ errx(1, "unexpected %s", purpose2str(cert->purpose));
break;
}
cert_free(cert);
-/* $OpenBSD: x509.c,v 1.95 2024/06/08 13:28:35 tb Exp $ */
+/* $OpenBSD: x509.c,v 1.96 2024/06/08 13:31:38 tb Exp $ */
/*
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
}
/*
- * Check the certificate's purpose: CA or BGPsec Router.
- * Return a member of enum cert_purpose.
+ * Check the cert's purpose: the cA bit in basic constraints distinguishes
+ * between TA/CA and EE/BGPsec router. TAs are self-signed, CAs not self-issued,
+ * EEs have no extended key usage, BGPsec router have id-kp-bgpsec-router OID.
*/
enum cert_purpose
x509_get_purpose(X509 *x, const char *fn)
{
BASIC_CONSTRAINTS *bc = NULL;
EXTENDED_KEY_USAGE *eku = NULL;
- int crit;
+ int crit, ext_flags, is_ca;
enum cert_purpose purpose = CERT_PURPOSE_INVALID;
- if (X509_check_ca(x) == 1) {
+ if (!x509_cache_extensions(x, fn))
+ goto out;
+
+ ext_flags = X509_get_extension_flags(x);
+
+ /* This weird API can return 0, 1, 2, 4, 5 but can't error... */
+ if ((is_ca = X509_check_ca(x)) > 1) {
+ if (is_ca == 4)
+ warnx("%s: RFC 6487: sections 4.8.1 and 4.8.4: "
+ "no basic constraints, but keyCertSign set", fn);
+ else
+ warnx("%s: unexpected legacy certificate", fn);
+ goto out;
+ }
+
+ if (is_ca) {
bc = X509_get_ext_d2i(x, NID_basic_constraints, &crit, NULL);
if (bc == NULL) {
if (crit != -1)
"Constraint must be absent", fn);
goto out;
}
- purpose = CERT_PURPOSE_CA;
- /* XXX - we may want to check EXFLAG_SI and add a TA purpose. */
+ /*
+ * EXFLAG_SI means that issuer and subject are identical.
+ * EXFLAG_SS is SI plus the AKI is absent or matches the SKI.
+ * Thus, exactly the trust anchors should have EXFLAG_SS set
+ * and we should never see EXFLAG_SI without EXFLAG_SS.
+ */
+ if ((ext_flags & EXFLAG_SS) != 0)
+ purpose = CERT_PURPOSE_TA;
+ else if ((ext_flags & EXFLAG_SI) == 0)
+ purpose = CERT_PURPOSE_CA;
+ else
+ warnx("%s: RFC 6487, section 4.8.3: "
+ "self-issued cert with AKI-SKI mismatch", fn);
goto out;
}
- if (X509_get_extension_flags(x) & EXFLAG_BCONS) {
+ if ((ext_flags & EXFLAG_BCONS) != 0) {
warnx("%s: Basic Constraints ext in non-CA cert", fn);
goto out;
}
+ /*
+ * EKU is only defined for BGPsec Router certs and must be absent from
+ * EE certs.
+ */
eku = X509_get_ext_d2i(x, NID_ext_key_usage, &crit, NULL);
if (eku == NULL) {
if (crit != -1)
warnx("%s: error parsing EKU", fn);
else
- warnx("%s: EKU: extension missing", fn);
+ purpose = CERT_PURPOSE_EE; /* EKU absent */
goto out;
}
if (crit != 0) {