In message <[EMAIL PROTECTED]> on Thu, 18 Nov 2004 09:30:41 +0100 (CET), Richard Levitte - VMS Whacker <[EMAIL PROTECTED]> said:
richard> OK, that's actually quite easy, I'll have a patch prepared richard> for review within an hour or so. I've got something that seems to work for my purposes. Do you have a collection of certificates to try this on? The weirder (within a realistic framework) the better, I'd say. I changed ca_check() into a publically available function, and had it do a bit more thorough checks on the nsCertType values to determine if the certificate could be a CA certificate or not. Oh, and please, if anyone *other* than Steve is interested in this, please jump in and test! I've made the changes against the 0.9.7 branch, and I don;t expect it to be difficult to apply to 0.9.8-dev. Cheers, Richard ----- Please consider sponsoring my work on free software. See http://www.free.lp.se/sponsoring.html for details. -- Richard Levitte [EMAIL PROTECTED] http://richard.levitte.org/ "When I became a man I put away childish things, including the fear of childishness and the desire to be very grown up." -- C.S. Lewis
? crypto/x509/Makefile.flc ? crypto/x509/semantic.cache ? crypto/x509/x509.h.flc ? crypto/x509/x509_vfy.c.flc ? crypto/x509v3/Makefile.flc ? crypto/x509v3/semantic.cache Index: apps/verify.c =================================================================== RCS file: /e/openssl/cvs/openssl/apps/verify.c,v retrieving revision 1.26.2.4 diff -u -r1.26.2.4 verify.c --- apps/verify.c 30 Jan 2003 17:37:36 -0000 1.26.2.4 +++ apps/verify.c 18 Nov 2004 12:00:14 -0000 @@ -354,6 +354,7 @@ if (ctx->error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ok=1; /* Continue after extension errors too */ if (ctx->error == X509_V_ERR_INVALID_CA) ok=1; + if (ctx->error == X509_V_ERR_INVALID_NON_CA) ok=1; if (ctx->error == X509_V_ERR_PATH_LENGTH_EXCEEDED) ok=1; if (ctx->error == X509_V_ERR_INVALID_PURPOSE) ok=1; if (ctx->error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ok=1; Index: crypto/x509/x509_txt.c =================================================================== RCS file: /e/openssl/cvs/openssl/crypto/x509/x509_txt.c,v retrieving revision 1.13.2.2 diff -u -r1.13.2.2 x509_txt.c --- crypto/x509/x509_txt.c 5 Mar 2004 17:16:05 -0000 1.13.2.2 +++ crypto/x509/x509_txt.c 18 Nov 2004 12:00:14 -0000 @@ -122,6 +122,8 @@ return("certificate revoked"); case X509_V_ERR_INVALID_CA: return ("invalid CA certificate"); + case X509_V_ERR_INVALID_NON_CA: + return ("invalid non-CA certificate (has CA markings)"); case X509_V_ERR_PATH_LENGTH_EXCEEDED: return ("path length constraint exceeded"); case X509_V_ERR_INVALID_PURPOSE: Index: crypto/x509/x509_vfy.c =================================================================== RCS file: /e/openssl/cvs/openssl/crypto/x509/x509_vfy.c,v retrieving revision 1.56.2.10 diff -u -r1.56.2.10 x509_vfy.c --- crypto/x509/x509_vfy.c 4 Oct 2004 16:27:35 -0000 1.56.2.10 +++ crypto/x509/x509_vfy.c 18 Nov 2004 12:00:14 -0000 @@ -73,7 +73,7 @@ static int null_callback(int ok,X509_STORE_CTX *e); static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); -static int check_chain_purpose(X509_STORE_CTX *ctx); +static int check_chain_extensions(X509_STORE_CTX *ctx); static int check_trust(X509_STORE_CTX *ctx); static int check_revocation(X509_STORE_CTX *ctx); static int check_cert(X509_STORE_CTX *ctx); @@ -281,7 +281,7 @@ } /* We have the chain complete: now we need to check its purpose */ - if (ctx->purpose > 0) ok = check_chain_purpose(ctx); + ok = check_chain_extensions(ctx); if (!ok) goto end; @@ -371,15 +371,25 @@ * with the supplied purpose */ -static int check_chain_purpose(X509_STORE_CTX *ctx) +static int check_chain_extensions(X509_STORE_CTX *ctx) { #ifdef OPENSSL_NO_CHAIN_VERIFY return 1; #else - int i, ok=0; + int i, ok=0, must_be_ca; X509 *x; int (*cb)(); cb=ctx->verify_cb; + + /* must_be_ca can have 1 of 3 values: + -1: we accept both CA and non-CA certificates, to allow direct + use of self-signed certificates (which are marked as CA). + 0: we only accept non-CA certificates. This is currently not + used, but the possibility is present for future extensions. + 1: we only accept CA certificates. This is currently used for + all certificates in the chain except the leaf certificate. + */ + must_be_ca = -1; /* Check all untrusted certificates */ for (i = 0; i < ctx->last_untrusted; i++) { @@ -394,20 +404,61 @@ ok=cb(0,ctx); if (!ok) goto end; } - ret = X509_check_purpose(x, ctx->purpose, i); - if ((ret == 0) - || ((ctx->flags & X509_V_FLAG_X509_STRICT) - && (ret != 1))) + ret = X509_check_ca(x); + switch(must_be_ca) { - if (i) + case -1: + if ((ctx->flags & X509_V_FLAG_X509_STRICT) + && (ret != 1) && (ret != 0)) + { + ret = 0; ctx->error = X509_V_ERR_INVALID_CA; + } else - ctx->error = X509_V_ERR_INVALID_PURPOSE; + ret = 1; + break; + case 0: + if (ret != 0) + { + ret = 0; + ctx->error = X509_V_ERR_INVALID_NON_CA; + } + else + ret = 1; + break; + default: + if ((ret == 0) + || ((ctx->flags & X509_V_FLAG_X509_STRICT) + && (ret != 1))) + { + ret = 0; + ctx->error = X509_V_ERR_INVALID_CA; + } + else + ret = 1; + break; + } + if (ret == 0) + { ctx->error_depth = i; ctx->current_cert = x; ok=cb(0,ctx); if (!ok) goto end; } + if (ctx->purpose > 0) + { + ret = X509_check_purpose(x, ctx->purpose, i); + if ((ret == 0) + || ((ctx->flags & X509_V_FLAG_X509_STRICT) + && (ret != 1))) + { + ctx->error = X509_V_ERR_INVALID_PURPOSE; + ctx->error_depth = i; + ctx->current_cert = x; + ok=cb(0,ctx); + if (!ok) goto end; + } + } /* Check pathlen */ if ((i > 1) && (x->ex_pathlen != -1) && (i > (x->ex_pathlen + 1))) @@ -418,6 +469,8 @@ ok=cb(0,ctx); if (!ok) goto end; } + /* The next certificate must be a CA */ + must_be_ca = 1; } ok = 1; end: Index: crypto/x509/x509_vfy.h =================================================================== RCS file: /e/openssl/cvs/openssl/crypto/x509/x509_vfy.h,v retrieving revision 1.43.2.1 diff -u -r1.43.2.1 x509_vfy.h --- crypto/x509/x509_vfy.h 5 Mar 2004 17:16:05 -0000 1.43.2.1 +++ crypto/x509/x509_vfy.h 18 Nov 2004 12:00:15 -0000 @@ -306,6 +306,7 @@ #define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 #define X509_V_ERR_KEYUSAGE_NO_CRL_SIGN 35 #define X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION 36 +#define X509_V_ERR_INVALID_NON_CA 37 /* The application is not happy */ #define X509_V_ERR_APPLICATION_VERIFICATION 50 Index: crypto/x509v3/v3_purp.c =================================================================== RCS file: /e/openssl/cvs/openssl/crypto/x509v3/v3_purp.c,v retrieving revision 1.26.2.2 diff -u -r1.26.2.2 v3_purp.c --- crypto/x509v3/v3_purp.c 5 Mar 2004 17:16:06 -0000 1.26.2.2 +++ crypto/x509v3/v3_purp.c 18 Nov 2004 12:00:15 -0000 @@ -63,7 +63,6 @@ static void x509v3_cache_extensions(X509 *x); -static int ca_check(const X509 *x); static int check_ssl_ca(const X509 *x); static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, int ca); static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca); @@ -426,8 +425,14 @@ #define ns_reject(x, usage) \ (((x)->ex_flags & EXFLAG_NSCERT) && !((x)->ex_nscert & (usage))) -static int ca_check(const X509 *x) +int X509_check_ca(X509 *x) { + if(!(x->ex_flags & EXFLAG_SET)) { + CRYPTO_w_lock(CRYPTO_LOCK_X509); + x509v3_cache_extensions(x); + CRYPTO_w_unlock(CRYPTO_LOCK_X509); + } + /* keyUsage if present should allow cert signing */ if(ku_reject(x, KU_KEY_CERT_SIGN)) return 0; if(x->ex_flags & EXFLAG_BCONS) { @@ -435,10 +440,17 @@ /* If basicConstraints says not a CA then say so */ else return 0; } else { + /* we support V1 roots for... uh, I don't really know why. */ if((x->ex_flags & V1_ROOT) == V1_ROOT) return 3; /* If key usage present it must have certSign so tolerate it */ else if (x->ex_flags & EXFLAG_KUSAGE) return 4; - else return 2; + /* Older certificates could have Netscape-specific CA types */ + else if (x->ex_flags & EXFLAG_NSCERT + && x->ex_nscert & NS_ANY_CA) return 5; + /* 2 means "I don't know...", which is legal for V1 and V2 */ + else if (x->ex_flags & EXFLAG_V1) return 2; + /* can this still be regarded a CA certificate? I doubt it */ + return 0; } } @@ -446,14 +458,10 @@ static int check_ssl_ca(const X509 *x) { int ca_ret; - ca_ret = ca_check(x); + ca_ret = X509_check_ca(x); if(!ca_ret) return 0; /* check nsCertType if present */ - if(x->ex_flags & EXFLAG_NSCERT) { - if(x->ex_nscert & NS_SSL_CA) return ca_ret; - return 0; - } - if(ca_ret != 2) return ca_ret; + if(ca_ret != 5 || x->ex_nscert & NS_SSL_CA) return ca_ret; else return 0; } @@ -498,14 +506,10 @@ if(xku_reject(x,XKU_SMIME)) return 0; if(ca) { int ca_ret; - ca_ret = ca_check(x); + ca_ret = X509_check_ca(x); if(!ca_ret) return 0; /* check nsCertType if present */ - if(x->ex_flags & EXFLAG_NSCERT) { - if(x->ex_nscert & NS_SMIME_CA) return ca_ret; - return 0; - } - if(ca_ret != 2) return ca_ret; + if(ca_ret != 5 || x->ex_nscert & NS_SMIME_CA) return ca_ret; else return 0; } if(x->ex_flags & EXFLAG_NSCERT) { @@ -539,7 +543,7 @@ { if(ca) { int ca_ret; - if((ca_ret = ca_check(x)) != 2) return ca_ret; + if((ca_ret = X509_check_ca(x)) != 2) return ca_ret; else return 0; } if(ku_reject(x, KU_CRL_SIGN)) return 0; @@ -552,17 +556,9 @@ static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca) { - /* Must be a valid CA */ - if(ca) { - int ca_ret; - ca_ret = ca_check(x); - if(ca_ret != 2) return ca_ret; - if(x->ex_flags & EXFLAG_NSCERT) { - if(x->ex_nscert & NS_ANY_CA) return ca_ret; - return 0; - } - return 0; - } + /* Must be a valid CA. Should we really support the "I don't know" + value (2)? */ + if(ca) return X509_check_ca(x); /* leaf certificate is checked in OCSP_verify() */ return 1; } Index: crypto/x509v3/x509v3.h =================================================================== RCS file: /e/openssl/cvs/openssl/crypto/x509v3/x509v3.h,v retrieving revision 1.83.2.1 diff -u -r1.83.2.1 x509v3.h --- crypto/x509v3/x509v3.h 29 Jan 2003 15:06:38 -0000 1.83.2.1 +++ crypto/x509v3/x509v3.h 18 Nov 2004 12:00:15 -0000 @@ -527,6 +527,7 @@ int X509V3_extensions_print(BIO *out, char *title, STACK_OF(X509_EXTENSION) *exts, unsigned long flag, int indent); +int X509_check_ca(X509 *x); int X509_check_purpose(X509 *x, int id, int ca); int X509_supported_extension(X509_EXTENSION *ex); int X509_PURPOSE_set(int *p, int purpose);