On Sat, Dec 25, 2021 at 05:48:53PM +0100, Claudio Jeker wrote:
[...]
> I would love to get rid of X509_V_FLAG_IGNORE_CRITICAL and use a callback
> to ensure the right extensions are critical but I never managed to
> understand how the X509_verify_cert() callback actually works.
> Documentation seems to be non-existent.
My understanding is that X509_V_FLAG_IGNORE_CRITICAL is a big hammer
that was necessary as long as libcrypto didn't know about the RFC 3779
extensions. Without it X509_verify_cert() would error on encountering
them. This changed shortly after Gouveia when we enabled the RFC 3779
code in libcrypto. I think setting the flag is no longer appropriate in
-current.
Below is a diff that removes X509_V_FLAG_IGNORE_CRITICAL and adds a
debug verify callback that intercepts unhandled critical extensions. It
lets through the RFC 3779 extensions and errors on everything else.
If you run with this on -current, the callback doesn't print anything.
On -stable you'll see that it logs at least one of the two extensions
per cert while X509_verify_cert() is walking through the chains.
If we want to drop X509_V_FLAG_IGNORE_CRITICAL, something like this
will be needed as long as rpki-client wants to support LibreSSL < 3.5.
The idea is that we handle RFC 3779 extensions ourselves, so we can
safely ignore them in the verifier. All other critical extensions that
neither rpki-client nor libcrypto know about should be verification
failures.
You can play with this on -current by disabling RFC 3779 again: remove
or comment the #ifndef LIBRESSL_CRYPTO_INTERNAL around OPENSSL_NO_RFC3779
in lib/libcrypto/opensslfeatures.h then make clean, make includes, make
and make install.
Index: parser.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/parser.c,v
retrieving revision 1.28
diff -u -p -r1.28 parser.c
--- parser.c 4 Nov 2021 18:26:48 -0000 1.28
+++ parser.c 27 Dec 2021 08:37:53 -0000
@@ -34,6 +34,7 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
+#include <openssl/x509v3.h>
#include "extern.h"
@@ -45,6 +46,102 @@ static X509_STORE_CTX *ctx;
static struct auth_tree auths = RB_INITIALIZER(&auths);
static struct crl_tree crlt = RB_INITIALIZER(&crlt);
+static int
+generic_verify(int ok, X509_STORE_CTX *store_ctx, const char *descr)
+{
+ X509 *cert;
+ const STACK_OF(X509_EXTENSION) *exts;
+ X509_EXTENSION *ext;
+ ASN1_OBJECT *obj;
+ int depth, error, i, nid;
+ int saw_ipAddrBlock = 0;
+ int saw_autonomousSysNum = 0;
+ int saw_unknown = 0;
+
+ error = X509_STORE_CTX_get_error(store_ctx);
+ depth = X509_STORE_CTX_get_error_depth(store_ctx);
+
+ switch (error) {
+ case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
+ case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
+ break;
+ default:
+ return ok;
+ }
+
+ fprintf(stderr, "%s cb: ok %d, %s, depth %d\n",
+ descr, ok, X509_verify_cert_error_string(error), depth);
+
+ if ((cert = X509_STORE_CTX_get_current_cert(store_ctx)) == NULL) {
+ fprintf(stderr, "%s cb: got no current cert\n", descr);
+ return 0;
+ }
+ if ((exts = X509_get0_extensions(cert)) == NULL) {
+ fprintf(stderr, "%s cb: got no extensions\n", descr);
+ return 0;
+ }
+
+ for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
+ ext = sk_X509_EXTENSION_value(exts, i);
+
+ /* skip over non-critical and known extensions */
+ if (!X509_EXTENSION_get_critical(ext))
+ continue;
+ if (X509_supported_extension(ext))
+ continue;
+
+ if ((obj = X509_EXTENSION_get_object(ext)) == NULL) {
+ fprintf(stderr, "%s cb: got no object\n", descr);
+ return 0;
+ }
+
+ nid = OBJ_obj2nid(obj);
+ switch (nid) {
+ case NID_sbgp_ipAddrBlock:
+ saw_ipAddrBlock = 1;
+ break;
+ case NID_sbgp_autonomousSysNum:
+ saw_autonomousSysNum = 1;
+ break;
+ default:
+ fprintf(stderr, "unknown extension: nid %d\n", nid);
+ X509V3_EXT_print_fp(stderr, ext,
X509V3_EXT_DUMP_UNKNOWN, 8);
+ saw_unknown = 1;
+ break;
+ }
+ }
+
+ fprintf(stderr, "%s cb: addrBlock %d, autonomousSysNum %d\n",
+ descr, saw_ipAddrBlock, saw_autonomousSysNum);
+
+ /* Fail if we saw an unknown extension. */
+ return !saw_unknown;
+}
+
+static int
+roa_verify_cb(int ok, X509_STORE_CTX *store_ctx)
+{
+ return generic_verify(ok, store_ctx, "roa");
+}
+
+static int
+mft_verify_cb(int ok, X509_STORE_CTX *store_ctx)
+{
+ return generic_verify(ok, store_ctx, "mft");
+}
+
+static int
+cert_verify_cb(int ok, X509_STORE_CTX *store_ctx)
+{
+ return generic_verify(ok, store_ctx, "crt");
+}
+
+static int
+gbr_verify_cb(int ok, X509_STORE_CTX *store_ctx)
+{
+ return generic_verify(ok, store_ctx, "gbr");
+}
+
/*
* Parse and validate a ROA.
* This is standard stuff.
@@ -72,8 +169,8 @@ proc_parser_roa(struct entity *entp, con
assert(x509 != NULL);
if (!X509_STORE_CTX_init(ctx, NULL, x509, NULL))
cryptoerrx("X509_STORE_CTX_init");
- X509_STORE_CTX_set_flags(ctx,
- X509_V_FLAG_IGNORE_CRITICAL | X509_V_FLAG_CRL_CHECK);
+ X509_STORE_CTX_set_verify_cb(ctx, roa_verify_cb);
+ X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK);
X509_STORE_CTX_set_depth(ctx, MAX_CERT_DEPTH);
X509_STORE_CTX_set0_trusted_stack(ctx, chain);
X509_STORE_CTX_set0_crls(ctx, crls);
@@ -151,7 +248,7 @@ proc_parser_mft(struct entity *entp, con
cryptoerrx("X509_STORE_CTX_init");
/* CRL checked disabled here because CRL is referenced from mft */
- X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_IGNORE_CRITICAL);
+ X509_STORE_CTX_set_verify_cb(ctx, mft_verify_cb);
X509_STORE_CTX_set_depth(ctx, MAX_CERT_DEPTH);
X509_STORE_CTX_set0_trusted_stack(ctx, chain);
@@ -211,8 +308,8 @@ proc_parser_cert(const struct entity *en
if (!X509_STORE_CTX_init(ctx, NULL, x509, NULL))
cryptoerrx("X509_STORE_CTX_init");
- X509_STORE_CTX_set_flags(ctx,
- X509_V_FLAG_IGNORE_CRITICAL | X509_V_FLAG_CRL_CHECK);
+ X509_STORE_CTX_set_verify_cb(ctx, cert_verify_cb);
+ X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK);
X509_STORE_CTX_set_depth(ctx, MAX_CERT_DEPTH);
X509_STORE_CTX_set0_trusted_stack(ctx, chain);
X509_STORE_CTX_set0_crls(ctx, crls);
@@ -412,8 +509,8 @@ proc_parser_gbr(struct entity *entp, con
assert(x509 != NULL);
if (!X509_STORE_CTX_init(ctx, NULL, x509, NULL))
cryptoerrx("X509_STORE_CTX_init");
- X509_STORE_CTX_set_flags(ctx,
- X509_V_FLAG_IGNORE_CRITICAL | X509_V_FLAG_CRL_CHECK);
+ X509_STORE_CTX_set_verify_cb(ctx, gbr_verify_cb);
+ X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK);
X509_STORE_CTX_set_depth(ctx, MAX_CERT_DEPTH);
X509_STORE_CTX_set0_trusted_stack(ctx, chain);
X509_STORE_CTX_set0_crls(ctx, crls);