On Wed, Dec 29, 2021 at 01:06:30AM +0100, Theo Buehler wrote:
> 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.

Absolutly. Idealy we can pass the path to rpki-client -f in the future to
get a verbose output of the object in question.
 
> 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.

Not too worried about those errors, I doubt the will be seen often.
Lets decide if someone actually runs into those.
 
> 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 think the verbose > 1 warnx could indeed be removed. With modern libs it
will be never hit and for old libs the code takes care of this.
Would simplify the code a bit more.
 
> 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?

I really need to try this on a system with old libressl. Will hopefully
manage to do that today. Apart from that I'm OK with this going in.
We can cleanup the last bits in tree.
 
> 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);
> 

-- 
:wq Claudio

Reply via email to