PPC assembly pack update addendum.
[openssl.git] / crypto / x509v3 / pcy_tree.c
index 9ac169c00cfba42fe242d314fd78f2f56102f755..47b1bf8f27163d5ce3aa279804d9da1755f14fb2 100644 (file)
@@ -1,5 +1,5 @@
 /* pcy_tree.c */
-/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2004.
  */
 /* ====================================================================
 
 #include "pcy_int.h"
 
+/* Enable this to print out the complete policy tree at various point during
+ * evaluation.
+ */
+
+/*#define OPENSSL_POLICY_DEBUG*/
+
+#ifdef OPENSSL_POLICY_DEBUG
+
+static void expected_print(BIO *err, X509_POLICY_LEVEL *lev,
+                               X509_POLICY_NODE *node, int indent)
+       {
+       if (        (lev->flags & X509_V_FLAG_INHIBIT_MAP)
+               || !(node->data->flags & POLICY_DATA_FLAG_MAP_MASK))
+               BIO_puts(err, "  Not Mapped\n");
+       else
+               {
+               int i;
+               STACK_OF(ASN1_OBJECT) *pset = node->data->expected_policy_set;
+               ASN1_OBJECT *oid;
+               BIO_puts(err, "  Expected: ");
+               for (i = 0; i < sk_ASN1_OBJECT_num(pset); i++)
+                       {
+                       oid = sk_ASN1_OBJECT_value(pset, i);
+                       if (i)
+                               BIO_puts(err, ", ");
+                       i2a_ASN1_OBJECT(err, oid);
+                       }
+               BIO_puts(err, "\n");
+               }
+       }
+
+static void tree_print(char *str, X509_POLICY_TREE *tree,
+                       X509_POLICY_LEVEL *curr)
+       {
+       X509_POLICY_LEVEL *plev;
+       X509_POLICY_NODE *node;
+       int i;
+       BIO *err;
+       err = BIO_new_fp(stderr, BIO_NOCLOSE);
+       if (!curr)
+               curr = tree->levels + tree->nlevel;
+       else
+               curr++;
+       BIO_printf(err, "Level print after %s\n", str);
+       BIO_printf(err, "Printing Up to Level %ld\n", curr - tree->levels);
+       for (plev = tree->levels; plev != curr; plev++)
+               {
+               BIO_printf(err, "Level %ld, flags = %x\n",
+                               plev - tree->levels, plev->flags);
+               for (i = 0; i < sk_X509_POLICY_NODE_num(plev->nodes); i++)
+                       {
+                       node = sk_X509_POLICY_NODE_value(plev->nodes, i);
+                       X509_POLICY_NODE_print(err, node, 2);
+                       expected_print(err, plev, node, 2);
+                       BIO_printf(err, "  Flags: %x\n", node->data->flags);
+                       }
+               if (plev->anyPolicy)
+                       X509_POLICY_NODE_print(err, plev->anyPolicy, 2);
+               }
+
+       BIO_free(err);
+
+       }
+#else
+
+#define tree_print(a,b,c) /* */
+
+#endif
+
 /* Initialize policy tree. Return values:
- *  0 Some internal error occured.
+ *  0 Some internal error occurred.
  * -1 Inconsistent or invalid extensions in certificates.
  *  1 Tree initialized OK.
  *  2 Policy tree is empty.
@@ -87,8 +156,10 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
        *ptree = NULL;
        n = sk_X509_num(certs);
 
+#if 0
        /* Disable policy mapping for now... */
        flags |= X509_V_FLAG_INHIBIT_MAP;
+#endif
 
        if (flags & X509_V_FLAG_EXPLICIT_POLICY)
                explicit_policy = 0;
@@ -130,9 +201,9 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
                        ret = 2;
                if (explicit_policy > 0)
                        {
-                       explicit_policy--;
-                       if (!(x->ex_flags & EXFLAG_SS)
-                               && (cache->explicit_skip != -1)
+                       if (!(x->ex_flags & EXFLAG_SI))
+                               explicit_policy--;
+                       if ((cache->explicit_skip != -1)
                                && (cache->explicit_skip < explicit_policy))
                                explicit_policy = cache->explicit_skip;
                        }
@@ -160,7 +231,7 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
        tree->auth_policies = NULL;
        tree->user_policies = NULL;
 
-       if (!tree)
+       if (!tree->levels)
                {
                OPENSSL_free(tree);
                return 0;
@@ -184,7 +255,6 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
                level++;
                x = sk_X509_value(certs, i);
                cache = policy_cache_set(x);
-
                CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
                level->cert = x;
 
@@ -197,13 +267,14 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
                        /* Any matching allowed if certificate is self
                         * issued and not the last in the chain.
                         */
-                       if (!(x->ex_flags && EXFLAG_SS) || (i == 0))
+                       if (!(x->ex_flags & EXFLAG_SI) || (i == 0))
                                level->flags |= X509_V_FLAG_INHIBIT_ANY;
                        }
                else
                        {
-                       any_skip--;
-                       if ((cache->any_skip > 0)
+                       if (!(x->ex_flags & EXFLAG_SI))
+                               any_skip--;
+                       if ((cache->any_skip >= 0)
                                && (cache->any_skip < any_skip))
                                any_skip = cache->any_skip;
                        }
@@ -212,13 +283,13 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
                        level->flags |= X509_V_FLAG_INHIBIT_MAP;
                else
                        {
-                       map_skip--;
-                       if ((cache->map_skip > 0)
+                       if (!(x->ex_flags & EXFLAG_SI))
+                               map_skip--;
+                       if ((cache->map_skip >= 0)
                                && (cache->map_skip < map_skip))
                                map_skip = cache->map_skip;
                        }
 
-
                }
 
        *ptree = tree;
@@ -236,7 +307,32 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
 
        }
 
-/* This corresponds to RFC3280 XXXX XXXXX:
+static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
+                               const X509_POLICY_DATA *data)
+       {
+       X509_POLICY_LEVEL *last = curr - 1;
+       X509_POLICY_NODE *node;
+       int i, matched = 0;
+       /* Iterate through all in nodes linking matches */
+       for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++)
+               {
+               node = sk_X509_POLICY_NODE_value(last->nodes, i);
+               if (policy_node_match(last, node, data->valid_policy))
+                       {
+                       if (!level_add_node(curr, data, node, NULL))
+                               return 0;
+                       matched = 1;
+                       }
+               }
+       if (!matched && last->anyPolicy)
+               {
+               if (!level_add_node(curr, data, last->anyPolicy, NULL))
+                       return 0;
+               }
+       return 1;
+       }
+
+/* This corresponds to RFC3280 6.1.3(d)(1):
  * link any data from CertificatePolicies onto matching parent
  * or anyPolicy if no match.
  */
@@ -245,10 +341,8 @@ static int tree_link_nodes(X509_POLICY_LEVEL *curr,
                                const X509_POLICY_CACHE *cache)
        {
        int i;
-       X509_POLICY_LEVEL *last;
        X509_POLICY_DATA *data;
-       X509_POLICY_NODE *parent;
-       last = curr - 1;
+
        for (i = 0; i < sk_X509_POLICY_DATA_num(cache->data); i++)
                {
                data = sk_X509_POLICY_DATA_value(cache->data, i);
@@ -260,40 +354,109 @@ static int tree_link_nodes(X509_POLICY_LEVEL *curr,
                 * link because then it will have the mapping flags
                 * right and we can prune it later.
                 */
+#if 0
                if ((data->flags & POLICY_DATA_FLAG_MAPPED_ANY)
                        && !(curr->flags & X509_V_FLAG_INHIBIT_ANY))
                        continue;
-               /* Look for matching node in parent */
-               parent = level_find_node(last, data->valid_policy);
-               /* If no match link to anyPolicy */
-               if (!parent)
-                       parent = last->anyPolicy;
-               if (parent && !level_add_node(curr, data, parent, NULL))
+#endif
+               /* Look for matching nodes in previous level */
+               if (!tree_link_matching_nodes(curr, data))
                                return 0;
                }
        return 1;
        }
 
-/* This corresponds to RFC3280 XXXX XXXXX:
+/* This corresponds to RFC3280 6.1.3(d)(2):
  * Create new data for any unmatched policies in the parent and link
  * to anyPolicy.
  */
 
+static int tree_add_unmatched(X509_POLICY_LEVEL *curr,
+                       const X509_POLICY_CACHE *cache,
+                       const ASN1_OBJECT *id,
+                       X509_POLICY_NODE *node,
+                       X509_POLICY_TREE *tree)
+       {
+       X509_POLICY_DATA *data;
+       if (id == NULL)
+               id = node->data->valid_policy;
+       /* Create a new node with qualifiers from anyPolicy and
+        * id from unmatched node.
+        */
+       data = policy_data_new(NULL, id, node_critical(node));
+
+       if (data == NULL)
+               return 0;
+       /* Curr may not have anyPolicy */
+       data->qualifier_set = cache->anyPolicy->qualifier_set;
+       data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
+       if (!level_add_node(curr, data, node, tree))
+               {
+               policy_data_free(data);
+               return 0;
+               }
+
+       return 1;
+       }
+
+static int tree_link_unmatched(X509_POLICY_LEVEL *curr,
+                       const X509_POLICY_CACHE *cache,
+                       X509_POLICY_NODE *node,
+                       X509_POLICY_TREE *tree)
+       {
+       const X509_POLICY_LEVEL *last = curr - 1;
+       int i;
+
+       if (        (last->flags & X509_V_FLAG_INHIBIT_MAP)
+               || !(node->data->flags & POLICY_DATA_FLAG_MAPPED))
+               {
+               /* If no policy mapping: matched if one child present */
+               if (node->nchild)
+                       return 1;
+               if (!tree_add_unmatched(curr, cache, NULL, node, tree))
+                       return 0;
+               /* Add it */
+               }
+       else
+               {
+               /* If mapping: matched if one child per expected policy set */
+               STACK_OF(ASN1_OBJECT) *expset = node->data->expected_policy_set;
+               if (node->nchild == sk_ASN1_OBJECT_num(expset))
+                       return 1;
+               /* Locate unmatched nodes */
+               for (i = 0; i < sk_ASN1_OBJECT_num(expset); i++)
+                       {
+                       ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(expset, i);
+                       if (level_find_node(curr, node, oid))
+                               continue;
+                       if (!tree_add_unmatched(curr, cache, oid, node, tree))
+                               return 0;
+                       }
+
+               }
+
+       return 1;
+
+       }
+
 static int tree_link_any(X509_POLICY_LEVEL *curr,
                        const X509_POLICY_CACHE *cache,
                        X509_POLICY_TREE *tree)
        {
        int i;
-       X509_POLICY_DATA *data;
+       /*X509_POLICY_DATA *data;*/
        X509_POLICY_NODE *node;
-       X509_POLICY_LEVEL *last;
-
-       last = curr - 1;
+       X509_POLICY_LEVEL *last = curr - 1;
 
        for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++)
                {
                node = sk_X509_POLICY_NODE_value(last->nodes, i);
 
+               if (!tree_link_unmatched(curr, cache, node, tree))
+                       return 0;
+
+#if 0
+
                /* Skip any node with any children: we only want unmathced
                 * nodes.
                 *
@@ -302,6 +465,7 @@ static int tree_link_any(X509_POLICY_LEVEL *curr,
                 */
                if (node->nchild)
                        continue;
+
                /* Create a new node with qualifiers from anyPolicy and
                 * id from unmatched node.
                 */
@@ -310,13 +474,17 @@ static int tree_link_any(X509_POLICY_LEVEL *curr,
 
                if (data == NULL)
                        return 0;
-               data->qualifier_set = curr->anyPolicy->data->qualifier_set;
+               /* Curr may not have anyPolicy */
+               data->qualifier_set = cache->anyPolicy->qualifier_set;
                data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
                if (!level_add_node(curr, data, node, tree))
                        {
                        policy_data_free(data);
                        return 0;
                        }
+
+#endif
+
                }
        /* Finally add link to anyPolicy */
        if (last->anyPolicy)
@@ -335,30 +503,36 @@ static int tree_link_any(X509_POLICY_LEVEL *curr,
 
 static int tree_prune(X509_POLICY_TREE *tree, X509_POLICY_LEVEL *curr)
        {
+       STACK_OF(X509_POLICY_NODE) *nodes;
        X509_POLICY_NODE *node;
        int i;
-       for (i = sk_X509_POLICY_NODE_num(curr->nodes) - 1; i >= 0; i--)
+       nodes = curr->nodes;
+       if (curr->flags & X509_V_FLAG_INHIBIT_MAP)
                {
-               node = sk_X509_POLICY_NODE_value(curr->nodes, i);
-               /* Delete any mapped data: see RFC3280 XXXX */
-               if (node->data->flags & POLICY_DATA_FLAG_MAP_MASK)
+               for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--)
                        {
-                       node->parent->nchild--;
-                       OPENSSL_free(node);
-                       sk_X509_POLICY_NODE_delete(curr->nodes, i);
+                       node = sk_X509_POLICY_NODE_value(nodes, i);
+                       /* Delete any mapped data: see RFC3280 XXXX */
+                       if (node->data->flags & POLICY_DATA_FLAG_MAP_MASK)
+                               {
+                               node->parent->nchild--;
+                               OPENSSL_free(node);
+                               (void)sk_X509_POLICY_NODE_delete(nodes,i);
+                               }
                        }
                }
 
        for(;;) {
                --curr;
-               for (i = sk_X509_POLICY_NODE_num(curr->nodes) - 1; i >= 0; i--)
+               nodes = curr->nodes;
+               for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--)
                        {
-                       node = sk_X509_POLICY_NODE_value(curr->nodes, i);
+                       node = sk_X509_POLICY_NODE_value(nodes, i);
                        if (node->nchild == 0)
                                {
                                node->parent->nchild--;
                                OPENSSL_free(node);
-                               sk_X509_POLICY_NODE_delete(curr->nodes, i);
+                               (void)sk_X509_POLICY_NODE_delete(nodes, i);
                                }
                        }
                if (curr->anyPolicy && !curr->anyPolicy->nchild)
@@ -534,6 +708,7 @@ static int tree_evaluate(X509_POLICY_TREE *tree)
                if (!(curr->flags & X509_V_FLAG_INHIBIT_ANY)
                        && !tree_link_any(curr, cache, tree))
                        return 0;
+       tree_print("before tree_prune()", tree, curr);
                ret = tree_prune(tree, curr);
                if (ret != 1)
                        return ret;
@@ -589,7 +764,7 @@ void X509_policy_tree_free(X509_POLICY_TREE *tree)
  * -2  User constrained policy set empty and requireExplicit true.
  */
 
-int X509_policy_check(X509_POLICY_TREE **ptree, int *explicit,
+int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy,
                        STACK_OF(X509) *certs,
                        STACK_OF(ASN1_OBJECT) *policy_oids,
                        unsigned int flags)
@@ -599,10 +774,9 @@ int X509_policy_check(X509_POLICY_TREE **ptree, int *explicit,
        STACK_OF(X509_POLICY_NODE) *nodes, *auth_nodes = NULL;
        *ptree = NULL;
 
-       *explicit = 0;
+       *pexplicit_policy = 0;
        ret = tree_init(&tree, certs, flags);
 
-
        switch (ret)
                {
 
@@ -610,6 +784,10 @@ int X509_policy_check(X509_POLICY_TREE **ptree, int *explicit,
                case 2:
                return 1;
 
+               /* Some internal error */
+               case -1:
+               return -1;
+
                /* Some internal error */
                case 0:
                return 0;
@@ -617,22 +795,35 @@ int X509_policy_check(X509_POLICY_TREE **ptree, int *explicit,
                /* Tree empty requireExplicit True: Error */
 
                case 6:
-               *explicit = 1;
+               *pexplicit_policy = 1;
                return -2;
 
                /* Tree OK requireExplicit True: OK and continue */
                case 5:
-               *explicit = 1;
+               *pexplicit_policy = 1;
                break;
 
                /* Tree OK: continue */
 
                case 1:
+               if (!tree)
+                       /*
+                        * tree_init() returns success and a null tree
+                        * if it's just looking at a trust anchor.
+                        * I'm not sure that returning success here is
+                        * correct, but I'm sure that reporting this
+                        * as an internal error which our caller
+                        * interprets as a malloc failure is wrong.
+                        */
+                       return 1;
                break;
                }
 
+       if (!tree) goto error;
        ret = tree_evaluate(tree);
 
+       tree_print("tree_evaluate()", tree, NULL);
+
        if (ret <= 0)
                goto error;
 
@@ -640,7 +831,7 @@ int X509_policy_check(X509_POLICY_TREE **ptree, int *explicit,
        if (ret == 2)
                {
                X509_policy_tree_free(tree);
-               if (*explicit)
+               if (*pexplicit_policy)
                        return -2;
                else
                        return 1;
@@ -662,7 +853,7 @@ int X509_policy_check(X509_POLICY_TREE **ptree, int *explicit,
        if (tree)
                *ptree = tree;
 
-       if (*explicit)
+       if (*pexplicit_policy)
                {
                nodes = X509_policy_tree_get0_user_policies(tree);
                if (sk_X509_POLICY_NODE_num(nodes) <= 0)