V1 certificates that aren't self signed can't be accepted as CAs.
[openssl.git] / crypto / x509v3 / v3_purp.c
index 30687119a30dc613fbffee4976fb652dac5837cc..a60d41bc243645aeabffc7e71534b13e632a66b5 100644 (file)
@@ -3,7 +3,7 @@
  * project 2001.
  */
 /* ====================================================================
- * Copyright (c) 1999-2001 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1999-2004 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -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);
@@ -140,7 +139,7 @@ int X509_PURPOSE_get_count(void)
 X509_PURPOSE * X509_PURPOSE_get0(int idx)
 {
        if(idx < 0) return NULL;
-       if(idx < X509_PURPOSE_COUNT) return xstandard + idx;
+       if(idx < (int)X509_PURPOSE_COUNT) return xstandard + idx;
        return sk_X509_PURPOSE_value(xptable, idx - X509_PURPOSE_COUNT);
 }
 
@@ -240,7 +239,7 @@ static void xptable_free(X509_PURPOSE *p)
 
 void X509_PURPOSE_cleanup(void)
 {
-       int i;
+       unsigned int i;
        sk_X509_PURPOSE_pop_free(xptable, xptable_free);
        for(i = 0; i < X509_PURPOSE_COUNT; i++) xptable_free(xstandard + i);
        xptable = NULL;
@@ -378,6 +377,10 @@ static void x509v3_cache_extensions(X509 *x)
                                case NID_time_stamp:
                                x->ex_xkusage |= XKU_TIMESTAMP;
                                break;
+
+                               case NID_dvcs:
+                               x->ex_xkusage |= XKU_DVCS;
+                               break;
                        }
                }
                sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free);
@@ -411,6 +414,7 @@ static void x509v3_cache_extensions(X509 *x)
  * 1 is a CA
  * 2 basicConstraints absent so "maybe" a CA
  * 3 basicConstraints absent but self signed V1.
+ * 4 basicConstraints absent but keyUsage present and keyCertSign asserted.
  */
 
 #define V1_ROOT (EXFLAG_V1|EXFLAG_SS)
@@ -421,7 +425,7 @@ static void x509v3_cache_extensions(X509 *x)
 #define ns_reject(x, usage) \
        (((x)->ex_flags & EXFLAG_NSCERT) && !((x)->ex_nscert & (usage)))
 
-static int ca_check(const X509 *x)
+static int check_ca(const X509 *x)
 {
        /* keyUsage if present should allow cert signing */
        if(ku_reject(x, KU_KEY_CERT_SIGN)) return 0;
@@ -430,25 +434,37 @@ static int ca_check(const X509 *x)
                /* 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 3;
-               else return 2;
+               else if (x->ex_flags & EXFLAG_KUSAGE) return 4;
+               /* Older certificates could have Netscape-specific CA types */
+               else if (x->ex_flags & EXFLAG_NSCERT
+                        && x->ex_nscert & NS_ANY_CA) return 5;
+               /* can this still be regarded a CA certificate?  I doubt it */
+               return 0;
+       }
+}
+
+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);
        }
+
+       return check_ca(x);
 }
 
 /* Check SSL CA: common checks for SSL client and server */
 static int check_ssl_ca(const X509 *x)
 {
        int ca_ret;
-       ca_ret = ca_check(x);
+       ca_ret = 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;
 }
 
@@ -493,14 +509,10 @@ static int purpose_smime(const X509 *x, int ca)
        if(xku_reject(x,XKU_SMIME)) return 0;
        if(ca) {
                int ca_ret;
-               ca_ret = ca_check(x);
+               ca_ret = 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) {
@@ -534,7 +546,7 @@ static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca)
 {
        if(ca) {
                int ca_ret;
-               if((ca_ret = ca_check(x)) != 2) return ca_ret;
+               if((ca_ret = check_ca(x)) != 2) return ca_ret;
                else return 0;
        }
        if(ku_reject(x, KU_CRL_SIGN)) return 0;
@@ -547,17 +559,9 @@ static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca)
 
 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 check_ca(x);
        /* leaf certificate is checked in OCSP_verify() */
        return 1;
 }
@@ -615,7 +619,7 @@ int X509_check_issued(X509 *issuer, X509 *subject)
                                        break;
                                }
                        }
-                       if(nm && X509_NAME_cmp(nm, X509_get_subject_name(issuer)))
+                       if(nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer)))
                                return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH;
                }
        }