|  | /* pcy_tree.c */ | 
|  | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL | 
|  | * project 2004. | 
|  | */ | 
|  | /* ==================================================================== | 
|  | * Copyright (c) 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 | 
|  | * are met: | 
|  | * | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in | 
|  | *    the documentation and/or other materials provided with the | 
|  | *    distribution. | 
|  | * | 
|  | * 3. All advertising materials mentioning features or use of this | 
|  | *    software must display the following acknowledgment: | 
|  | *    "This product includes software developed by the OpenSSL Project | 
|  | *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" | 
|  | * | 
|  | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to | 
|  | *    endorse or promote products derived from this software without | 
|  | *    prior written permission. For written permission, please contact | 
|  | *    licensing@OpenSSL.org. | 
|  | * | 
|  | * 5. Products derived from this software may not be called "OpenSSL" | 
|  | *    nor may "OpenSSL" appear in their names without prior written | 
|  | *    permission of the OpenSSL Project. | 
|  | * | 
|  | * 6. Redistributions of any form whatsoever must retain the following | 
|  | *    acknowledgment: | 
|  | *    "This product includes software developed by the OpenSSL Project | 
|  | *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY | 
|  | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR | 
|  | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
|  | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | 
|  | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
|  | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | 
|  | * OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | * ==================================================================== | 
|  | * | 
|  | * This product includes cryptographic software written by Eric Young | 
|  | * (eay@cryptsoft.com).  This product includes software written by Tim | 
|  | * Hudson (tjh@cryptsoft.com). | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "cryptlib.h" | 
|  | #include <openssl/x509.h> | 
|  | #include <openssl/x509v3.h> | 
|  |  | 
|  | #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. | 
|  | * -1 Inconsistent or invalid extensions in certificates. | 
|  | *  1 Tree initialized OK. | 
|  | *  2 Policy tree is empty. | 
|  | *  5 Tree OK and requireExplicitPolicy true. | 
|  | *  6 Tree empty and requireExplicitPolicy true. | 
|  | */ | 
|  |  | 
|  | static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs, | 
|  | unsigned int flags) | 
|  | { | 
|  | X509_POLICY_TREE *tree; | 
|  | X509_POLICY_LEVEL *level; | 
|  | const X509_POLICY_CACHE *cache; | 
|  | X509_POLICY_DATA *data = NULL; | 
|  | X509 *x; | 
|  | int ret = 1; | 
|  | int i, n; | 
|  | int explicit_policy; | 
|  | int any_skip; | 
|  | int map_skip; | 
|  | *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; | 
|  | else | 
|  | explicit_policy = n + 1; | 
|  |  | 
|  | if (flags & X509_V_FLAG_INHIBIT_ANY) | 
|  | any_skip = 0; | 
|  | else | 
|  | any_skip = n + 1; | 
|  |  | 
|  | if (flags & X509_V_FLAG_INHIBIT_MAP) | 
|  | map_skip = 0; | 
|  | else | 
|  | map_skip = n + 1; | 
|  |  | 
|  | /* Can't do anything with just a trust anchor */ | 
|  | if (n == 1) | 
|  | return 1; | 
|  | /* First setup policy cache in all certificates apart from the | 
|  | * trust anchor. Note any bad cache results on the way. Also can | 
|  | * calculate explicit_policy value at this point. | 
|  | */ | 
|  | for (i = n - 2; i >= 0; i--) | 
|  | { | 
|  | x = sk_X509_value(certs, i); | 
|  | X509_check_purpose(x, -1, -1); | 
|  | cache = policy_cache_set(x); | 
|  | /* If cache NULL something bad happened: return immediately */ | 
|  | if (cache == NULL) | 
|  | return 0; | 
|  | /* If inconsistent extensions keep a note of it but continue */ | 
|  | if (x->ex_flags & EXFLAG_INVALID_POLICY) | 
|  | ret = -1; | 
|  | /* Otherwise if we have no data (hence no CertificatePolicies) | 
|  | * and haven't already set an inconsistent code note it. | 
|  | */ | 
|  | else if ((ret == 1) && !cache->data) | 
|  | ret = 2; | 
|  | if (explicit_policy > 0) | 
|  | { | 
|  | if (!(x->ex_flags & EXFLAG_SI)) | 
|  | explicit_policy--; | 
|  | if ((cache->explicit_skip != -1) | 
|  | && (cache->explicit_skip < explicit_policy)) | 
|  | explicit_policy = cache->explicit_skip; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ret != 1) | 
|  | { | 
|  | if (ret == 2 && !explicit_policy) | 
|  | return 6; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* If we get this far initialize the tree */ | 
|  |  | 
|  | tree = OPENSSL_malloc(sizeof(X509_POLICY_TREE)); | 
|  |  | 
|  | if (!tree) | 
|  | return 0; | 
|  |  | 
|  | tree->flags = 0; | 
|  | tree->levels = OPENSSL_malloc(sizeof(X509_POLICY_LEVEL) * n); | 
|  | tree->nlevel = 0; | 
|  | tree->extra_data = NULL; | 
|  | tree->auth_policies = NULL; | 
|  | tree->user_policies = NULL; | 
|  |  | 
|  | if (!tree) | 
|  | { | 
|  | OPENSSL_free(tree); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | memset(tree->levels, 0, n * sizeof(X509_POLICY_LEVEL)); | 
|  |  | 
|  | tree->nlevel = n; | 
|  |  | 
|  | level = tree->levels; | 
|  |  | 
|  | /* Root data: initialize to anyPolicy */ | 
|  |  | 
|  | data = policy_data_new(NULL, OBJ_nid2obj(NID_any_policy), 0); | 
|  |  | 
|  | if (!data || !level_add_node(level, data, NULL, tree)) | 
|  | goto bad_tree; | 
|  |  | 
|  | for (i = n - 2; i >= 0; i--) | 
|  | { | 
|  | level++; | 
|  | x = sk_X509_value(certs, i); | 
|  | cache = policy_cache_set(x); | 
|  | CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); | 
|  | level->cert = x; | 
|  |  | 
|  | if (!cache->anyPolicy) | 
|  | level->flags |= X509_V_FLAG_INHIBIT_ANY; | 
|  |  | 
|  | /* Determine inhibit any and inhibit map flags */ | 
|  | if (any_skip == 0) | 
|  | { | 
|  | /* Any matching allowed if certificate is self | 
|  | * issued and not the last in the chain. | 
|  | */ | 
|  | if (!(x->ex_flags & EXFLAG_SI) || (i == 0)) | 
|  | level->flags |= X509_V_FLAG_INHIBIT_ANY; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (!(x->ex_flags & EXFLAG_SI)) | 
|  | any_skip--; | 
|  | if ((cache->any_skip >= 0) | 
|  | && (cache->any_skip < any_skip)) | 
|  | any_skip = cache->any_skip; | 
|  | } | 
|  |  | 
|  | if (map_skip == 0) | 
|  | level->flags |= X509_V_FLAG_INHIBIT_MAP; | 
|  | else | 
|  | { | 
|  | 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; | 
|  |  | 
|  | if (explicit_policy) | 
|  | return 1; | 
|  | else | 
|  | return 5; | 
|  |  | 
|  | bad_tree: | 
|  |  | 
|  | X509_policy_tree_free(tree); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | } | 
|  |  | 
|  | 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. | 
|  | */ | 
|  |  | 
|  | static int tree_link_nodes(X509_POLICY_LEVEL *curr, | 
|  | const X509_POLICY_CACHE *cache) | 
|  | { | 
|  | int i; | 
|  | X509_POLICY_LEVEL *last; | 
|  | X509_POLICY_DATA *data; | 
|  | last = curr - 1; | 
|  | for (i = 0; i < sk_X509_POLICY_DATA_num(cache->data); i++) | 
|  | { | 
|  | data = sk_X509_POLICY_DATA_value(cache->data, i); | 
|  | /* If a node is mapped any it doesn't have a corresponding | 
|  | * CertificatePolicies entry. | 
|  | * However such an identical node would be created | 
|  | * if anyPolicy matching is enabled because there would be | 
|  | * no match with the parent valid_policy_set. So we create | 
|  | * 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; | 
|  | #endif | 
|  | /* Look for matching nodes in previous level */ | 
|  | if (!tree_link_matching_nodes(curr, data)) | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* 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_NODE *node; | 
|  | 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. | 
|  | * | 
|  | * Note: need something better for policy mapping | 
|  | * because each node may have multiple children | 
|  | */ | 
|  | if (node->nchild) | 
|  | continue; | 
|  |  | 
|  | /* Create a new node with qualifiers from anyPolicy and | 
|  | * id from unmatched node. | 
|  | */ | 
|  | data = policy_data_new(NULL, node->data->valid_policy, | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | } | 
|  | /* Finally add link to anyPolicy */ | 
|  | if (last->anyPolicy) | 
|  | { | 
|  | if (!level_add_node(curr, cache->anyPolicy, | 
|  | last->anyPolicy, NULL)) | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Prune the tree: delete any child mapped child data on the current level | 
|  | * then proceed up the tree deleting any data with no children. If we ever | 
|  | * have no data on a level we can halt because the tree will be empty. | 
|  | */ | 
|  |  | 
|  | static int tree_prune(X509_POLICY_TREE *tree, X509_POLICY_LEVEL *curr) | 
|  | { | 
|  | STACK_OF(X509_POLICY_NODE) *nodes; | 
|  | X509_POLICY_NODE *node; | 
|  | int i; | 
|  | nodes = curr->nodes; | 
|  | if (curr->flags & X509_V_FLAG_INHIBIT_MAP) | 
|  | { | 
|  | for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; 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; | 
|  | nodes = curr->nodes; | 
|  | for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--) | 
|  | { | 
|  | node = sk_X509_POLICY_NODE_value(nodes, i); | 
|  | if (node->nchild == 0) | 
|  | { | 
|  | node->parent->nchild--; | 
|  | OPENSSL_free(node); | 
|  | (void)sk_X509_POLICY_NODE_delete(nodes, i); | 
|  | } | 
|  | } | 
|  | if (curr->anyPolicy && !curr->anyPolicy->nchild) | 
|  | { | 
|  | if (curr->anyPolicy->parent) | 
|  | curr->anyPolicy->parent->nchild--; | 
|  | OPENSSL_free(curr->anyPolicy); | 
|  | curr->anyPolicy = NULL; | 
|  | } | 
|  | if (curr == tree->levels) | 
|  | { | 
|  | /* If we zapped anyPolicy at top then tree is empty */ | 
|  | if (!curr->anyPolicy) | 
|  | return 2; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 1; | 
|  |  | 
|  | } | 
|  |  | 
|  | static int tree_add_auth_node(STACK_OF(X509_POLICY_NODE) **pnodes, | 
|  | X509_POLICY_NODE *pcy) | 
|  | { | 
|  | if (!*pnodes) | 
|  | { | 
|  | *pnodes = policy_node_cmp_new(); | 
|  | if (!*pnodes) | 
|  | return 0; | 
|  | } | 
|  | else if (sk_X509_POLICY_NODE_find(*pnodes, pcy) != -1) | 
|  | return 1; | 
|  |  | 
|  | if (!sk_X509_POLICY_NODE_push(*pnodes, pcy)) | 
|  | return 0; | 
|  |  | 
|  | return 1; | 
|  |  | 
|  | } | 
|  |  | 
|  | /* Calculate the authority set based on policy tree. | 
|  | * The 'pnodes' parameter is used as a store for the set of policy nodes | 
|  | * used to calculate the user set. If the authority set is not anyPolicy | 
|  | * then pnodes will just point to the authority set. If however the authority | 
|  | * set is anyPolicy then the set of valid policies (other than anyPolicy) | 
|  | * is store in pnodes. The return value of '2' is used in this case to indicate | 
|  | * that pnodes should be freed. | 
|  | */ | 
|  |  | 
|  | static int tree_calculate_authority_set(X509_POLICY_TREE *tree, | 
|  | STACK_OF(X509_POLICY_NODE) **pnodes) | 
|  | { | 
|  | X509_POLICY_LEVEL *curr; | 
|  | X509_POLICY_NODE *node, *anyptr; | 
|  | STACK_OF(X509_POLICY_NODE) **addnodes; | 
|  | int i, j; | 
|  | curr = tree->levels + tree->nlevel - 1; | 
|  |  | 
|  | /* If last level contains anyPolicy set is anyPolicy */ | 
|  | if (curr->anyPolicy) | 
|  | { | 
|  | if (!tree_add_auth_node(&tree->auth_policies, curr->anyPolicy)) | 
|  | return 0; | 
|  | addnodes = pnodes; | 
|  | } | 
|  | else | 
|  | /* Add policies to authority set */ | 
|  | addnodes = &tree->auth_policies; | 
|  |  | 
|  | curr = tree->levels; | 
|  | for (i = 1; i < tree->nlevel; i++) | 
|  | { | 
|  | /* If no anyPolicy node on this this level it can't | 
|  | * appear on lower levels so end search. | 
|  | */ | 
|  | if (!(anyptr = curr->anyPolicy)) | 
|  | break; | 
|  | curr++; | 
|  | for (j = 0; j < sk_X509_POLICY_NODE_num(curr->nodes); j++) | 
|  | { | 
|  | node = sk_X509_POLICY_NODE_value(curr->nodes, j); | 
|  | if ((node->parent == anyptr) | 
|  | && !tree_add_auth_node(addnodes, node)) | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (addnodes == pnodes) | 
|  | return 2; | 
|  |  | 
|  | *pnodes = tree->auth_policies; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int tree_calculate_user_set(X509_POLICY_TREE *tree, | 
|  | STACK_OF(ASN1_OBJECT) *policy_oids, | 
|  | STACK_OF(X509_POLICY_NODE) *auth_nodes) | 
|  | { | 
|  | int i; | 
|  | X509_POLICY_NODE *node; | 
|  | ASN1_OBJECT *oid; | 
|  |  | 
|  | X509_POLICY_NODE *anyPolicy; | 
|  | X509_POLICY_DATA *extra; | 
|  |  | 
|  | /* Check if anyPolicy present in authority constrained policy set: | 
|  | * this will happen if it is a leaf node. | 
|  | */ | 
|  |  | 
|  | if (sk_ASN1_OBJECT_num(policy_oids) <= 0) | 
|  | return 1; | 
|  |  | 
|  | anyPolicy = tree->levels[tree->nlevel - 1].anyPolicy; | 
|  |  | 
|  | for (i = 0; i < sk_ASN1_OBJECT_num(policy_oids); i++) | 
|  | { | 
|  | oid = sk_ASN1_OBJECT_value(policy_oids, i); | 
|  | if (OBJ_obj2nid(oid) == NID_any_policy) | 
|  | { | 
|  | tree->flags |= POLICY_FLAG_ANY_POLICY; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 0; i < sk_ASN1_OBJECT_num(policy_oids); i++) | 
|  | { | 
|  | oid = sk_ASN1_OBJECT_value(policy_oids, i); | 
|  | node = tree_find_sk(auth_nodes, oid); | 
|  | if (!node) | 
|  | { | 
|  | if (!anyPolicy) | 
|  | continue; | 
|  | /* Create a new node with policy ID from user set | 
|  | * and qualifiers from anyPolicy. | 
|  | */ | 
|  | extra = policy_data_new(NULL, oid, | 
|  | node_critical(anyPolicy)); | 
|  | if (!extra) | 
|  | return 0; | 
|  | extra->qualifier_set = anyPolicy->data->qualifier_set; | 
|  | extra->flags = POLICY_DATA_FLAG_SHARED_QUALIFIERS | 
|  | | POLICY_DATA_FLAG_EXTRA_NODE; | 
|  | node = level_add_node(NULL, extra, anyPolicy->parent, | 
|  | tree); | 
|  | } | 
|  | if (!tree->user_policies) | 
|  | { | 
|  | tree->user_policies = sk_X509_POLICY_NODE_new_null(); | 
|  | if (!tree->user_policies) | 
|  | return 1; | 
|  | } | 
|  | if (!sk_X509_POLICY_NODE_push(tree->user_policies, node)) | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  |  | 
|  | } | 
|  |  | 
|  | static int tree_evaluate(X509_POLICY_TREE *tree) | 
|  | { | 
|  | int ret, i; | 
|  | X509_POLICY_LEVEL *curr = tree->levels + 1; | 
|  | const X509_POLICY_CACHE *cache; | 
|  |  | 
|  | for(i = 1; i < tree->nlevel; i++, curr++) | 
|  | { | 
|  | cache = policy_cache_set(curr->cert); | 
|  | if (!tree_link_nodes(curr, cache)) | 
|  | return 0; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  |  | 
|  | } | 
|  |  | 
|  | static void exnode_free(X509_POLICY_NODE *node) | 
|  | { | 
|  | if (node->data && (node->data->flags & POLICY_DATA_FLAG_EXTRA_NODE)) | 
|  | OPENSSL_free(node); | 
|  | } | 
|  |  | 
|  |  | 
|  | void X509_policy_tree_free(X509_POLICY_TREE *tree) | 
|  | { | 
|  | X509_POLICY_LEVEL *curr; | 
|  | int i; | 
|  |  | 
|  | if (!tree) | 
|  | return; | 
|  |  | 
|  | sk_X509_POLICY_NODE_free(tree->auth_policies); | 
|  | sk_X509_POLICY_NODE_pop_free(tree->user_policies, exnode_free); | 
|  |  | 
|  | for(i = 0, curr = tree->levels; i < tree->nlevel; i++, curr++) | 
|  | { | 
|  | if (curr->cert) | 
|  | X509_free(curr->cert); | 
|  | if (curr->nodes) | 
|  | sk_X509_POLICY_NODE_pop_free(curr->nodes, | 
|  | policy_node_free); | 
|  | if (curr->anyPolicy) | 
|  | policy_node_free(curr->anyPolicy); | 
|  | } | 
|  |  | 
|  | if (tree->extra_data) | 
|  | sk_X509_POLICY_DATA_pop_free(tree->extra_data, | 
|  | policy_data_free); | 
|  |  | 
|  | OPENSSL_free(tree->levels); | 
|  | OPENSSL_free(tree); | 
|  |  | 
|  | } | 
|  |  | 
|  | /* Application policy checking function. | 
|  | * Return codes: | 
|  | *  0 	Internal Error. | 
|  | *  1   Successful. | 
|  | * -1   One or more certificates contain invalid or inconsistent extensions | 
|  | * -2	User constrained policy set empty and requireExplicit true. | 
|  | */ | 
|  |  | 
|  | int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy, | 
|  | STACK_OF(X509) *certs, | 
|  | STACK_OF(ASN1_OBJECT) *policy_oids, | 
|  | unsigned int flags) | 
|  | { | 
|  | int ret; | 
|  | X509_POLICY_TREE *tree = NULL; | 
|  | STACK_OF(X509_POLICY_NODE) *nodes, *auth_nodes = NULL; | 
|  | *ptree = NULL; | 
|  |  | 
|  | *pexplicit_policy = 0; | 
|  | ret = tree_init(&tree, certs, flags); | 
|  |  | 
|  | switch (ret) | 
|  | { | 
|  |  | 
|  | /* Tree empty requireExplicit False: OK */ | 
|  | case 2: | 
|  | return 1; | 
|  |  | 
|  | /* Some internal error */ | 
|  | case -1: | 
|  | return -1; | 
|  |  | 
|  | /* Some internal error */ | 
|  | case 0: | 
|  | return 0; | 
|  |  | 
|  | /* Tree empty requireExplicit True: Error */ | 
|  |  | 
|  | case 6: | 
|  | *pexplicit_policy = 1; | 
|  | return -2; | 
|  |  | 
|  | /* Tree OK requireExplicit True: OK and continue */ | 
|  | case 5: | 
|  | *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; | 
|  |  | 
|  | /* Return value 2 means tree empty */ | 
|  | if (ret == 2) | 
|  | { | 
|  | X509_policy_tree_free(tree); | 
|  | if (*pexplicit_policy) | 
|  | return -2; | 
|  | else | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Tree is not empty: continue */ | 
|  |  | 
|  | ret = tree_calculate_authority_set(tree, &auth_nodes); | 
|  |  | 
|  | if (!ret) | 
|  | goto error; | 
|  |  | 
|  | if (!tree_calculate_user_set(tree, policy_oids, auth_nodes)) | 
|  | goto error; | 
|  |  | 
|  | if (ret == 2) | 
|  | sk_X509_POLICY_NODE_free(auth_nodes); | 
|  |  | 
|  | if (tree) | 
|  | *ptree = tree; | 
|  |  | 
|  | if (*pexplicit_policy) | 
|  | { | 
|  | nodes = X509_policy_tree_get0_user_policies(tree); | 
|  | if (sk_X509_POLICY_NODE_num(nodes) <= 0) | 
|  | return -2; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  |  | 
|  | error: | 
|  |  | 
|  | X509_policy_tree_free(tree); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | } | 
|  |  |