/* Copyright (c) 2022, Google Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <openssl/x509.h> #include <assert.h> #include <openssl/mem.h> #include <openssl/obj.h> #include <openssl/stack.h> #include "../internal.h" #include "internal.h" // This file computes the X.509 policy tree, as described in RFC 5280, section // 6.1. It differs in that: // // (1) It does not track "qualifier_set". This is not needed as it is not // output by this implementation. // // (2) It builds a directed acyclic graph, rather than a tree. When a given // policy matches multiple parents, RFC 5280 makes a separate node for // each parent. This representation condenses them into one node with // multiple parents. Thus we refer to this structure as a "policy graph", // rather than a "policy tree". // // (3) "expected_policy_set" is not tracked explicitly and built temporarily // as part of building the graph. // // (4) anyPolicy nodes are not tracked explicitly. // // (5) Some pruning steps are deferred to when policies are evaluated, as a // reachability pass. // An X509_POLICY_NODE is a node in the policy graph. It corresponds to a node // from RFC 5280, section 6.1.2, step (a), but we store some fields differently. X509_POLICY_NODE; DEFINE_STACK_OF(…) // An X509_POLICY_LEVEL is the collection of nodes at the same depth in the // policy graph. This structure can also be used to represent a level's // "expected_policy_set" values. See |process_policy_mappings|. X509_POLICY_LEVEL; DEFINE_STACK_OF(…) static int is_any_policy(const ASN1_OBJECT *obj) { … } static void x509_policy_node_free(X509_POLICY_NODE *node) { … } static X509_POLICY_NODE *x509_policy_node_new(const ASN1_OBJECT *policy) { … } static int x509_policy_node_cmp(const X509_POLICY_NODE *const *a, const X509_POLICY_NODE *const *b) { … } static void x509_policy_level_free(X509_POLICY_LEVEL *level) { … } static X509_POLICY_LEVEL *x509_policy_level_new(void) { … } static int x509_policy_level_is_empty(const X509_POLICY_LEVEL *level) { … } static void x509_policy_level_clear(X509_POLICY_LEVEL *level) { … } // x509_policy_level_find returns the node in |level| corresponding to |policy|, // or NULL if none exists. static X509_POLICY_NODE *x509_policy_level_find(X509_POLICY_LEVEL *level, const ASN1_OBJECT *policy) { … } // x509_policy_level_add_nodes adds the nodes in |nodes| to |level|. It returns // one on success and zero on error. No policy in |nodes| may already be present // in |level|. This function modifies |nodes| to avoid making a copy, but the // caller is still responsible for releasing |nodes| itself. // // This function is used to add nodes to |level| in bulk, and avoid resorting // |level| after each addition. static int x509_policy_level_add_nodes(X509_POLICY_LEVEL *level, STACK_OF(X509_POLICY_NODE) *nodes) { … } static int policyinfo_cmp(const POLICYINFO *const *a, const POLICYINFO *const *b) { … } static int delete_if_not_in_policies(X509_POLICY_NODE *node, void *data) { … } // process_certificate_policies updates |level| to incorporate |x509|'s // certificate policies extension. This implements steps (d) and (e) of RFC // 5280, section 6.1.3. |level| must contain the previous level's // "expected_policy_set" information. For all but the top-most level, this is // the output of |process_policy_mappings|. |any_policy_allowed| specifies // whether anyPolicy is allowed or inhibited, taking into account the exception // for self-issued certificates. static int process_certificate_policies(const X509 *x509, X509_POLICY_LEVEL *level, int any_policy_allowed) { … } static int compare_issuer_policy(const POLICY_MAPPING *const *a, const POLICY_MAPPING *const *b) { … } static int compare_subject_policy(const POLICY_MAPPING *const *a, const POLICY_MAPPING *const *b) { … } static int delete_if_mapped(X509_POLICY_NODE *node, void *data) { … } // process_policy_mappings processes the policy mappings extension of |cert|, // whose corresponding graph level is |level|. |mapping_allowed| specifies // whether policy mapping is inhibited at this point. On success, it returns an // |X509_POLICY_LEVEL| containing the "expected_policy_set" for |level|. On // error, it returns NULL. This implements steps (a) and (b) of RFC 5280, // section 6.1.4. // // We represent the "expected_policy_set" as an |X509_POLICY_LEVEL|. // |has_any_policy| indicates whether there is an anyPolicy node with // "expected_policy_set" of {anyPolicy}. If a node with policy oid P1 contains // P2 in its "expected_policy_set", the level will contain a node of policy P2 // with P1 in |parent_policies|. // // This is equivalent to the |X509_POLICY_LEVEL| that would result if the next // certificats contained anyPolicy. |process_certificate_policies| will filter // this result down to compute the actual level. static X509_POLICY_LEVEL *process_policy_mappings(const X509 *cert, X509_POLICY_LEVEL *level, int mapping_allowed) { … } // apply_skip_certs, if |skip_certs| is non-NULL, sets |*value| to the minimum // of its current value and |skip_certs|. It returns one on success and zero if // |skip_certs| is negative. static int apply_skip_certs(const ASN1_INTEGER *skip_certs, size_t *value) { … } // process_policy_constraints updates |*explicit_policy|, |*policy_mapping|, and // |*inhibit_any_policy| according to |x509|'s policy constraints and inhibit // anyPolicy extensions. It returns one on success and zero on error. This // implements steps (i) and (j) of RFC 5280, section 6.1.4. static int process_policy_constraints(const X509 *x509, size_t *explicit_policy, size_t *policy_mapping, size_t *inhibit_any_policy) { … } // has_explicit_policy returns one if the set of authority-space policy OIDs // |levels| has some non-empty intersection with |user_policies|, and zero // otherwise. This mirrors the logic in RFC 5280, section 6.1.5, step (g). This // function modifies |levels| and should only be called at the end of policy // evaluation. static int has_explicit_policy(STACK_OF(X509_POLICY_LEVEL) *levels, const STACK_OF(ASN1_OBJECT) *user_policies) { … } static int asn1_object_cmp(const ASN1_OBJECT *const *a, const ASN1_OBJECT *const *b) { … } int X509_policy_check(const STACK_OF(X509) *certs, const STACK_OF(ASN1_OBJECT) *user_policies, unsigned long flags, X509 **out_current_cert) { … }