Add a x509_cache_extensions() helper
authortb <tb@openbsd.org>
Sat, 8 Jun 2024 13:28:35 +0000 (13:28 +0000)
committertb <tb@openbsd.org>
Sat, 8 Jun 2024 13:28:35 +0000 (13:28 +0000)
This is a simple wrapper around X509_check_policy(cert, -1, 0) that
doesn't need an explanatory comment in the caller.

The reason for having to do this is that various OpenSSL API calls rely
on having extension information cached. As an unsurprising consequence of
OpenSSL's characteristic API misdesign these calls can't report errors,
so they call the extension caching without error checking and the result
is that they may report nonsense.

To work around this, cache the extensions up front so a second call can't
fail and thus API calls such as X509_check_ca(), X509_get_key_usage() and
X509_cmp() work reliably.

ok job

usr.sbin/rpki-client/cert.c
usr.sbin/rpki-client/cms.c
usr.sbin/rpki-client/extern.h
usr.sbin/rpki-client/x509.c

index 0faf997..ae16265 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cert.c,v 1.141 2024/06/07 08:36:54 tb Exp $ */
+/*     $OpenBSD: cert.c,v 1.142 2024/06/08 13:28:35 tb Exp $ */
 /*
  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
  * Copyright (c) 2021 Job Snijders <job@openbsd.org>
@@ -744,6 +744,9 @@ cert_parse_ee_cert(const char *fn, int talid, X509 *x)
        if (!cert_check_subject_and_issuer(fn, x))
                goto out;
 
+       if (!x509_cache_extensions(x, fn))
+               goto out;
+
        if (X509_get_key_usage(x) != KU_DIGITAL_SIGNATURE) {
                warnx("%s: RFC 6487 section 4.8.4: KU must be digitalSignature",
                    fn);
@@ -827,11 +830,8 @@ cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
                goto out;
        }
 
-       /* Cache X509v3 extensions, see X509_check_ca(3). */
-       if (X509_check_purpose(x, -1, -1) <= 0) {
-               warnx("%s: could not cache X509v3 extensions", fn);
+       if (!x509_cache_extensions(x, fn))
                goto out;
-       }
 
        if (X509_get_version(x) != 2) {
                warnx("%s: RFC 6487 4.1: X.509 version must be v3", fn);
index 5ef647e..0394680 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cms.c,v 1.45 2024/05/24 12:57:20 tb Exp $ */
+/*     $OpenBSD: cms.c,v 1.46 2024/06/08 13:28:35 tb Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -324,11 +324,8 @@ cms_parse_validate_internal(X509 **xp, const char *fn, const unsigned char *der,
                goto out;
        }
 
-       /* Cache X509v3 extensions, see X509_check_ca(3). */
-       if (X509_check_purpose(*xp, -1, -1) <= 0) {
-               warnx("%s: could not cache X509v3 extensions", fn);
+       if (!x509_cache_extensions(*xp, fn))
                goto out;
-       }
 
        if (!x509_get_notafter(*xp, fn, &notafter))
                goto out;
index 601d7d2..446eaf1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: extern.h,v 1.221 2024/06/04 04:17:18 tb Exp $ */
+/*     $OpenBSD: extern.h,v 1.222 2024/06/08 13:28:35 tb Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -901,6 +901,7 @@ struct ibuf *io_buf_recvfd(int, struct ibuf **);
 /* X509 helpers. */
 
 void            x509_init_oid(void);
+int             x509_cache_extensions(X509 *, const char *);
 int             x509_get_aia(X509 *, const char *, char **);
 int             x509_get_aki(X509 *, const char *, char **);
 int             x509_get_sia(X509 *, const char *, char **);
index dc9ffcb..3c0517c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: x509.c,v 1.94 2024/06/07 08:36:54 tb Exp $ */
+/*     $OpenBSD: x509.c,v 1.95 2024/06/08 13:28:35 tb Exp $ */
 /*
  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
@@ -132,6 +132,26 @@ x509_init_oid(void)
        }
 }
 
+/*
+ * A number of critical OpenSSL API functions can't properly indicate failure
+ * and are unreliable if the extensions aren't already cached. An old trick is
+ * to cache the extensions using an error-checked call to X509_check_purpose()
+ * with a purpose of -1. This way functions such as X509_check_ca(), X509_cmp(),
+ * X509_get_key_usage(), X509_get_extended_key_usage() won't lie.
+ *
+ * Should be called right after deserialization and is essentially free to call
+ * multiple times.
+ */
+int
+x509_cache_extensions(X509 *x509, const char *fn)
+{
+       if (X509_check_purpose(x509, -1, 0) <= 0) {
+               warnx("%s: could not cache X509v3 extensions", fn);
+               return 0;
+       }
+       return 1;
+}
+
 /*
  * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3.
  * Returns the AKI or NULL if it could not be parsed.