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);

Reply via email to