On Tue, Dec 28, 2021 at 05:08:46PM +0100, Claudio Jeker wrote: > On Mon, Dec 27, 2021 at 12:23:32PM +0100, Theo Buehler wrote: > > 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. > > I like this. A few questions inline.
cool > > + fprintf(stderr, "%s cb: ok %d, %s, depth %d\n", > > + descr, ok, X509_verify_cert_error_string(error), depth); > > Is there a reason you decided to use fprintf() over warnx() here? Only that I didn't really intend this for commit and was therefore a bit lazy. I should have made that clearer. > And especially for the error cases below. AFAIK rpki-client uses warnx() > in almost all such cases. > Also we normally print the path of the file causing the error but I guess > this is tricky to pass in here. I guess one could use > X509_STORE_CTX_set_app_data() for that. Good idea, yes, that works. > > +static int > > +roa_verify_cb(int ok, X509_STORE_CTX *store_ctx) > > +{ > > + return generic_verify(ok, store_ctx, "roa"); > > +} > > Is there no other way to avoid this extra wrapping of functions? It was the first thing that came to mind so I could tell where the calls originated. > See above I guess using X509_STORE_CTX_set_app_data() and > X509_STORE_CTX_get_app_data() these wrappers become unnecessary. I think the suffix of the file gives enough of a clue, so that's definitely no longer needed. This should be closer to commit now. I removed the switch over the error since I guess we would need to handle unhandled critical CRL extensions differently. I switched the fprintf to warnx, although some more of them should perhaps be fatal since it's most likely an allocation failure. Since this is super noisy (multiple lines per processed file), I reduced to printing one line in the ordinary path and only on verbosity > 1. We might even want to crank that higher or ditch this last warnx altogether. I also suppressed printing the cert error and the ok variable since I don't think this is actually interesting, whereas the depth should give a valuable clue where to look next, so I kept it. ok? 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 28 Dec 2021 23:34:41 -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,75 @@ static X509_STORE_CTX *ctx; static struct auth_tree auths = RB_INITIALIZER(&auths); static struct crl_tree crlt = RB_INITIALIZER(&crlt); +static int +verify_cb(int ok, X509_STORE_CTX *store_ctx) +{ + X509 *cert; + const STACK_OF(X509_EXTENSION) *exts; + X509_EXTENSION *ext; + ASN1_OBJECT *obj; + char *file; + 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); + + if (error != X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) + return ok; + + if ((file = X509_STORE_CTX_get_app_data(store_ctx)) == NULL) + cryptoerrx("X509_STORE_CTX_get_app_data"); + + if ((cert = X509_STORE_CTX_get_current_cert(store_ctx)) == NULL) { + warnx("%s: got no current cert", file); + return 0; + } + if ((exts = X509_get0_extensions(cert)) == NULL) { + warnx("%s: got no cert extensions", file); + 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) { + warnx("%s: got no extension object", file); + 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: + warnx("%s: depth %d: unknown extension: nid %d", + file, depth, nid); + saw_unknown = 1; + break; + } + } + + if (verbose > 1) + warnx("%s: depth %d, ipAddrBlock %d, autonomousSysNum %d", + file, depth, saw_ipAddrBlock, saw_autonomousSysNum); + + /* Fail if we saw an unknown extension. */ + return !saw_unknown; +} + /* * Parse and validate a ROA. * This is standard stuff. @@ -72,8 +142,10 @@ 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, verify_cb); + if (!X509_STORE_CTX_set_app_data(ctx, entp->file)) + cryptoerrx("X509_STORE_CTX_set_app_data"); + 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); @@ -150,8 +222,10 @@ proc_parser_mft(struct entity *entp, con if (!X509_STORE_CTX_init(ctx, NULL, x509, NULL)) 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); + /* CRL checks disabled here because CRL is referenced from mft */ + X509_STORE_CTX_set_verify_cb(ctx, verify_cb); + if (!X509_STORE_CTX_set_app_data(ctx, entp->file)) + cryptoerrx("X509_STORE_CTX_set_app_data"); X509_STORE_CTX_set_depth(ctx, MAX_CERT_DEPTH); X509_STORE_CTX_set0_trusted_stack(ctx, chain); @@ -211,8 +285,10 @@ 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, verify_cb); + if (!X509_STORE_CTX_set_app_data(ctx, entp->file)) + cryptoerrx("X509_STORE_CTX_set_app_data"); + 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 +488,10 @@ 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, verify_cb); + if (!X509_STORE_CTX_set_app_data(ctx, entp->file)) + cryptoerrx("X509_STORE_CTX_set_app_data"); + 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);