diff options
Diffstat (limited to 'crypto/x509')
42 files changed, 12028 insertions, 1 deletions
diff --git a/crypto/x509/build.info b/crypto/x509/build.info index afd0b6134e..bee9f80961 100644 --- a/crypto/x509/build.info +++ b/crypto/x509/build.info @@ -7,4 +7,11 @@ SOURCE[../../libcrypto]=\ x509type.c x509_meth.c x509_lu.c x_all.c x509_txt.c \ x509_trs.c by_file.c by_dir.c x509_vpm.c \ x_crl.c t_crl.c x_req.c t_req.c x_x509.c t_x509.c \ - x_pubkey.c x_x509a.c x_attrib.c x_exten.c x_name.c + x_pubkey.c x_x509a.c x_attrib.c x_exten.c x_name.c \ + v3_bcons.c v3_bitst.c v3_conf.c v3_extku.c v3_ia5.c v3_lib.c \ + v3_prn.c v3_utl.c v3err.c v3_genn.c v3_alt.c v3_skey.c v3_akey.c \ + v3_pku.c v3_int.c v3_enum.c v3_sxnet.c v3_cpols.c v3_crld.c v3_purp.c \ + v3_info.c v3_akeya.c v3_pmaps.c v3_pcons.c v3_ncons.c \ + v3_pcia.c v3_pci.c \ + pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c \ + v3_asid.c v3_addr.c v3_tlsf.c v3_admis.c diff --git a/crypto/x509/ext_dat.h b/crypto/x509/ext_dat.h new file mode 100644 index 0000000000..aa6fa79774 --- /dev/null +++ b/crypto/x509/ext_dat.h @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +int name_cmp(const char *name, const char *cmp); + +extern const X509V3_EXT_METHOD v3_bcons, v3_nscert, v3_key_usage, v3_ext_ku; +extern const X509V3_EXT_METHOD v3_pkey_usage_period, v3_sxnet, v3_info, v3_sinfo; +extern const X509V3_EXT_METHOD v3_ns_ia5_list[8], v3_alt[3], v3_skey_id, v3_akey_id; +extern const X509V3_EXT_METHOD v3_crl_num, v3_crl_reason, v3_crl_invdate; +extern const X509V3_EXT_METHOD v3_delta_crl, v3_cpols, v3_crld, v3_freshest_crl; +extern const X509V3_EXT_METHOD v3_ocsp_nonce, v3_ocsp_accresp, v3_ocsp_acutoff; +extern const X509V3_EXT_METHOD v3_ocsp_crlid, v3_ocsp_nocheck, v3_ocsp_serviceloc; +extern const X509V3_EXT_METHOD v3_crl_hold, v3_pci; +extern const X509V3_EXT_METHOD v3_policy_mappings, v3_policy_constraints; +extern const X509V3_EXT_METHOD v3_name_constraints, v3_inhibit_anyp, v3_idp; +extern const X509V3_EXT_METHOD v3_addr, v3_asid; +extern const X509V3_EXT_METHOD v3_ct_scts[3]; +extern const X509V3_EXT_METHOD v3_tls_feature; +extern const X509V3_EXT_METHOD v3_ext_admission; diff --git a/crypto/x509/pcy_cache.c b/crypto/x509/pcy_cache.c new file mode 100644 index 0000000000..21a89e62c0 --- /dev/null +++ b/crypto/x509/pcy_cache.c @@ -0,0 +1,224 @@ +/* + * Copyright 2004-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include "internal/x509_int.h" + +#include "pcy_int.h" + +static int policy_data_cmp(const X509_POLICY_DATA *const *a, + const X509_POLICY_DATA *const *b); +static int policy_cache_set_int(long *out, ASN1_INTEGER *value); + +/* + * Set cache entry according to CertificatePolicies extension. Note: this + * destroys the passed CERTIFICATEPOLICIES structure. + */ + +static int policy_cache_create(X509 *x, + CERTIFICATEPOLICIES *policies, int crit) +{ + int i, num, ret = 0; + X509_POLICY_CACHE *cache = x->policy_cache; + X509_POLICY_DATA *data = NULL; + POLICYINFO *policy; + + if ((num = sk_POLICYINFO_num(policies)) <= 0) + goto bad_policy; + cache->data = sk_X509_POLICY_DATA_new(policy_data_cmp); + if (cache->data == NULL) { + X509V3err(X509V3_F_POLICY_CACHE_CREATE, ERR_R_MALLOC_FAILURE); + goto just_cleanup; + } + for (i = 0; i < num; i++) { + policy = sk_POLICYINFO_value(policies, i); + data = policy_data_new(policy, NULL, crit); + if (data == NULL) { + X509V3err(X509V3_F_POLICY_CACHE_CREATE, ERR_R_MALLOC_FAILURE); + goto just_cleanup; + } + /* + * Duplicate policy OIDs are illegal: reject if matches found. + */ + if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) { + if (cache->anyPolicy) { + ret = -1; + goto bad_policy; + } + cache->anyPolicy = data; + } else if (sk_X509_POLICY_DATA_find(cache->data, data) >=0 ) { + ret = -1; + goto bad_policy; + } else if (!sk_X509_POLICY_DATA_push(cache->data, data)) { + X509V3err(X509V3_F_POLICY_CACHE_CREATE, ERR_R_MALLOC_FAILURE); + goto bad_policy; + } + data = NULL; + } + ret = 1; + + bad_policy: + if (ret == -1) + x->ex_flags |= EXFLAG_INVALID_POLICY; + policy_data_free(data); + just_cleanup: + sk_POLICYINFO_pop_free(policies, POLICYINFO_free); + if (ret <= 0) { + sk_X509_POLICY_DATA_pop_free(cache->data, policy_data_free); + cache->data = NULL; + } + return ret; +} + +static int policy_cache_new(X509 *x) +{ + X509_POLICY_CACHE *cache; + ASN1_INTEGER *ext_any = NULL; + POLICY_CONSTRAINTS *ext_pcons = NULL; + CERTIFICATEPOLICIES *ext_cpols = NULL; + POLICY_MAPPINGS *ext_pmaps = NULL; + int i; + + if (x->policy_cache != NULL) + return 1; + cache = OPENSSL_malloc(sizeof(*cache)); + if (cache == NULL) { + X509V3err(X509V3_F_POLICY_CACHE_NEW, ERR_R_MALLOC_FAILURE); + return 0; + } + cache->anyPolicy = NULL; + cache->data = NULL; + cache->any_skip = -1; + cache->explicit_skip = -1; + cache->map_skip = -1; + + x->policy_cache = cache; + + /* + * Handle requireExplicitPolicy *first*. Need to process this even if we + * don't have any policies. + */ + ext_pcons = X509_get_ext_d2i(x, NID_policy_constraints, &i, NULL); + + if (!ext_pcons) { + if (i != -1) + goto bad_cache; + } else { + if (!ext_pcons->requireExplicitPolicy + && !ext_pcons->inhibitPolicyMapping) + goto bad_cache; + if (!policy_cache_set_int(&cache->explicit_skip, + ext_pcons->requireExplicitPolicy)) + goto bad_cache; + if (!policy_cache_set_int(&cache->map_skip, + ext_pcons->inhibitPolicyMapping)) + goto bad_cache; + } + + /* Process CertificatePolicies */ + + ext_cpols = X509_get_ext_d2i(x, NID_certificate_policies, &i, NULL); + /* + * If no CertificatePolicies extension or problem decoding then there is + * no point continuing because the valid policies will be NULL. + */ + if (!ext_cpols) { + /* If not absent some problem with extension */ + if (i != -1) + goto bad_cache; + return 1; + } + + i = policy_cache_create(x, ext_cpols, i); + + /* NB: ext_cpols freed by policy_cache_set_policies */ + + if (i <= 0) + return i; + + ext_pmaps = X509_get_ext_d2i(x, NID_policy_mappings, &i, NULL); + + if (!ext_pmaps) { + /* If not absent some problem with extension */ + if (i != -1) + goto bad_cache; + } else { + i = policy_cache_set_mapping(x, ext_pmaps); + if (i <= 0) + goto bad_cache; + } + + ext_any = X509_get_ext_d2i(x, NID_inhibit_any_policy, &i, NULL); + + if (!ext_any) { + if (i != -1) + goto bad_cache; + } else if (!policy_cache_set_int(&cache->any_skip, ext_any)) + goto bad_cache; + goto just_cleanup; + + bad_cache: + x->ex_flags |= EXFLAG_INVALID_POLICY; + + just_cleanup: + POLICY_CONSTRAINTS_free(ext_pcons); + ASN1_INTEGER_free(ext_any); + return 1; + +} + +void policy_cache_free(X509_POLICY_CACHE *cache) +{ + if (!cache) + return; + policy_data_free(cache->anyPolicy); + sk_X509_POLICY_DATA_pop_free(cache->data, policy_data_free); + OPENSSL_free(cache); +} + +const X509_POLICY_CACHE *policy_cache_set(X509 *x) +{ + + if (x->policy_cache == NULL) { + CRYPTO_THREAD_write_lock(x->lock); + policy_cache_new(x); + CRYPTO_THREAD_unlock(x->lock); + } + + return x->policy_cache; + +} + +X509_POLICY_DATA *policy_cache_find_data(const X509_POLICY_CACHE *cache, + const ASN1_OBJECT *id) +{ + int idx; + X509_POLICY_DATA tmp; + tmp.valid_policy = (ASN1_OBJECT *)id; + idx = sk_X509_POLICY_DATA_find(cache->data, &tmp); + return sk_X509_POLICY_DATA_value(cache->data, idx); +} + +static int policy_data_cmp(const X509_POLICY_DATA *const *a, + const X509_POLICY_DATA *const *b) +{ + return OBJ_cmp((*a)->valid_policy, (*b)->valid_policy); +} + +static int policy_cache_set_int(long *out, ASN1_INTEGER *value) +{ + if (value == NULL) + return 1; + if (value->type == V_ASN1_NEG_INTEGER) + return 0; + *out = ASN1_INTEGER_get(value); + return 1; +} diff --git a/crypto/x509/pcy_data.c b/crypto/x509/pcy_data.c new file mode 100644 index 0000000000..c1d56bdcde --- /dev/null +++ b/crypto/x509/pcy_data.c @@ -0,0 +1,81 @@ +/* + * Copyright 2004-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include <openssl/x509.h> +#include <openssl/x509v3.h> + +#include "pcy_int.h" + +/* Policy Node routines */ + +void policy_data_free(X509_POLICY_DATA *data) +{ + if (data == NULL) + return; + ASN1_OBJECT_free(data->valid_policy); + /* Don't free qualifiers if shared */ + if (!(data->flags & POLICY_DATA_FLAG_SHARED_QUALIFIERS)) + sk_POLICYQUALINFO_pop_free(data->qualifier_set, POLICYQUALINFO_free); + sk_ASN1_OBJECT_pop_free(data->expected_policy_set, ASN1_OBJECT_free); + OPENSSL_free(data); +} + +/* + * Create a data based on an existing policy. If 'id' is NULL use the OID in + * the policy, otherwise use 'id'. This behaviour covers the two types of + * data in RFC3280: data with from a CertificatePolicies extension and + * additional data with just the qualifiers of anyPolicy and ID from another + * source. + */ + +X509_POLICY_DATA *policy_data_new(POLICYINFO *policy, + const ASN1_OBJECT *cid, int crit) +{ + X509_POLICY_DATA *ret; + ASN1_OBJECT *id; + + if (policy == NULL && cid == NULL) + return NULL; + if (cid) { + id = OBJ_dup(cid); + if (id == NULL) + return NULL; + } else + id = NULL; + ret = OPENSSL_zalloc(sizeof(*ret)); + if (ret == NULL) { + X509V3err(X509V3_F_POLICY_DATA_NEW, ERR_R_MALLOC_FAILURE); + return NULL; + } + ret->expected_policy_set = sk_ASN1_OBJECT_new_null(); + if (ret->expected_policy_set == NULL) { + OPENSSL_free(ret); + ASN1_OBJECT_free(id); + X509V3err(X509V3_F_POLICY_DATA_NEW, ERR_R_MALLOC_FAILURE); + return NULL; + } + + if (crit) + ret->flags = POLICY_DATA_FLAG_CRITICAL; + + if (id) + ret->valid_policy = id; + else { + ret->valid_policy = policy->policyid; + policy->policyid = NULL; + } + + if (policy) { + ret->qualifier_set = policy->qualifiers; + policy->qualifiers = NULL; + } + + return ret; +} diff --git a/crypto/x509/pcy_int.h b/crypto/x509/pcy_int.h new file mode 100644 index 0000000000..0deee52c1b --- /dev/null +++ b/crypto/x509/pcy_int.h @@ -0,0 +1,167 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +typedef struct X509_POLICY_DATA_st X509_POLICY_DATA; + +DEFINE_STACK_OF(X509_POLICY_DATA) + +/* Internal structures */ + +/* + * This structure and the field names correspond to the Policy 'node' of + * RFC3280. NB this structure contains no pointers to parent or child data: + * X509_POLICY_NODE contains that. This means that the main policy data can + * be kept static and cached with the certificate. + */ + +struct X509_POLICY_DATA_st { + unsigned int flags; + /* Policy OID and qualifiers for this data */ + ASN1_OBJECT *valid_policy; + STACK_OF(POLICYQUALINFO) *qualifier_set; + STACK_OF(ASN1_OBJECT) *expected_policy_set; +}; + +/* X509_POLICY_DATA flags values */ + +/* + * This flag indicates the structure has been mapped using a policy mapping + * extension. If policy mapping is not active its references get deleted. + */ + +#define POLICY_DATA_FLAG_MAPPED 0x1 + +/* + * This flag indicates the data doesn't correspond to a policy in Certificate + * Policies: it has been mapped to any policy. + */ + +#define POLICY_DATA_FLAG_MAPPED_ANY 0x2 + +/* AND with flags to see if any mapping has occurred */ + +#define POLICY_DATA_FLAG_MAP_MASK 0x3 + +/* qualifiers are shared and shouldn't be freed */ + +#define POLICY_DATA_FLAG_SHARED_QUALIFIERS 0x4 + +/* Parent node is an extra node and should be freed */ + +#define POLICY_DATA_FLAG_EXTRA_NODE 0x8 + +/* Corresponding CertificatePolicies is critical */ + +#define POLICY_DATA_FLAG_CRITICAL 0x10 + +/* This structure is cached with a certificate */ + +struct X509_POLICY_CACHE_st { + /* anyPolicy data or NULL if no anyPolicy */ + X509_POLICY_DATA *anyPolicy; + /* other policy data */ + STACK_OF(X509_POLICY_DATA) *data; + /* If InhibitAnyPolicy present this is its value or -1 if absent. */ + long any_skip; + /* + * If policyConstraints and requireExplicitPolicy present this is its + * value or -1 if absent. + */ + long explicit_skip; + /* + * If policyConstraints and policyMapping present this is its value or -1 + * if absent. + */ + long map_skip; +}; + +/* + * #define POLICY_CACHE_FLAG_CRITICAL POLICY_DATA_FLAG_CRITICAL + */ + +/* This structure represents the relationship between nodes */ + +struct X509_POLICY_NODE_st { + /* node data this refers to */ + const X509_POLICY_DATA *data; + /* Parent node */ + X509_POLICY_NODE *parent; + /* Number of child nodes */ + int nchild; +}; + +struct X509_POLICY_LEVEL_st { + /* Cert for this level */ + X509 *cert; + /* nodes at this level */ + STACK_OF(X509_POLICY_NODE) *nodes; + /* anyPolicy node */ + X509_POLICY_NODE *anyPolicy; + /* Extra data */ + /* + * STACK_OF(X509_POLICY_DATA) *extra_data; + */ + unsigned int flags; +}; + +struct X509_POLICY_TREE_st { + /* This is the tree 'level' data */ + X509_POLICY_LEVEL *levels; + int nlevel; + /* + * Extra policy data when additional nodes (not from the certificate) are + * required. + */ + STACK_OF(X509_POLICY_DATA) *extra_data; + /* This is the authority constrained policy set */ + STACK_OF(X509_POLICY_NODE) *auth_policies; + STACK_OF(X509_POLICY_NODE) *user_policies; + unsigned int flags; +}; + +/* Set if anyPolicy present in user policies */ +#define POLICY_FLAG_ANY_POLICY 0x2 + +/* Useful macros */ + +#define node_data_critical(data) (data->flags & POLICY_DATA_FLAG_CRITICAL) +#define node_critical(node) node_data_critical(node->data) + +/* Internal functions */ + +X509_POLICY_DATA *policy_data_new(POLICYINFO *policy, const ASN1_OBJECT *id, + int crit); +void policy_data_free(X509_POLICY_DATA *data); + +X509_POLICY_DATA *policy_cache_find_data(const X509_POLICY_CACHE *cache, + const ASN1_OBJECT *id); +int policy_cache_set_mapping(X509 *x, POLICY_MAPPINGS *maps); + +STACK_OF(X509_POLICY_NODE) *policy_node_cmp_new(void); + +void policy_cache_init(void); + +void policy_cache_free(X509_POLICY_CACHE *cache); + +X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level, + const X509_POLICY_NODE *parent, + const ASN1_OBJECT *id); + +X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk, + const ASN1_OBJECT *id); + +X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level, + X509_POLICY_DATA *data, + X509_POLICY_NODE *parent, + X509_POLICY_TREE *tree); +void policy_node_free(X509_POLICY_NODE *node); +int policy_node_match(const X509_POLICY_LEVEL *lvl, + const X509_POLICY_NODE *node, const ASN1_OBJECT *oid); + +const X509_POLICY_CACHE *policy_cache_set(X509 *x); diff --git a/crypto/x509/pcy_lib.c b/crypto/x509/pcy_lib.c new file mode 100644 index 0000000000..deee8f6c0c --- /dev/null +++ b/crypto/x509/pcy_lib.c @@ -0,0 +1,108 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include <openssl/x509.h> +#include <openssl/x509v3.h> + +#include "pcy_int.h" + +/* accessor functions */ + +/* X509_POLICY_TREE stuff */ + +int X509_policy_tree_level_count(const X509_POLICY_TREE *tree) +{ + if (!tree) + return 0; + return tree->nlevel; +} + +X509_POLICY_LEVEL *X509_policy_tree_get0_level(const X509_POLICY_TREE *tree, + int i) +{ + if (!tree || (i < 0) || (i >= tree->nlevel)) + return NULL; + return tree->levels + i; +} + +STACK_OF(X509_POLICY_NODE) *X509_policy_tree_get0_policies(const + X509_POLICY_TREE + *tree) +{ + if (!tree) + return NULL; + return tree->auth_policies; +} + +STACK_OF(X509_POLICY_NODE) *X509_policy_tree_get0_user_policies(const + X509_POLICY_TREE + *tree) +{ + if (!tree) + return NULL; + if (tree->flags & POLICY_FLAG_ANY_POLICY) + return tree->auth_policies; + else + return tree->user_policies; +} + +/* X509_POLICY_LEVEL stuff */ + +int X509_policy_level_node_count(X509_POLICY_LEVEL *level) +{ + int n; + if (!level) + return 0; + if (level->anyPolicy) + n = 1; + else + n = 0; + if (level->nodes) + n += sk_X509_POLICY_NODE_num(level->nodes); + return n; +} + +X509_POLICY_NODE *X509_policy_level_get0_node(X509_POLICY_LEVEL *level, int i) +{ + if (!level) + return NULL; + if (level->anyPolicy) { + if (i == 0) + return level->anyPolicy; + i--; + } + return sk_X509_POLICY_NODE_value(level->nodes, i); +} + +/* X509_POLICY_NODE stuff */ + +const ASN1_OBJECT *X509_policy_node_get0_policy(const X509_POLICY_NODE *node) +{ + if (!node) + return NULL; + return node->data->valid_policy; +} + +STACK_OF(POLICYQUALINFO) *X509_policy_node_get0_qualifiers(const + X509_POLICY_NODE + *node) +{ + if (!node) + return NULL; + return node->data->qualifier_set; +} + +const X509_POLICY_NODE *X509_policy_node_get0_parent(const X509_POLICY_NODE + *node) +{ + if (!node) + return NULL; + return node->parent; +} diff --git a/crypto/x509/pcy_map.c b/crypto/x509/pcy_map.c new file mode 100644 index 0000000000..243d6e2b7c --- /dev/null +++ b/crypto/x509/pcy_map.c @@ -0,0 +1,81 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include "internal/x509_int.h" + +#include "pcy_int.h" + +/* + * Set policy mapping entries in cache. Note: this modifies the passed + * POLICY_MAPPINGS structure + */ + +int policy_cache_set_mapping(X509 *x, POLICY_MAPPINGS *maps) +{ + POLICY_MAPPING *map; + X509_POLICY_DATA *data; + X509_POLICY_CACHE *cache = x->policy_cache; + int i; + int ret = 0; + if (sk_POLICY_MAPPING_num(maps) == 0) { + ret = -1; + goto bad_mapping; + } + for (i = 0; i < sk_POLICY_MAPPING_num(maps); i++) { + map = sk_POLICY_MAPPING_value(maps, i); + /* Reject if map to or from anyPolicy */ + if ((OBJ_obj2nid(map->subjectDomainPolicy) == NID_any_policy) + || (OBJ_obj2nid(map->issuerDomainPolicy) == NID_any_policy)) { + ret = -1; + goto bad_mapping; + } + + /* Attempt to find matching policy data */ + data = policy_cache_find_data(cache, map->issuerDomainPolicy); + /* If we don't have anyPolicy can't map */ + if (data == NULL && !cache->anyPolicy) + continue; + + /* Create a NODE from anyPolicy */ + if (data == NULL) { + data = policy_data_new(NULL, map->issuerDomainPolicy, + cache->anyPolicy->flags + & POLICY_DATA_FLAG_CRITICAL); + if (data == NULL) + goto bad_mapping; + data->qualifier_set = cache->anyPolicy->qualifier_set; + /* + * map->issuerDomainPolicy = NULL; + */ + data->flags |= POLICY_DATA_FLAG_MAPPED_ANY; + data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS; + if (!sk_X509_POLICY_DATA_push(cache->data, data)) { + policy_data_free(data); + goto bad_mapping; + } + } else + data->flags |= POLICY_DATA_FLAG_MAPPED; + if (!sk_ASN1_OBJECT_push(data->expected_policy_set, + map->subjectDomainPolicy)) + goto bad_mapping; + map->subjectDomainPolicy = NULL; + + } + + ret = 1; + bad_mapping: + if (ret == -1) + x->ex_flags |= EXFLAG_INVALID_POLICY; + sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free); + return ret; + +} diff --git a/crypto/x509/pcy_node.c b/crypto/x509/pcy_node.c new file mode 100644 index 0000000000..005d1de7e3 --- /dev/null +++ b/crypto/x509/pcy_node.c @@ -0,0 +1,147 @@ +/* + * Copyright 2004-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/asn1.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/err.h> + +#include "pcy_int.h" + +static int node_cmp(const X509_POLICY_NODE *const *a, + const X509_POLICY_NODE *const *b) +{ + return OBJ_cmp((*a)->data->valid_policy, (*b)->data->valid_policy); +} + +STACK_OF(X509_POLICY_NODE) *policy_node_cmp_new(void) +{ + return sk_X509_POLICY_NODE_new(node_cmp); +} + +X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *nodes, + const ASN1_OBJECT *id) +{ + X509_POLICY_DATA n; + X509_POLICY_NODE l; + int idx; + + n.valid_policy = (ASN1_OBJECT *)id; + l.data = &n; + + idx = sk_X509_POLICY_NODE_find(nodes, &l); + return sk_X509_POLICY_NODE_value(nodes, idx); + +} + +X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level, + const X509_POLICY_NODE *parent, + const ASN1_OBJECT *id) +{ + X509_POLICY_NODE *node; + int i; + for (i = 0; i < sk_X509_POLICY_NODE_num(level->nodes); i++) { + node = sk_X509_POLICY_NODE_value(level->nodes, i); + if (node->parent == parent) { + if (!OBJ_cmp(node->data->valid_policy, id)) + return node; + } + } + return NULL; +} + +X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level, + X509_POLICY_DATA *data, + X509_POLICY_NODE *parent, + X509_POLICY_TREE *tree) +{ + X509_POLICY_NODE *node; + + node = OPENSSL_zalloc(sizeof(*node)); + if (node == NULL) { + X509V3err(X509V3_F_LEVEL_ADD_NODE, ERR_R_MALLOC_FAILURE); + return NULL; + } + node->data = data; + node->parent = parent; + if (level) { + if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) { + if (level->anyPolicy) + goto node_error; + level->anyPolicy = node; + } else { + + if (level->nodes == NULL) + level->nodes = policy_node_cmp_new(); + if (level->nodes == NULL) { + X509V3err(X509V3_F_LEVEL_ADD_NODE, ERR_R_MALLOC_FAILURE); + goto node_error; + } + if (!sk_X509_POLICY_NODE_push(level->nodes, node)) { + X509V3err(X509V3_F_LEVEL_ADD_NODE, ERR_R_MALLOC_FAILURE); + goto node_error; + } + } + } + + if (tree) { + if (tree->extra_data == NULL) + tree->extra_data = sk_X509_POLICY_DATA_new_null(); + if (tree->extra_data == NULL){ + X509V3err(X509V3_F_LEVEL_ADD_NODE, ERR_R_MALLOC_FAILURE); + goto node_error; + } + if (!sk_X509_POLICY_DATA_push(tree->extra_data, data)) { + X509V3err(X509V3_F_LEVEL_ADD_NODE, ERR_R_MALLOC_FAILURE); + goto node_error; + } + } + + if (parent) + parent->nchild++; + + return node; + + node_error: + policy_node_free(node); + return NULL; +} + +void policy_node_free(X509_POLICY_NODE *node) +{ + OPENSSL_free(node); +} + +/* + * See if a policy node matches a policy OID. If mapping enabled look through + * expected policy set otherwise just valid policy. + */ + +int policy_node_match(const X509_POLICY_LEVEL *lvl, + const X509_POLICY_NODE *node, const ASN1_OBJECT *oid) +{ + int i; + ASN1_OBJECT *policy_oid; + const X509_POLICY_DATA *x = node->data; + + if ((lvl->flags & X509_V_FLAG_INHIBIT_MAP) + || !(x->flags & POLICY_DATA_FLAG_MAP_MASK)) { + if (!OBJ_cmp(x->valid_policy, oid)) + return 1; + return 0; + } + + for (i = 0; i < sk_ASN1_OBJECT_num(x->expected_policy_set); i++) { + policy_oid = sk_ASN1_OBJECT_value(x->expected_policy_set, i); + if (!OBJ_cmp(policy_oid, oid)) + return 1; + } + return 0; + +} diff --git a/crypto/x509/pcy_tree.c b/crypto/x509/pcy_tree.c new file mode 100644 index 0000000000..0c5887c25a --- /dev/null +++ b/crypto/x509/pcy_tree.c @@ -0,0 +1,694 @@ +/* + * Copyright 2004-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include <openssl/trace.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> + +#include "pcy_int.h" + +static void expected_print(BIO *channel, + 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(channel, " Not Mapped\n"); + else { + int i; + + STACK_OF(ASN1_OBJECT) *pset = node->data->expected_policy_set; + ASN1_OBJECT *oid; + BIO_puts(channel, " Expected: "); + for (i = 0; i < sk_ASN1_OBJECT_num(pset); i++) { + oid = sk_ASN1_OBJECT_value(pset, i); + if (i) + BIO_puts(channel, ", "); + i2a_ASN1_OBJECT(channel, oid); + } + BIO_puts(channel, "\n"); + } +} + +static void tree_print(BIO *channel, + char *str, X509_POLICY_TREE *tree, + X509_POLICY_LEVEL *curr) +{ + X509_POLICY_LEVEL *plev; + + if (!curr) + curr = tree->levels + tree->nlevel; + else + curr++; + + BIO_printf(channel, "Level print after %s\n", str); + BIO_printf(channel, "Printing Up to Level %ld\n", + curr - tree->levels); + for (plev = tree->levels; plev != curr; plev++) { + int i; + + BIO_printf(channel, "Level %ld, flags = %x\n", + (long)(plev - tree->levels), plev->flags); + for (i = 0; i < sk_X509_POLICY_NODE_num(plev->nodes); i++) { + X509_POLICY_NODE *node = + sk_X509_POLICY_NODE_value(plev->nodes, i); + + X509_POLICY_NODE_print(channel, node, 2); + expected_print(channel, plev, node, 2); + BIO_printf(channel, " Flags: %x\n", node->data->flags); + } + if (plev->anyPolicy) + X509_POLICY_NODE_print(channel, plev->anyPolicy, 2); + } +} + +#define TREE_PRINT(str, tree, curr) \ + OSSL_TRACE_BEGIN(X509V3_POLICY) { \ + tree_print(trc_out, "before tree_prune()", tree, curr); \ + } OSSL_TRACE_END(X509V3_POLICY) + +/*- + * Return value: <= 0 on error, or positive bit mask: + * + * X509_PCY_TREE_VALID: valid tree + * X509_PCY_TREE_EMPTY: empty tree (including bare TA case) + * X509_PCY_TREE_EXPLICIT: explicit policy required + */ +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; + int ret = X509_PCY_TREE_VALID; + int n = sk_X509_num(certs) - 1; /* RFC5280 paths omit the TA */ + int explicit_policy = (flags & X509_V_FLAG_EXPLICIT_POLICY) ? 0 : n+1; + int any_skip = (flags & X509_V_FLAG_INHIBIT_ANY) ? 0 : n+1; + int map_skip = (flags & X509_V_FLAG_INHIBIT_MAP) ? 0 : n+1; + int i; + + *ptree = NULL; + + /* Can't do anything with just a trust anchor */ + if (n == 0) + return X509_PCY_TREE_EMPTY; + + /* + * First setup the policy cache in all n non-TA certificates, this will be + * used in X509_verify_cert() which will invoke the verify callback for all + * certificates with invalid policy extensions. + */ + for (i = n - 1; i >= 0; i--) { + X509 *x = sk_X509_value(certs, i); + + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, 0); + + /* If cache is NULL, likely ENOMEM: return immediately */ + if (policy_cache_set(x) == NULL) + return X509_PCY_TREE_INTERNAL; + } + + /* + * At this point check for invalid policies and required explicit policy. + * Note that the explicit_policy counter is a count-down to zero, with the + * requirement kicking in if and once it does that. The counter is + * decremented for every non-self-issued certificate in the path, but may + * be further reduced by policy constraints in a non-leaf certificate. + * + * The ultimate policy set is the intersection of all the policies along + * the path, if we hit a certificate with an empty policy set, and explicit + * policy is required we're done. + */ + for (i = n - 1; + i >= 0 && (explicit_policy > 0 || (ret & X509_PCY_TREE_EMPTY) == 0); + i--) { + X509 *x = sk_X509_value(certs, i); + uint32_t ex_flags = X509_get_extension_flags(x); + + /* All the policies are already cached, we can return early */ + if (ex_flags & EXFLAG_INVALID_POLICY) + return X509_PCY_TREE_INVALID; + + /* Access the cache which we now know exists */ + cache = policy_cache_set(x); + + if ((ret & X509_PCY_TREE_VALID) && cache->data == NULL) + ret = X509_PCY_TREE_EMPTY; + if (explicit_policy > 0) { + if (!(ex_flags & EXFLAG_SI)) + explicit_policy--; + if ((cache->explicit_skip >= 0) + && (cache->explicit_skip < explicit_policy)) + explicit_policy = cache->explicit_skip; + } + } + + if (explicit_policy == 0) + ret |= X509_PCY_TREE_EXPLICIT; + if ((ret & X509_PCY_TREE_VALID) == 0) + return ret; + + /* If we get this far initialize the tree */ + if ((tree = OPENSSL_zalloc(sizeof(*tree))) == NULL) { + X509V3err(X509V3_F_TREE_INIT, ERR_R_MALLOC_FAILURE); + return X509_PCY_TREE_INTERNAL; + } + + /* + * http://tools.ietf.org/html/rfc5280#section-6.1.2, figure 3. + * + * The top level is implicitly for the trust anchor with valid expected + * policies of anyPolicy. (RFC 5280 has the TA at depth 0 and the leaf at + * depth n, we have the leaf at depth 0 and the TA at depth n). + */ + if ((tree->levels = OPENSSL_zalloc(sizeof(*tree->levels)*(n+1))) == NULL) { + OPENSSL_free(tree); + X509V3err(X509V3_F_TREE_INIT, ERR_R_MALLOC_FAILURE); + return X509_PCY_TREE_INTERNAL; + } + tree->nlevel = n+1; + level = tree->levels; + if ((data = policy_data_new(NULL, OBJ_nid2obj(NID_any_policy), 0)) == NULL) + goto bad_tree; + if (level_add_node(level, data, NULL, tree) == NULL) { + policy_data_free(data); + goto bad_tree; + } + + /* + * In this pass initialize all the tree levels and whether anyPolicy and + * policy mapping are inhibited at each level. + */ + for (i = n - 1; i >= 0; i--) { + X509 *x = sk_X509_value(certs, i); + uint32_t ex_flags = X509_get_extension_flags(x); + + /* Access the cache which we now know exists */ + cache = policy_cache_set(x); + + X509_up_ref(x); + (++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 only if certificate is self issued and not + * the last in the chain. + */ + if (!(ex_flags & EXFLAG_SI) || (i == 0)) + level->flags |= X509_V_FLAG_INHIBIT_ANY; + } else { + if (!(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 (!(ex_flags & EXFLAG_SI)) + map_skip--; + if ((cache->map_skip >= 0) && (cache->map_skip < map_skip)) + map_skip = cache->map_skip; + } + } + + *ptree = tree; + return ret; + + bad_tree: + X509_policy_tree_free(tree); + return X509_PCY_TREE_INTERNAL; +} + +/* + * Return value: 1 on success, 0 otherwise + */ +static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr, + X509_POLICY_DATA *data) +{ + X509_POLICY_LEVEL *last = curr - 1; + int i, matched = 0; + + /* Iterate through all in nodes linking matches */ + for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++) { + X509_POLICY_NODE *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) == NULL) + return 0; + matched = 1; + } + } + if (!matched && last->anyPolicy) { + if (level_add_node(curr, data, last->anyPolicy, NULL) == 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. + * + * Return value: 1 on success, 0 otherwise. + */ +static int tree_link_nodes(X509_POLICY_LEVEL *curr, + const X509_POLICY_CACHE *cache) +{ + int i; + + for (i = 0; i < sk_X509_POLICY_DATA_num(cache->data); i++) { + X509_POLICY_DATA *data = sk_X509_POLICY_DATA_value(cache->data, i); + + /* 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. + * + * Return value: 1 on success, 0 otherwise. + */ +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. + */ + if ((data = policy_data_new(NULL, id, node_critical(node))) == 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) == NULL) { + policy_data_free(data); + return 0; + } + return 1; +} + +/* + * Return value: 1 on success, 0 otherwise. + */ +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; +} + +/* + * Return value: 1 on success, 0 otherwise + */ +static int tree_link_any(X509_POLICY_LEVEL *curr, + const X509_POLICY_CACHE *cache, + X509_POLICY_TREE *tree) +{ + int i; + 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; + } + /* Finally add link to anyPolicy */ + if (last->anyPolicy && + level_add_node(curr, cache->anyPolicy, last->anyPolicy, NULL) == 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. + * + * Return value: <= 0 error, otherwise one of: + * + * X509_PCY_TREE_VALID: valid tree + * X509_PCY_TREE_EMPTY: empty tree + */ +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 X509_PCY_TREE_EMPTY; + break; + } + } + return X509_PCY_TREE_VALID; +} + +/* + * Return value: 1 on success, 0 otherwise. + */ +static int tree_add_auth_node(STACK_OF(X509_POLICY_NODE) **pnodes, + X509_POLICY_NODE *pcy) +{ + if (*pnodes == NULL && + (*pnodes = policy_node_cmp_new()) == NULL) + return 0; + if (sk_X509_POLICY_NODE_find(*pnodes, pcy) >= 0) + return 1; + return sk_X509_POLICY_NODE_push(*pnodes, pcy) != 0; +} + +#define TREE_CALC_FAILURE 0 +#define TREE_CALC_OK_NOFREE 1 +#define TREE_CALC_OK_DOFREE 2 + +/*- + * 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. + * + * Return value: + * TREE_CALC_FAILURE on failure, + * TREE_CALC_OK_NOFREE on success and pnodes need not be freed, + * TREE_CALC_OK_DOFREE on success and pnodes needs to 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 TREE_CALC_FAILURE; + 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) == NULL) + 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)) { + if (addnodes == pnodes) { + sk_X509_POLICY_NODE_free(*pnodes); + *pnodes = NULL; + } + return TREE_CALC_FAILURE; + } + } + } + if (addnodes == pnodes) + return TREE_CALC_OK_DOFREE; + + *pnodes = tree->auth_policies; + return TREE_CALC_OK_NOFREE; +} + +/* + * Return value: 1 on success, 0 otherwise. + */ +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 == NULL) + 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; +} + +/*- + * Return value: <= 0 error, otherwise one of: + * X509_PCY_TREE_VALID: valid tree + * X509_PCY_TREE_EMPTY: empty tree + * (see tree_prune()). + */ +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 X509_PCY_TREE_INTERNAL; + + if (!(curr->flags & X509_V_FLAG_INHIBIT_ANY) + && !tree_link_any(curr, cache, tree)) + return X509_PCY_TREE_INTERNAL; + TREE_PRINT("before tree_prune()", tree, curr); + ret = tree_prune(tree, curr); + if (ret != X509_PCY_TREE_VALID) + return ret; + } + return X509_PCY_TREE_VALID; +} + +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++) { + X509_free(curr->cert); + sk_X509_POLICY_NODE_pop_free(curr->nodes, policy_node_free); + policy_node_free(curr->anyPolicy); + } + + 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: + * X509_PCY_TREE_FAILURE: Failure to satisfy explicit policy + * X509_PCY_TREE_INVALID: Inconsistent or invalid extensions + * X509_PCY_TREE_INTERNAL: Internal error, most likely malloc + * X509_PCY_TREE_VALID: Success (null tree if empty or bare TA) + */ +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 init_ret; + int ret; + int calc_ret; + X509_POLICY_TREE *tree = NULL; + STACK_OF(X509_POLICY_NODE) *nodes, *auth_nodes = NULL; + + *ptree = NULL; + *pexplicit_policy = 0; + init_ret = tree_init(&tree, certs, flags); + + if (init_ret <= 0) + return init_ret; + + if ((init_ret & X509_PCY_TREE_EXPLICIT) == 0) { + if (init_ret & X509_PCY_TREE_EMPTY) { + X509_policy_tree_free(tree); + return X509_PCY_TREE_VALID; + } + } else { + *pexplicit_policy = 1; + /* Tree empty and requireExplicit True: Error */ + if (init_ret & X509_PCY_TREE_EMPTY) + return X509_PCY_TREE_FAILURE; + } + + ret = tree_evaluate(tree); + TREE_PRINT("tree_evaluate()", tree, NULL); + if (ret <= 0) + goto error; + + if (ret == X509_PCY_TREE_EMPTY) { + X509_policy_tree_free(tree); + if (init_ret & X509_PCY_TREE_EXPLICIT) + return X509_PCY_TREE_FAILURE; + return X509_PCY_TREE_VALID; + } + + /* Tree is not empty: continue */ + + if ((calc_ret = tree_calculate_authority_set(tree, &auth_nodes)) == 0) + goto error; + ret = tree_calculate_user_set(tree, policy_oids, auth_nodes); + if (calc_ret == TREE_CALC_OK_DOFREE) + sk_X509_POLICY_NODE_free(auth_nodes); + if (!ret) + goto error; + + *ptree = tree; + + if (init_ret & X509_PCY_TREE_EXPLICIT) { + nodes = X509_policy_tree_get0_user_policies(tree); + if (sk_X509_POLICY_NODE_num(nodes) <= 0) + return X509_PCY_TREE_FAILURE; + } + return X509_PCY_TREE_VALID; + + error: + X509_policy_tree_free(tree); + return X509_PCY_TREE_INTERNAL; +} diff --git a/crypto/x509/standard_exts.h b/crypto/x509/standard_exts.h new file mode 100644 index 0000000000..bc63696799 --- /dev/null +++ b/crypto/x509/standard_exts.h @@ -0,0 +1,78 @@ +/* + * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * This table will be searched using OBJ_bsearch so it *must* kept in order + * of the ext_nid values. + */ + +static const X509V3_EXT_METHOD *standard_exts[] = { + &v3_nscert, + &v3_ns_ia5_list[0], + &v3_ns_ia5_list[1], + &v3_ns_ia5_list[2], + &v3_ns_ia5_list[3], + &v3_ns_ia5_list[4], + &v3_ns_ia5_list[5], + &v3_ns_ia5_list[6], + &v3_skey_id, + &v3_key_usage, + &v3_pkey_usage_period, + &v3_alt[0], + &v3_alt[1], + &v3_bcons, + &v3_crl_num, + &v3_cpols, + &v3_akey_id, + &v3_crld, + &v3_ext_ku, + &v3_delta_crl, + &v3_crl_reason, +#ifndef OPENSSL_NO_OCSP + &v3_crl_invdate, +#endif + &v3_sxnet, + &v3_info, +#ifndef OPENSSL_NO_RFC3779 + &v3_addr, + &v3_asid, +#endif +#ifndef OPENSSL_NO_OCSP + &v3_ocsp_nonce, + &v3_ocsp_crlid, + &v3_ocsp_accresp, + &v3_ocsp_nocheck, + &v3_ocsp_acutoff, + &v3_ocsp_serviceloc, +#endif + &v3_sinfo, + &v3_policy_constraints, +#ifndef OPENSSL_NO_OCSP + &v3_crl_hold, +#endif + &v3_pci, + &v3_name_constraints, + &v3_policy_mappings, + &v3_inhibit_anyp, + &v3_idp, + &v3_alt[2], + &v3_freshest_crl, +#ifndef OPENSSL_NO_CT + &v3_ct_scts[0], + &v3_ct_scts[1], + &v3_ct_scts[2], +#endif + &v3_tls_feature, + &v3_ext_admission +}; + +/* Number of standard extensions */ + +#define STANDARD_EXTENSION_COUNT OSSL_NELEM(standard_exts) + diff --git a/crypto/x509/v3_addr.c b/crypto/x509/v3_addr.c new file mode 100644 index 0000000000..9e6bffed28 --- /dev/null +++ b/crypto/x509/v3_addr.c @@ -0,0 +1,1315 @@ +/* + * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Implementation of RFC 3779 section 2.2. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/buffer.h> +#include <openssl/x509v3.h> +#include "internal/x509_int.h" +#include "ext_dat.h" + +#ifndef OPENSSL_NO_RFC3779 + +/* + * OpenSSL ASN.1 template translation of RFC 3779 2.2.3. + */ + +ASN1_SEQUENCE(IPAddressRange) = { + ASN1_SIMPLE(IPAddressRange, min, ASN1_BIT_STRING), + ASN1_SIMPLE(IPAddressRange, max, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END(IPAddressRange) + +ASN1_CHOICE(IPAddressOrRange) = { + ASN1_SIMPLE(IPAddressOrRange, u.addressPrefix, ASN1_BIT_STRING), + ASN1_SIMPLE(IPAddressOrRange, u.addressRange, IPAddressRange) +} ASN1_CHOICE_END(IPAddressOrRange) + +ASN1_CHOICE(IPAddressChoice) = { + ASN1_SIMPLE(IPAddressChoice, u.inherit, ASN1_NULL), + ASN1_SEQUENCE_OF(IPAddressChoice, u.addressesOrRanges, IPAddressOrRange) +} ASN1_CHOICE_END(IPAddressChoice) + +ASN1_SEQUENCE(IPAddressFamily) = { + ASN1_SIMPLE(IPAddressFamily, addressFamily, ASN1_OCTET_STRING), + ASN1_SIMPLE(IPAddressFamily, ipAddressChoice, IPAddressChoice) +} ASN1_SEQUENCE_END(IPAddressFamily) + +ASN1_ITEM_TEMPLATE(IPAddrBlocks) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, + IPAddrBlocks, IPAddressFamily) +static_ASN1_ITEM_TEMPLATE_END(IPAddrBlocks) + +IMPLEMENT_ASN1_FUNCTIONS(IPAddressRange) +IMPLEMENT_ASN1_FUNCTIONS(IPAddressOrRange) +IMPLEMENT_ASN1_FUNCTIONS(IPAddressChoice) +IMPLEMENT_ASN1_FUNCTIONS(IPAddressFamily) + +/* + * How much buffer space do we need for a raw address? + */ +#define ADDR_RAW_BUF_LEN 16 + +/* + * What's the address length associated with this AFI? + */ +static int length_from_afi(const unsigned afi) +{ + switch (afi) { + case IANA_AFI_IPV4: + return 4; + case IANA_AFI_IPV6: + return 16; + default: + return 0; + } +} + +/* + * Extract the AFI from an IPAddressFamily. + */ +unsigned int X509v3_addr_get_afi(const IPAddressFamily *f) +{ + if (f == NULL + || f->addressFamily == NULL + || f->addressFamily->data == NULL + || f->addressFamily->length < 2) + return 0; + return (f->addressFamily->data[0] << 8) | f->addressFamily->data[1]; +} + +/* + * Expand the bitstring form of an address into a raw byte array. + * At the moment this is coded for simplicity, not speed. + */ +static int addr_expand(unsigned char *addr, + const ASN1_BIT_STRING *bs, + const int length, const unsigned char fill) +{ + if (bs->length < 0 || bs->length > length) + return 0; + if (bs->length > 0) { + memcpy(addr, bs->data, bs->length); + if ((bs->flags & 7) != 0) { + unsigned char mask = 0xFF >> (8 - (bs->flags & 7)); + if (fill == 0) + addr[bs->length - 1] &= ~mask; + else + addr[bs->length - 1] |= mask; + } + } + memset(addr + bs->length, fill, length - bs->length); + return 1; +} + +/* + * Extract the prefix length from a bitstring. + */ +#define addr_prefixlen(bs) ((int) ((bs)->length * 8 - ((bs)->flags & 7))) + +/* + * i2r handler for one address bitstring. + */ +static int i2r_address(BIO *out, + const unsigned afi, + const unsigned char fill, const ASN1_BIT_STRING *bs) +{ + unsigned char addr[ADDR_RAW_BUF_LEN]; + int i, n; + + if (bs->length < 0) + return 0; + switch (afi) { + case IANA_AFI_IPV4: + if (!addr_expand(addr, bs, 4, fill)) + return 0; + BIO_printf(out, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + break; + case IANA_AFI_IPV6: + if (!addr_expand(addr, bs, 16, fill)) + return 0; + for (n = 16; n > 1 && addr[n - 1] == 0x00 && addr[n - 2] == 0x00; + n -= 2) ; + for (i = 0; i < n; i += 2) + BIO_printf(out, "%x%s", (addr[i] << 8) | addr[i + 1], + (i < 14 ? ":" : "")); + if (i < 16) + BIO_puts(out, ":"); + if (i == 0) + BIO_puts(out, ":"); + break; + default: + for (i = 0; i < bs->length; i++) + BIO_printf(out, "%s%02x", (i > 0 ? ":" : ""), bs->data[i]); + BIO_printf(out, "[%d]", (int)(bs->flags & 7)); + break; + } + return 1; +} + +/* + * i2r handler for a sequence of addresses and ranges. + */ +static int i2r_IPAddressOrRanges(BIO *out, + const int indent, + const IPAddressOrRanges *aors, + const unsigned afi) +{ + int i; + for (i = 0; i < sk_IPAddressOrRange_num(aors); i++) { + const IPAddressOrRange *aor = sk_IPAddressOrRange_value(aors, i); + BIO_printf(out, "%*s", indent, ""); + switch (aor->type) { + case IPAddressOrRange_addressPrefix: + if (!i2r_address(out, afi, 0x00, aor->u.addressPrefix)) + return 0; + BIO_printf(out, "/%d\n", addr_prefixlen(aor->u.addressPrefix)); + continue; + case IPAddressOrRange_addressRange: + if (!i2r_address(out, afi, 0x00, aor->u.addressRange->min)) + return 0; + BIO_puts(out, "-"); + if (!i2r_address(out, afi, 0xFF, aor->u.addressRange->max)) + return 0; + BIO_puts(out, "\n"); + continue; + } + } + return 1; +} + +/* + * i2r handler for an IPAddrBlocks extension. + */ +static int i2r_IPAddrBlocks(const X509V3_EXT_METHOD *method, + void *ext, BIO *out, int indent) +{ + const IPAddrBlocks *addr = ext; + int i; + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + const unsigned int afi = X509v3_addr_get_afi(f); + switch (afi) { + case IANA_AFI_IPV4: + BIO_printf(out, "%*sIPv4", indent, ""); + break; + case IANA_AFI_IPV6: + BIO_printf(out, "%*sIPv6", indent, ""); + break; + default: + BIO_printf(out, "%*sUnknown AFI %u", indent, "", afi); + break; + } + if (f->addressFamily->length > 2) { + switch (f->addressFamily->data[2]) { + case 1: + BIO_puts(out, " (Unicast)"); + break; + case 2: + BIO_puts(out, " (Multicast)"); + break; + case 3: + BIO_puts(out, " (Unicast/Multicast)"); + break; + case 4: + BIO_puts(out, " (MPLS)"); + break; + case 64: + BIO_puts(out, " (Tunnel)"); + break; + case 65: + BIO_puts(out, " (VPLS)"); + break; + case 66: + BIO_puts(out, " (BGP MDT)"); + break; + case 128: + BIO_puts(out, " (MPLS-labeled VPN)"); + break; + default: + BIO_printf(out, " (Unknown SAFI %u)", + (unsigned)f->addressFamily->data[2]); + break; + } + } + switch (f->ipAddressChoice->type) { + case IPAddressChoice_inherit: + BIO_puts(out, ": inherit\n"); + break; + case IPAddressChoice_addressesOrRanges: + BIO_puts(out, ":\n"); + if (!i2r_IPAddressOrRanges(out, + indent + 2, + f->ipAddressChoice-> + u.addressesOrRanges, afi)) + return 0; + break; + } + } + return 1; +} + +/* + * Sort comparison function for a sequence of IPAddressOrRange + * elements. + * + * There's no sane answer we can give if addr_expand() fails, and an + * assertion failure on externally supplied data is seriously uncool, + * so we just arbitrarily declare that if given invalid inputs this + * function returns -1. If this messes up your preferred sort order + * for garbage input, tough noogies. + */ +static int IPAddressOrRange_cmp(const IPAddressOrRange *a, + const IPAddressOrRange *b, const int length) +{ + unsigned char addr_a[ADDR_RAW_BUF_LEN], addr_b[ADDR_RAW_BUF_LEN]; + int prefixlen_a = 0, prefixlen_b = 0; + int r; + + switch (a->type) { + case IPAddressOrRange_addressPrefix: + if (!addr_expand(addr_a, a->u.addressPrefix, length, 0x00)) + return -1; + prefixlen_a = addr_prefixlen(a->u.addressPrefix); + break; + case IPAddressOrRange_addressRange: + if (!addr_expand(addr_a, a->u.addressRange->min, length, 0x00)) + return -1; + prefixlen_a = length * 8; + break; + } + + switch (b->type) { + case IPAddressOrRange_addressPrefix: + if (!addr_expand(addr_b, b->u.addressPrefix, length, 0x00)) + return -1; + prefixlen_b = addr_prefixlen(b->u.addressPrefix); + break; + case IPAddressOrRange_addressRange: + if (!addr_expand(addr_b, b->u.addressRange->min, length, 0x00)) + return -1; + prefixlen_b = length * 8; + break; + } + + if ((r = memcmp(addr_a, addr_b, length)) != 0) + return r; + else + return prefixlen_a - prefixlen_b; +} + +/* + * IPv4-specific closure over IPAddressOrRange_cmp, since sk_sort() + * comparison routines are only allowed two arguments. + */ +static int v4IPAddressOrRange_cmp(const IPAddressOrRange *const *a, + const IPAddressOrRange *const *b) +{ + return IPAddressOrRange_cmp(*a, *b, 4); +} + +/* + * IPv6-specific closure over IPAddressOrRange_cmp, since sk_sort() + * comparison routines are only allowed two arguments. + */ +static int v6IPAddressOrRange_cmp(const IPAddressOrRange *const *a, + const IPAddressOrRange *const *b) +{ + return IPAddressOrRange_cmp(*a, *b, 16); +} + +/* + * Calculate whether a range collapses to a prefix. + * See last paragraph of RFC 3779 2.2.3.7. + */ +static int range_should_be_prefix(const unsigned char *min, + const unsigned char *max, const int length) +{ + unsigned char mask; + int i, j; + + if (memcmp(min, max, length) <= 0) + return -1; + for (i = 0; i < length && min[i] == max[i]; i++) ; + for (j = length - 1; j >= 0 && min[j] == 0x00 && max[j] == 0xFF; j--) ; + if (i < j) + return -1; + if (i > j) + return i * 8; + mask = min[i] ^ max[i]; + switch (mask) { + case 0x01: + j = 7; + break; + case 0x03: + j = 6; + break; + case 0x07: + j = 5; + break; + case 0x0F: + j = 4; + break; + case 0x1F: + j = 3; + break; + case 0x3F: + j = 2; + break; + case 0x7F: + j = 1; + break; + default: + return -1; + } + if ((min[i] & mask) != 0 || (max[i] & mask) != mask) + return -1; + else + return i * 8 + j; +} + +/* + * Construct a prefix. + */ +static int make_addressPrefix(IPAddressOrRange **result, + unsigned char *addr, const int prefixlen) +{ + int bytelen = (prefixlen + 7) / 8, bitlen = prefixlen % 8; + IPAddressOrRange *aor = IPAddressOrRange_new(); + + if (aor == NULL) + return 0; + aor->type = IPAddressOrRange_addressPrefix; + if (aor->u.addressPrefix == NULL && + (aor->u.addressPrefix = ASN1_BIT_STRING_new()) == NULL) + goto err; + if (!ASN1_BIT_STRING_set(aor->u.addressPrefix, addr, bytelen)) + goto err; + aor->u.addressPrefix->flags &= ~7; + aor->u.addressPrefix->flags |= ASN1_STRING_FLAG_BITS_LEFT; + if (bitlen > 0) { + aor->u.addressPrefix->data[bytelen - 1] &= ~(0xFF >> bitlen); + aor->u.addressPrefix->flags |= 8 - bitlen; + } + + *result = aor; + return 1; + + err: + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Construct a range. If it can be expressed as a prefix, + * return a prefix instead. Doing this here simplifies + * the rest of the code considerably. + */ +static int make_addressRange(IPAddressOrRange **result, + unsigned char *min, + unsigned char *max, const int length) +{ + IPAddressOrRange *aor; + int i, prefixlen; + + if ((prefixlen = range_should_be_prefix(min, max, length)) >= 0) + return make_addressPrefix(result, min, prefixlen); + + if ((aor = IPAddressOrRange_new()) == NULL) + return 0; + aor->type = IPAddressOrRange_addressRange; + if ((aor->u.addressRange = IPAddressRange_new()) == NULL) + goto err; + if (aor->u.addressRange->min == NULL && + (aor->u.addressRange->min = ASN1_BIT_STRING_new()) == NULL) + goto err; + if (aor->u.addressRange->max == NULL && + (aor->u.addressRange->max = ASN1_BIT_STRING_new()) == NULL) + goto err; + + for (i = length; i > 0 && min[i - 1] == 0x00; --i) ; + if (!ASN1_BIT_STRING_set(aor->u.addressRange->min, min, i)) + goto err; + aor->u.addressRange->min->flags &= ~7; + aor->u.addressRange->min->flags |= ASN1_STRING_FLAG_BITS_LEFT; + if (i > 0) { + unsigned char b = min[i - 1]; + int j = 1; + while ((b & (0xFFU >> j)) != 0) + ++j; + aor->u.addressRange->min->flags |= 8 - j; + } + + for (i = length; i > 0 && max[i - 1] == 0xFF; --i) ; + if (!ASN1_BIT_STRING_set(aor->u.addressRange->max, max, i)) + goto err; + aor->u.addressRange->max->flags &= ~7; + aor->u.addressRange->max->flags |= ASN1_STRING_FLAG_BITS_LEFT; + if (i > 0) { + unsigned char b = max[i - 1]; + int j = 1; + while ((b & (0xFFU >> j)) != (0xFFU >> j)) + ++j; + aor->u.addressRange->max->flags |= 8 - j; + } + + *result = aor; + return 1; + + err: + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Construct a new address family or find an existing one. + */ +static IPAddressFamily *make_IPAddressFamily(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi) +{ + IPAddressFamily *f; + unsigned char key[3]; + int keylen; + int i; + + key[0] = (afi >> 8) & 0xFF; + key[1] = afi & 0xFF; + if (safi != NULL) { + key[2] = *safi & 0xFF; + keylen = 3; + } else { + keylen = 2; + } + + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + f = sk_IPAddressFamily_value(addr, i); + if (f->addressFamily->length == keylen && + !memcmp(f->addressFamily->data, key, keylen)) + return f; + } + + if ((f = IPAddressFamily_new()) == NULL) + goto err; + if (f->ipAddressChoice == NULL && + (f->ipAddressChoice = IPAddressChoice_new()) == NULL) + goto err; + if (f->addressFamily == NULL && + (f->addressFamily = ASN1_OCTET_STRING_new()) == NULL) + goto err; + if (!ASN1_OCTET_STRING_set(f->addressFamily, key, keylen)) + goto err; + if (!sk_IPAddressFamily_push(addr, f)) + goto err; + + return f; + + err: + IPAddressFamily_free(f); + return NULL; +} + +/* + * Add an inheritance element. + */ +int X509v3_addr_add_inherit(IPAddrBlocks *addr, + const unsigned afi, const unsigned *safi) +{ + IPAddressFamily *f = make_IPAddressFamily(addr, afi, safi); + if (f == NULL || + f->ipAddressChoice == NULL || + (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges && + f->ipAddressChoice->u.addressesOrRanges != NULL)) + return 0; + if (f->ipAddressChoice->type == IPAddressChoice_inherit && + f->ipAddressChoice->u.inherit != NULL) + return 1; + if (f->ipAddressChoice->u.inherit == NULL && + (f->ipAddressChoice->u.inherit = ASN1_NULL_new()) == NULL) + return 0; + f->ipAddressChoice->type = IPAddressChoice_inherit; + return 1; +} + +/* + * Construct an IPAddressOrRange sequence, or return an existing one. + */ +static IPAddressOrRanges *make_prefix_or_range(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi) +{ + IPAddressFamily *f = make_IPAddressFamily(addr, afi, safi); + IPAddressOrRanges *aors = NULL; + + if (f == NULL || + f->ipAddressChoice == NULL || + (f->ipAddressChoice->type == IPAddressChoice_inherit && + f->ipAddressChoice->u.inherit != NULL)) + return NULL; + if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges) + aors = f->ipAddressChoice->u.addressesOrRanges; + if (aors != NULL) + return aors; + if ((aors = sk_IPAddressOrRange_new_null()) == NULL) + return NULL; + switch (afi) { + case IANA_AFI_IPV4: + (void)sk_IPAddressOrRange_set_cmp_func(aors, v4IPAddressOrRange_cmp); + break; + case IANA_AFI_IPV6: + (void)sk_IPAddressOrRange_set_cmp_func(aors, v6IPAddressOrRange_cmp); + break; + } + f->ipAddressChoice->type = IPAddressChoice_addressesOrRanges; + f->ipAddressChoice->u.addressesOrRanges = aors; + return aors; +} + +/* + * Add a prefix. + */ +int X509v3_addr_add_prefix(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi, + unsigned char *a, const int prefixlen) +{ + IPAddressOrRanges *aors = make_prefix_or_range(addr, afi, safi); + IPAddressOrRange *aor; + if (aors == NULL || !make_addressPrefix(&aor, a, prefixlen)) + return 0; + if (sk_IPAddressOrRange_push(aors, aor)) + return 1; + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Add a range. + */ +int X509v3_addr_add_range(IPAddrBlocks *addr, + const unsigned afi, + const unsigned *safi, + unsigned char *min, unsigned char *max) +{ + IPAddressOrRanges *aors = make_prefix_or_range(addr, afi, safi); + IPAddressOrRange *aor; + int length = length_from_afi(afi); + if (aors == NULL) + return 0; + if (!make_addressRange(&aor, min, max, length)) + return 0; + if (sk_IPAddressOrRange_push(aors, aor)) + return 1; + IPAddressOrRange_free(aor); + return 0; +} + +/* + * Extract min and max values from an IPAddressOrRange. + */ +static int extract_min_max(IPAddressOrRange *aor, + unsigned char *min, unsigned char *max, int length) +{ + if (aor == NULL || min == NULL || max == NULL) + return 0; + switch (aor->type) { + case IPAddressOrRange_addressPrefix: + return (addr_expand(min, aor->u.addressPrefix, length, 0x00) && + addr_expand(max, aor->u.addressPrefix, length, 0xFF)); + case IPAddressOrRange_addressRange: + return (addr_expand(min, aor->u.addressRange->min, length, 0x00) && + addr_expand(max, aor->u.addressRange->max, length, 0xFF)); + } + return 0; +} + +/* + * Public wrapper for extract_min_max(). + */ +int X509v3_addr_get_range(IPAddressOrRange *aor, + const unsigned afi, + unsigned char *min, + unsigned char *max, const int length) +{ + int afi_length = length_from_afi(afi); + if (aor == NULL || min == NULL || max == NULL || + afi_length == 0 || length < afi_length || + (aor->type != IPAddressOrRange_addressPrefix && + aor->type != IPAddressOrRange_addressRange) || + !extract_min_max(aor, min, max, afi_length)) + return 0; + + return afi_length; +} + +/* + * Sort comparison function for a sequence of IPAddressFamily. + * + * The last paragraph of RFC 3779 2.2.3.3 is slightly ambiguous about + * the ordering: I can read it as meaning that IPv6 without a SAFI + * comes before IPv4 with a SAFI, which seems pretty weird. The + * examples in appendix B suggest that the author intended the + * null-SAFI rule to apply only within a single AFI, which is what I + * would have expected and is what the following code implements. + */ +static int IPAddressFamily_cmp(const IPAddressFamily *const *a_, + const IPAddressFamily *const *b_) +{ + const ASN1_OCTET_STRING *a = (*a_)->addressFamily; + const ASN1_OCTET_STRING *b = (*b_)->addressFamily; + int len = ((a->length <= b->length) ? a->length : b->length); + int cmp = memcmp(a->data, b->data, len); + return cmp ? cmp : a->length - b->length; +} + +/* + * Check whether an IPAddrBLocks is in canonical form. + */ +int X509v3_addr_is_canonical(IPAddrBlocks *addr) +{ + unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; + unsigned char b_min[ADDR_RAW_BUF_LEN], b_max[ADDR_RAW_BUF_LEN]; + IPAddressOrRanges *aors; + int i, j, k; + + /* + * Empty extension is canonical. + */ + if (addr == NULL) + return 1; + + /* + * Check whether the top-level list is in order. + */ + for (i = 0; i < sk_IPAddressFamily_num(addr) - 1; i++) { + const IPAddressFamily *a = sk_IPAddressFamily_value(addr, i); + const IPAddressFamily *b = sk_IPAddressFamily_value(addr, i + 1); + if (IPAddressFamily_cmp(&a, &b) >= 0) + return 0; + } + + /* + * Top level's ok, now check each address family. + */ + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + int length = length_from_afi(X509v3_addr_get_afi(f)); + + /* + * Inheritance is canonical. Anything other than inheritance or + * a SEQUENCE OF IPAddressOrRange is an ASN.1 error or something. + */ + if (f == NULL || f->ipAddressChoice == NULL) + return 0; + switch (f->ipAddressChoice->type) { + case IPAddressChoice_inherit: + continue; + case IPAddressChoice_addressesOrRanges: + break; + default: + return 0; + } + + /* + * It's an IPAddressOrRanges sequence, check it. + */ + aors = f->ipAddressChoice->u.addressesOrRanges; + if (sk_IPAddressOrRange_num(aors) == 0) + return 0; + for (j = 0; j < sk_IPAddressOrRange_num(aors) - 1; j++) { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); + IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, j + 1); + + if (!extract_min_max(a, a_min, a_max, length) || + !extract_min_max(b, b_min, b_max, length)) + return 0; + + /* + * Punt misordered list, overlapping start, or inverted range. + */ + if (memcmp(a_min, b_min, length) >= 0 || + memcmp(a_min, a_max, length) > 0 || + memcmp(b_min, b_max, length) > 0) + return 0; + + /* + * Punt if adjacent or overlapping. Check for adjacency by + * subtracting one from b_min first. + */ + for (k = length - 1; k >= 0 && b_min[k]-- == 0x00; k--) ; + if (memcmp(a_max, b_min, length) >= 0) + return 0; + + /* + * Check for range that should be expressed as a prefix. + */ + if (a->type == IPAddressOrRange_addressRange && + range_should_be_prefix(a_min, a_max, length) >= 0) + return 0; + } + + /* + * Check range to see if it's inverted or should be a + * prefix. + */ + j = sk_IPAddressOrRange_num(aors) - 1; + { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); + if (a != NULL && a->type == IPAddressOrRange_addressRange) { + if (!extract_min_max(a, a_min, a_max, length)) + return 0; + if (memcmp(a_min, a_max, length) > 0 || + range_should_be_prefix(a_min, a_max, length) >= 0) + return 0; + } + } + } + + /* + * If we made it through all that, we're happy. + */ + return 1; +} + +/* + * Whack an IPAddressOrRanges into canonical form. + */ +static int IPAddressOrRanges_canonize(IPAddressOrRanges *aors, + const unsigned afi) +{ + int i, j, length = length_from_afi(afi); + + /* + * Sort the IPAddressOrRanges sequence. + */ + sk_IPAddressOrRange_sort(aors); + + /* + * Clean up representation issues, punt on duplicates or overlaps. + */ + for (i = 0; i < sk_IPAddressOrRange_num(aors) - 1; i++) { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, i); + IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, i + 1); + unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; + unsigned char b_min[ADDR_RAW_BUF_LEN], b_max[ADDR_RAW_BUF_LEN]; + + if (!extract_min_max(a, a_min, a_max, length) || + !extract_min_max(b, b_min, b_max, length)) + return 0; + + /* + * Punt inverted ranges. + */ + if (memcmp(a_min, a_max, length) > 0 || + memcmp(b_min, b_max, length) > 0) + return 0; + + /* + * Punt overlaps. + */ + if (memcmp(a_max, b_min, length) >= 0) + return 0; + + /* + * Merge if a and b are adjacent. We check for + * adjacency by subtracting one from b_min first. + */ + for (j = length - 1; j >= 0 && b_min[j]-- == 0x00; j--) ; + if (memcmp(a_max, b_min, length) == 0) { + IPAddressOrRange *merged; + if (!make_addressRange(&merged, a_min, b_max, length)) + return 0; + (void)sk_IPAddressOrRange_set(aors, i, merged); + (void)sk_IPAddressOrRange_delete(aors, i + 1); + IPAddressOrRange_free(a); + IPAddressOrRange_free(b); + --i; + continue; + } + } + + /* + * Check for inverted final range. + */ + j = sk_IPAddressOrRange_num(aors) - 1; + { + IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j); + if (a != NULL && a->type == IPAddressOrRange_addressRange) { + unsigned char a_min[ADDR_RAW_BUF_LEN], a_max[ADDR_RAW_BUF_LEN]; + if (!extract_min_max(a, a_min, a_max, length)) + return 0; + if (memcmp(a_min, a_max, length) > 0) + return 0; + } + } + + return 1; +} + +/* + * Whack an IPAddrBlocks extension into canonical form. + */ +int X509v3_addr_canonize(IPAddrBlocks *addr) +{ + int i; + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges && + !IPAddressOrRanges_canonize(f->ipAddressChoice-> + u.addressesOrRanges, + X509v3_addr_get_afi(f))) + return 0; + } + (void)sk_IPAddressFamily_set_cmp_func(addr, IPAddressFamily_cmp); + sk_IPAddressFamily_sort(addr); + if (!ossl_assert(X509v3_addr_is_canonical(addr))) + return 0; + return 1; +} + +/* + * v2i handler for the IPAddrBlocks extension. + */ +static void *v2i_IPAddrBlocks(const struct v3_ext_method *method, + struct v3_ext_ctx *ctx, + STACK_OF(CONF_VALUE) *values) +{ + static const char v4addr_chars[] = "0123456789."; + static const char v6addr_chars[] = "0123456789.:abcdefABCDEF"; + IPAddrBlocks *addr = NULL; + char *s = NULL, *t; + int i; + + if ((addr = sk_IPAddressFamily_new(IPAddressFamily_cmp)) == NULL) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + CONF_VALUE *val = sk_CONF_VALUE_value(values, i); + unsigned char min[ADDR_RAW_BUF_LEN], max[ADDR_RAW_BUF_LEN]; + unsigned afi, *safi = NULL, safi_; + const char *addr_chars = NULL; + int prefixlen, i1, i2, delim, length; + + if (!name_cmp(val->name, "IPv4")) { + afi = IANA_AFI_IPV4; + } else if (!name_cmp(val->name, "IPv6")) { + afi = IANA_AFI_IPV6; + } else if (!name_cmp(val->name, "IPv4-SAFI")) { + afi = IANA_AFI_IPV4; + safi = &safi_; + } else if (!name_cmp(val->name, "IPv6-SAFI")) { + afi = IANA_AFI_IPV6; + safi = &safi_; + } else { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_NAME_ERROR); + X509V3_conf_err(val); + goto err; + } + + switch (afi) { + case IANA_AFI_IPV4: + addr_chars = v4addr_chars; + break; + case IANA_AFI_IPV6: + addr_chars = v6addr_chars; + break; + } + + length = length_from_afi(afi); + + /* + * Handle SAFI, if any, and OPENSSL_strdup() so we can null-terminate + * the other input values. + */ + if (safi != NULL) { + *safi = strtoul(val->value, &t, 0); + t += strspn(t, " \t"); + if (*safi > 0xFF || *t++ != ':') { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, X509V3_R_INVALID_SAFI); + X509V3_conf_err(val); + goto err; + } + t += strspn(t, " \t"); + s = OPENSSL_strdup(t); + } else { + s = OPENSSL_strdup(val->value); + } + if (s == NULL) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* + * Check for inheritance. Not worth additional complexity to + * optimize this (seldom-used) case. + */ + if (strcmp(s, "inherit") == 0) { + if (!X509v3_addr_add_inherit(addr, afi, safi)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_INVALID_INHERITANCE); + X509V3_conf_err(val); + goto err; + } + OPENSSL_free(s); + s = NULL; + continue; + } + + i1 = strspn(s, addr_chars); + i2 = i1 + strspn(s + i1, " \t"); + delim = s[i2++]; + s[i1] = '\0'; + + if (a2i_ipadd(min, s) != length) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, X509V3_R_INVALID_IPADDRESS); + X509V3_conf_err(val); + goto err; + } + + switch (delim) { + case '/': + prefixlen = (int)strtoul(s + i2, &t, 10); + if (t == s + i2 || *t != '\0') { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + if (!X509v3_addr_add_prefix(addr, afi, safi, min, prefixlen)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + case '-': + i1 = i2 + strspn(s + i2, " \t"); + i2 = i1 + strspn(s + i1, addr_chars); + if (i1 == i2 || s[i2] != '\0') { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + if (a2i_ipadd(max, s + i1) != length) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_INVALID_IPADDRESS); + X509V3_conf_err(val); + goto err; + } + if (memcmp(min, max, length_from_afi(afi)) > 0) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + if (!X509v3_addr_add_range(addr, afi, safi, min, max)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + case '\0': + if (!X509v3_addr_add_prefix(addr, afi, safi, min, length * 8)) { + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + default: + X509V3err(X509V3_F_V2I_IPADDRBLOCKS, + X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_err(val); + goto err; + } + + OPENSSL_free(s); + s = NULL; + } + + /* + * Canonize the result, then we're done. + */ + if (!X509v3_addr_canonize(addr)) + goto err; + return addr; + + err: + OPENSSL_free(s); + sk_IPAddressFamily_pop_free(addr, IPAddressFamily_free); + return NULL; +} + +/* + * OpenSSL dispatch + */ +const X509V3_EXT_METHOD v3_addr = { + NID_sbgp_ipAddrBlock, /* nid */ + 0, /* flags */ + ASN1_ITEM_ref(IPAddrBlocks), /* template */ + 0, 0, 0, 0, /* old functions, ignored */ + 0, /* i2s */ + 0, /* s2i */ + 0, /* i2v */ + v2i_IPAddrBlocks, /* v2i */ + i2r_IPAddrBlocks, /* i2r */ + 0, /* r2i */ + NULL /* extension-specific data */ +}; + +/* + * Figure out whether extension sues inheritance. + */ +int X509v3_addr_inherits(IPAddrBlocks *addr) +{ + int i; + if (addr == NULL) + return 0; + for (i = 0; i < sk_IPAddressFamily_num(addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(addr, i); + if (f->ipAddressChoice->type == IPAddressChoice_inherit) + return 1; + } + return 0; +} + +/* + * Figure out whether parent contains child. + */ +static int addr_contains(IPAddressOrRanges *parent, + IPAddressOrRanges *child, int length) +{ + unsigned char p_min[ADDR_RAW_BUF_LEN], p_max[ADDR_RAW_BUF_LEN]; + unsigned char c_min[ADDR_RAW_BUF_LEN], c_max[ADDR_RAW_BUF_LEN]; + int p, c; + + if (child == NULL || parent == child) + return 1; + if (parent == NULL) + return 0; + + p = 0; + for (c = 0; c < sk_IPAddressOrRange_num(child); c++) { + if (!extract_min_max(sk_IPAddressOrRange_value(child, c), + c_min, c_max, length)) + return -1; + for (;; p++) { + if (p >= sk_IPAddressOrRange_num(parent)) + return 0; + if (!extract_min_max(sk_IPAddressOrRange_value(parent, p), + p_min, p_max, length)) + return 0; + if (memcmp(p_max, c_max, length) < 0) + continue; + if (memcmp(p_min, c_min, length) > 0) + return 0; + break; + } + } + + return 1; +} + +/* + * Test whether a is a subset of b. + */ +int X509v3_addr_subset(IPAddrBlocks *a, IPAddrBlocks *b) +{ + int i; + if (a == NULL || a == b) + return 1; + if (b == NULL || X509v3_addr_inherits(a) || X509v3_addr_inherits(b)) + return 0; + (void)sk_IPAddressFamily_set_cmp_func(b, IPAddressFamily_cmp); + for (i = 0; i < sk_IPAddressFamily_num(a); i++) { + IPAddressFamily *fa = sk_IPAddressFamily_value(a, i); + int j = sk_IPAddressFamily_find(b, fa); + IPAddressFamily *fb; + fb = sk_IPAddressFamily_value(b, j); + if (fb == NULL) + return 0; + if (!addr_contains(fb->ipAddressChoice->u.addressesOrRanges, + fa->ipAddressChoice->u.addressesOrRanges, + length_from_afi(X509v3_addr_get_afi(fb)))) + return 0; + } + return 1; +} + +/* + * Validation error handling via callback. + */ +#define validation_err(_err_) \ + do { \ + if (ctx != NULL) { \ + ctx->error = _err_; \ + ctx->error_depth = i; \ + ctx->current_cert = x; \ + ret = ctx->verify_cb(0, ctx); \ + } else { \ + ret = 0; \ + } \ + if (!ret) \ + goto done; \ + } while (0) + +/* + * Core code for RFC 3779 2.3 path validation. + * + * Returns 1 for success, 0 on error. + * + * When returning 0, ctx->error MUST be set to an appropriate value other than + * X509_V_OK. + */ +static int addr_validate_path_internal(X509_STORE_CTX *ctx, + STACK_OF(X509) *chain, + IPAddrBlocks *ext) +{ + IPAddrBlocks *child = NULL; + int i, j, ret = 1; + X509 *x; + + if (!ossl_assert(chain != NULL && sk_X509_num(chain) > 0) + || !ossl_assert(ctx != NULL || ext != NULL) + || !ossl_assert(ctx == NULL || ctx->verify_cb != NULL)) { + if (ctx != NULL) + ctx->error = X509_V_ERR_UNSPECIFIED; + return 0; + } + + /* + * Figure out where to start. If we don't have an extension to + * check, we're done. Otherwise, check canonical form and + * set up for walking up the chain. + */ + if (ext != NULL) { + i = -1; + x = NULL; + } else { + i = 0; + x = sk_X509_value(chain, i); + if ((ext = x->rfc3779_addr) == NULL) + goto done; + } + if (!X509v3_addr_is_canonical(ext)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + (void)sk_IPAddressFamily_set_cmp_func(ext, IPAddressFamily_cmp); + if ((child = sk_IPAddressFamily_dup(ext)) == NULL) { + X509V3err(X509V3_F_ADDR_VALIDATE_PATH_INTERNAL, + ERR_R_MALLOC_FAILURE); + if (ctx != NULL) + ctx->error = X509_V_ERR_OUT_OF_MEM; + ret = 0; + goto done; + } + + /* + * Now walk up the chain. No cert may list resources that its + * parent doesn't list. + */ + for (i++; i < sk_X509_num(chain); i++) { + x = sk_X509_value(chain, i); + if (!X509v3_addr_is_canonical(x->rfc3779_addr)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + if (x->rfc3779_addr == NULL) { + for (j = 0; j < sk_IPAddressFamily_num(child); j++) { + IPAddressFamily *fc = sk_IPAddressFamily_value(child, j); + if (fc->ipAddressChoice->type != IPAddressChoice_inherit) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + break; + } + } + continue; + } + (void)sk_IPAddressFamily_set_cmp_func(x->rfc3779_addr, + IPAddressFamily_cmp); + for (j = 0; j < sk_IPAddressFamily_num(child); j++) { + IPAddressFamily *fc = sk_IPAddressFamily_value(child, j); + int k = sk_IPAddressFamily_find(x->rfc3779_addr, fc); + IPAddressFamily *fp = + sk_IPAddressFamily_value(x->rfc3779_addr, k); + if (fp == NULL) { + if (fc->ipAddressChoice->type == + IPAddressChoice_addressesOrRanges) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + break; + } + continue; + } + if (fp->ipAddressChoice->type == + IPAddressChoice_addressesOrRanges) { + if (fc->ipAddressChoice->type == IPAddressChoice_inherit + || addr_contains(fp->ipAddressChoice->u.addressesOrRanges, + fc->ipAddressChoice->u.addressesOrRanges, + length_from_afi(X509v3_addr_get_afi(fc)))) + sk_IPAddressFamily_set(child, j, fp); + else + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + } + + /* + * Trust anchor can't inherit. + */ + if (x->rfc3779_addr != NULL) { + for (j = 0; j < sk_IPAddressFamily_num(x->rfc3779_addr); j++) { + IPAddressFamily *fp = + sk_IPAddressFamily_value(x->rfc3779_addr, j); + if (fp->ipAddressChoice->type == IPAddressChoice_inherit + && sk_IPAddressFamily_find(child, fp) >= 0) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + + done: + sk_IPAddressFamily_free(child); + return ret; +} + +#undef validation_err + +/* + * RFC 3779 2.3 path validation -- called from X509_verify_cert(). + */ +int X509v3_addr_validate_path(X509_STORE_CTX *ctx) +{ + if (ctx->chain == NULL + || sk_X509_num(ctx->chain) == 0 + || ctx->verify_cb == NULL) { + ctx->error = X509_V_ERR_UNSPECIFIED; + return 0; + } + return addr_validate_path_internal(ctx, ctx->chain, NULL); +} + +/* + * RFC 3779 2.3 path validation of an extension. + * Test whether chain covers extension. + */ +int X509v3_addr_validate_resource_set(STACK_OF(X509) *chain, + IPAddrBlocks *ext, int allow_inheritance) +{ + if (ext == NULL) + return 1; + if (chain == NULL || sk_X509_num(chain) == 0) + return 0; + if (!allow_inheritance && X509v3_addr_inherits(ext)) + return 0; + return addr_validate_path_internal(NULL, chain, ext); +} + +#endif /* OPENSSL_NO_RFC3779 */ diff --git a/crypto/x509/v3_admis.c b/crypto/x509/v3_admis.c new file mode 100644 index 0000000000..d6e4bc4166 --- /dev/null +++ b/crypto/x509/v3_admis.c @@ -0,0 +1,356 @@ +/* + * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/ossl_typ.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> + +#include <openssl/x509v3.h> + +#include <openssl/safestack.h> + +#include "v3_admis.h" +#include "ext_dat.h" + + +ASN1_SEQUENCE(NAMING_AUTHORITY) = { + ASN1_OPT(NAMING_AUTHORITY, namingAuthorityId, ASN1_OBJECT), + ASN1_OPT(NAMING_AUTHORITY, namingAuthorityUrl, ASN1_IA5STRING), + ASN1_OPT(NAMING_AUTHORITY, namingAuthorityText, DIRECTORYSTRING), +} ASN1_SEQUENCE_END(NAMING_AUTHORITY) + +ASN1_SEQUENCE(PROFESSION_INFO) = { + ASN1_EXP_OPT(PROFESSION_INFO, namingAuthority, NAMING_AUTHORITY, 0), + ASN1_SEQUENCE_OF(PROFESSION_INFO, professionItems, DIRECTORYSTRING), + ASN1_SEQUENCE_OF_OPT(PROFESSION_INFO, professionOIDs, ASN1_OBJECT), + ASN1_OPT(PROFESSION_INFO, registrationNumber, ASN1_PRINTABLESTRING), + ASN1_OPT(PROFESSION_INFO, addProfessionInfo, ASN1_OCTET_STRING), +} ASN1_SEQUENCE_END(PROFESSION_INFO) + +ASN1_SEQUENCE(ADMISSIONS) = { + ASN1_EXP_OPT(ADMISSIONS, admissionAuthority, GENERAL_NAME, 0), + ASN1_EXP_OPT(ADMISSIONS, namingAuthority, NAMING_AUTHORITY, 1), + ASN1_SEQUENCE_OF(ADMISSIONS, professionInfos, PROFESSION_INFO), +} ASN1_SEQUENCE_END(ADMISSIONS) + +ASN1_SEQUENCE(ADMISSION_SYNTAX) = { + ASN1_OPT(ADMISSION_SYNTAX, admissionAuthority, GENERAL_NAME), + ASN1_SEQUENCE_OF(ADMISSION_SYNTAX, contentsOfAdmissions, ADMISSIONS), +} ASN1_SEQUENCE_END(ADMISSION_SYNTAX) + +IMPLEMENT_ASN1_FUNCTIONS(NAMING_AUTHORITY) +IMPLEMENT_ASN1_FUNCTIONS(PROFESSION_INFO) +IMPLEMENT_ASN1_FUNCTIONS(ADMISSIONS) +IMPLEMENT_ASN1_FUNCTIONS(ADMISSION_SYNTAX) + +static int i2r_ADMISSION_SYNTAX(const struct v3_ext_method *method, void *in, + BIO *bp, int ind); + +const X509V3_EXT_METHOD v3_ext_admission = { + NID_x509ExtAdmission, /* .ext_nid = */ + 0, /* .ext_flags = */ + ASN1_ITEM_ref(ADMISSION_SYNTAX), /* .it = */ + NULL, NULL, NULL, NULL, + NULL, /* .i2s = */ + NULL, /* .s2i = */ + NULL, /* .i2v = */ + NULL, /* .v2i = */ + &i2r_ADMISSION_SYNTAX, /* .i2r = */ + NULL, /* .r2i = */ + NULL /* extension-specific data */ +}; + + +static int i2r_NAMING_AUTHORITY(const struct v3_ext_method *method, void *in, + BIO *bp, int ind) +{ + NAMING_AUTHORITY * namingAuthority = (NAMING_AUTHORITY*) in; + + if (namingAuthority == NULL) + return 0; + + if (namingAuthority->namingAuthorityId == NULL + && namingAuthority->namingAuthorityText == NULL + && namingAuthority->namingAuthorityUrl == NULL) + return 0; + + if (BIO_printf(bp, "%*snamingAuthority: ", ind, "") <= 0) + goto err; + + if (namingAuthority->namingAuthorityId != NULL) { + char objbuf[128]; + const char *ln = OBJ_nid2ln(OBJ_obj2nid(namingAuthority->namingAuthorityId)); + + if (BIO_printf(bp, "%*s admissionAuthorityId: ", ind, "") <= 0) + goto err; + + OBJ_obj2txt(objbuf, sizeof(objbuf), namingAuthority->namingAuthorityId, 1); + + if (BIO_printf(bp, "%s%s%s%s\n", ln ? ln : "", + ln ? " (" : "", objbuf, ln ? ")" : "") <= 0) + goto err; + } + if (namingAuthority->namingAuthorityText != NULL) { + if (BIO_printf(bp, "%*s namingAuthorityText: ", ind, "") <= 0 + || ASN1_STRING_print(bp, namingAuthority->namingAuthorityText) <= 0 + || BIO_printf(bp, "\n") <= 0) + goto err; + } + if (namingAuthority->namingAuthorityUrl != NULL ) { + if (BIO_printf(bp, "%*s namingAuthorityUrl: ", ind, "") <= 0 + || ASN1_STRING_print(bp, namingAuthority->namingAuthorityUrl) <= 0 + || BIO_printf(bp, "\n") <= 0) + goto err; + } + return 1; + +err: + return 0; +} + +static int i2r_ADMISSION_SYNTAX(const struct v3_ext_method *method, void *in, + BIO *bp, int ind) +{ + ADMISSION_SYNTAX * admission = (ADMISSION_SYNTAX *)in; + int i, j, k; + + if (admission->admissionAuthority != NULL) { + if (BIO_printf(bp, "%*sadmissionAuthority:\n", ind, "") <= 0 + || BIO_printf(bp, "%*s ", ind, "") <= 0 + || GENERAL_NAME_print(bp, admission->admissionAuthority) <= 0 + || BIO_printf(bp, "\n") <= 0) + goto err; + } + + for (i = 0; i < sk_ADMISSIONS_num(admission->contentsOfAdmissions); i++) { + ADMISSIONS* entry = sk_ADMISSIONS_value(admission->contentsOfAdmissions, i); + + if (BIO_printf(bp, "%*sEntry %0d:\n", ind, "", 1 + i) <= 0) goto err; + + if (entry->admissionAuthority != NULL) { + if (BIO_printf(bp, "%*s admissionAuthority:\n", ind, "") <= 0 + || BIO_printf(bp, "%*s ", ind, "") <= 0 + || GENERAL_NAME_print(bp, entry->admissionAuthority) <= 0 + || BIO_printf(bp, "\n") <= 0) + goto err; + } + + if (entry->namingAuthority != NULL) { + if (i2r_NAMING_AUTHORITY(method, entry->namingAuthority, bp, ind) <= 0) + goto err; + } + + for (j = 0; j < sk_PROFESSION_INFO_num(entry->professionInfos); j++) { + PROFESSION_INFO* pinfo = sk_PROFESSION_INFO_value(entry->professionInfos, j); + + if (BIO_printf(bp, "%*s Profession Info Entry %0d:\n", ind, "", 1 + j) <= 0) + goto err; + + if (pinfo->registrationNumber != NULL) { + if (BIO_printf(bp, "%*s registrationNumber: ", ind, "") <= 0 + || ASN1_STRING_print(bp, pinfo->registrationNumber) <= 0 + || BIO_printf(bp, "\n") <= 0) + goto err; + } + + if (pinfo->namingAuthority != NULL) { + if (i2r_NAMING_AUTHORITY(method, pinfo->namingAuthority, bp, ind + 2) <= 0) + goto err; + } + + if (pinfo->professionItems != NULL) { + + if (BIO_printf(bp, "%*s Info Entries:\n", ind, "") <= 0) + goto err; + for (k = 0; k < sk_ASN1_STRING_num(pinfo->professionItems); k++) { + ASN1_STRING* val = sk_ASN1_STRING_value(pinfo->professionItems, k); + + if (BIO_printf(bp, "%*s ", ind, "") <= 0 + || ASN1_STRING_print(bp, val) <= 0 + || BIO_printf(bp, "\n") <= 0) + goto err; + } + } + + if (pinfo->professionOIDs != NULL) { + if (BIO_printf(bp, "%*s Profession OIDs:\n", ind, "") <= 0) + goto err; + for (k = 0; k < sk_ASN1_OBJECT_num(pinfo->professionOIDs); k++) { + ASN1_OBJECT* obj = sk_ASN1_OBJECT_value(pinfo->professionOIDs, k); + const char *ln = OBJ_nid2ln(OBJ_obj2nid(obj)); + char objbuf[128]; + + OBJ_obj2txt(objbuf, sizeof(objbuf), obj, 1); + if (BIO_printf(bp, "%*s %s%s%s%s\n", ind, "", + ln ? ln : "", ln ? " (" : "", + objbuf, ln ? ")" : "") <= 0) + goto err; + } + } + } + } + return 1; + +err: + return -1; +} + +const ASN1_OBJECT *NAMING_AUTHORITY_get0_authorityId(const NAMING_AUTHORITY *n) +{ + return n->namingAuthorityId; +} + +void NAMING_AUTHORITY_set0_authorityId(NAMING_AUTHORITY *n, ASN1_OBJECT* id) +{ + ASN1_OBJECT_free(n->namingAuthorityId); + n->namingAuthorityId = id; +} + +const ASN1_IA5STRING *NAMING_AUTHORITY_get0_authorityURL( + const NAMING_AUTHORITY *n) +{ + return n->namingAuthorityUrl; +} + +void NAMING_AUTHORITY_set0_authorityURL(NAMING_AUTHORITY *n, ASN1_IA5STRING* u) +{ + ASN1_IA5STRING_free(n->namingAuthorityUrl); + n->namingAuthorityUrl = u; +} + +const ASN1_STRING *NAMING_AUTHORITY_get0_authorityText( + const NAMING_AUTHORITY *n) +{ + return n->namingAuthorityText; +} + +void NAMING_AUTHORITY_set0_authorityText(NAMING_AUTHORITY *n, ASN1_STRING* t) +{ + ASN1_IA5STRING_free(n->namingAuthorityText); + n->namingAuthorityText = t; +} + +const GENERAL_NAME *ADMISSION_SYNTAX_get0_admissionAuthority(const ADMISSION_SYNTAX *as) +{ + return as->admissionAuthority; +} + +void ADMISSION_SYNTAX_set0_admissionAuthority(ADMISSION_SYNTAX *as, + GENERAL_NAME *aa) +{ + GENERAL_NAME_free(as->admissionAuthority); + as->admissionAuthority = aa; +} + +const STACK_OF(ADMISSIONS) *ADMISSION_SYNTAX_get0_contentsOfAdmissions(const ADMISSION_SYNTAX *as) +{ + return as->contentsOfAdmissions; +} + +void ADMISSION_SYNTAX_set0_contentsOfAdmissions(ADMISSION_SYNTAX *as, + STACK_OF(ADMISSIONS) *a) +{ + sk_ADMISSIONS_pop_free(as->contentsOfAdmissions, ADMISSIONS_free); + as->contentsOfAdmissions = a; +} + +const GENERAL_NAME *ADMISSIONS_get0_admissionAuthority(const ADMISSIONS *a) +{ + return a->admissionAuthority; +} + +void ADMISSIONS_set0_admissionAuthority(ADMISSIONS *a, GENERAL_NAME *aa) +{ + GENERAL_NAME_free(a->admissionAuthority); + a->admissionAuthority = aa; +} + +const NAMING_AUTHORITY *ADMISSIONS_get0_namingAuthority(const ADMISSIONS *a) +{ + return a->namingAuthority; +} + +void ADMISSIONS_set0_namingAuthority(ADMISSIONS *a, NAMING_AUTHORITY *na) +{ + NAMING_AUTHORITY_free(a->namingAuthority); + a->namingAuthority = na; +} + +const PROFESSION_INFOS *ADMISSIONS_get0_professionInfos(const ADMISSIONS *a) +{ + return a->professionInfos; +} + +void ADMISSIONS_set0_professionInfos(ADMISSIONS *a, PROFESSION_INFOS *pi) +{ + sk_PROFESSION_INFO_pop_free(a->professionInfos, PROFESSION_INFO_free); + a->professionInfos = pi; +} + +const ASN1_OCTET_STRING *PROFESSION_INFO_get0_addProfessionInfo(const PROFESSION_INFO *pi) +{ + return pi->addProfessionInfo; +} + +void PROFESSION_INFO_set0_addProfessionInfo(PROFESSION_INFO *pi, + ASN1_OCTET_STRING *aos) +{ + ASN1_OCTET_STRING_free(pi->addProfessionInfo); + pi->addProfessionInfo = aos; +} + +const NAMING_AUTHORITY *PROFESSION_INFO_get0_namingAuthority(const PROFESSION_INFO *pi) +{ + return pi->namingAuthority; +} + +void PROFESSION_INFO_set0_namingAuthority(PROFESSION_INFO *pi, + NAMING_AUTHORITY *na) +{ + NAMING_AUTHORITY_free(pi->namingAuthority); + pi->namingAuthority = na; +} + +const STACK_OF(ASN1_STRING) *PROFESSION_INFO_get0_professionItems(const PROFESSION_INFO *pi) +{ + return pi->professionItems; +} + +void PROFESSION_INFO_set0_professionItems(PROFESSION_INFO *pi, + STACK_OF(ASN1_STRING) *as) +{ + sk_ASN1_STRING_pop_free(pi->professionItems, ASN1_STRING_free); + pi->professionItems = as; +} + +const STACK_OF(ASN1_OBJECT) *PROFESSION_INFO_get0_professionOIDs(const PROFESSION_INFO *pi) +{ + return pi->professionOIDs; +} + +void PROFESSION_INFO_set0_professionOIDs(PROFESSION_INFO *pi, + STACK_OF(ASN1_OBJECT) *po) +{ + sk_ASN1_OBJECT_pop_free(pi->professionOIDs, ASN1_OBJECT_free); + pi->professionOIDs = po; +} + +const ASN1_PRINTABLESTRING *PROFESSION_INFO_get0_registrationNumber(const PROFESSION_INFO *pi) +{ + return pi->registrationNumber; +} + +void PROFESSION_INFO_set0_registrationNumber(PROFESSION_INFO *pi, + ASN1_PRINTABLESTRING *rn) +{ + ASN1_PRINTABLESTRING_free(pi->registrationNumber); + pi->registrationNumber = rn; +} diff --git a/crypto/x509/v3_admis.h b/crypto/x509/v3_admis.h new file mode 100644 index 0000000000..9d1535c3ae --- /dev/null +++ b/crypto/x509/v3_admis.h @@ -0,0 +1,38 @@ +/* + * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef HEADER_V3_ADMISSION_H +# define HEADER_V3_ADMISSION_H + +struct NamingAuthority_st { + ASN1_OBJECT* namingAuthorityId; + ASN1_IA5STRING* namingAuthorityUrl; + ASN1_STRING* namingAuthorityText; /* i.e. DIRECTORYSTRING */ +}; + +struct ProfessionInfo_st { + NAMING_AUTHORITY* namingAuthority; + STACK_OF(ASN1_STRING)* professionItems; /* i.e. DIRECTORYSTRING */ + STACK_OF(ASN1_OBJECT)* professionOIDs; + ASN1_PRINTABLESTRING* registrationNumber; + ASN1_OCTET_STRING* addProfessionInfo; +}; + +struct Admissions_st { + GENERAL_NAME* admissionAuthority; + NAMING_AUTHORITY* namingAuthority; + STACK_OF(PROFESSION_INFO)* professionInfos; +}; + +struct AdmissionSyntax_st { + GENERAL_NAME* admissionAuthority; + STACK_OF(ADMISSIONS)* contentsOfAdmissions; +}; + +#endif diff --git a/crypto/x509/v3_akey.c b/crypto/x509/v3_akey.c new file mode 100644 index 0000000000..b656b4b502 --- /dev/null +++ b/crypto/x509/v3_akey.c @@ -0,0 +1,160 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + AUTHORITY_KEYID *akeyid, + STACK_OF(CONF_VALUE) + *extlist); +static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values); + +const X509V3_EXT_METHOD v3_akey_id = { + NID_authority_key_identifier, + X509V3_EXT_MULTILINE, ASN1_ITEM_ref(AUTHORITY_KEYID), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_AUTHORITY_KEYID, + (X509V3_EXT_V2I)v2i_AUTHORITY_KEYID, + 0, 0, + NULL +}; + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + AUTHORITY_KEYID *akeyid, + STACK_OF(CONF_VALUE) + *extlist) +{ + char *tmp; + if (akeyid->keyid) { + tmp = OPENSSL_buf2hexstr(akeyid->keyid->data, akeyid->keyid->length); + X509V3_add_value((akeyid->issuer || akeyid->serial) ? "keyid" : NULL, tmp, &extlist); + OPENSSL_free(tmp); + } + if (akeyid->issuer) + extlist = i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist); + if (akeyid->serial) { + tmp = OPENSSL_buf2hexstr(akeyid->serial->data, akeyid->serial->length); + X509V3_add_value("serial", tmp, &extlist); + OPENSSL_free(tmp); + } + return extlist; +} + +/*- + * Currently two options: + * keyid: use the issuers subject keyid, the value 'always' means its is + * an error if the issuer certificate doesn't have a key id. + * issuer: use the issuers cert issuer and serial number. The default is + * to only use this if keyid is not present. With the option 'always' + * this is always included. + */ + +static AUTHORITY_KEYID *v2i_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values) +{ + char keyid = 0, issuer = 0; + int i; + CONF_VALUE *cnf; + ASN1_OCTET_STRING *ikeyid = NULL; + X509_NAME *isname = NULL; + GENERAL_NAMES *gens = NULL; + GENERAL_NAME *gen = NULL; + ASN1_INTEGER *serial = NULL; + X509_EXTENSION *ext; + X509 *cert; + AUTHORITY_KEYID *akeyid; + + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + cnf = sk_CONF_VALUE_value(values, i); + if (strcmp(cnf->name, "keyid") == 0) { + keyid = 1; + if (cnf->value && strcmp(cnf->value, "always") == 0) + keyid = 2; + } else if (strcmp(cnf->name, "issuer") == 0) { + issuer = 1; + if (cnf->value && strcmp(cnf->value, "always") == 0) + issuer = 2; + } else { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, X509V3_R_UNKNOWN_OPTION); + ERR_add_error_data(2, "name=", cnf->name); + return NULL; + } + } + + if (!ctx || !ctx->issuer_cert) { + if (ctx && (ctx->flags == CTX_TEST)) + return AUTHORITY_KEYID_new(); + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, + X509V3_R_NO_ISSUER_CERTIFICATE); + return NULL; + } + + cert = ctx->issuer_cert; + + if (keyid) { + i = X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1); + if ((i >= 0) && (ext = X509_get_ext(cert, i))) + ikeyid = X509V3_EXT_d2i(ext); + if (keyid == 2 && !ikeyid) { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, + X509V3_R_UNABLE_TO_GET_ISSUER_KEYID); + return NULL; + } + } + + if ((issuer && !ikeyid) || (issuer == 2)) { + isname = X509_NAME_dup(X509_get_issuer_name(cert)); + serial = ASN1_INTEGER_dup(X509_get_serialNumber(cert)); + if (!isname || !serial) { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, + X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS); + goto err; + } + } + + if ((akeyid = AUTHORITY_KEYID_new()) == NULL) + goto err; + + if (isname) { + if ((gens = sk_GENERAL_NAME_new_null()) == NULL + || (gen = GENERAL_NAME_new()) == NULL + || !sk_GENERAL_NAME_push(gens, gen)) { + X509V3err(X509V3_F_V2I_AUTHORITY_KEYID, ERR_R_MALLOC_FAILURE); + goto err; + } + gen->type = GEN_DIRNAME; + gen->d.dirn = isname; + } + + akeyid->issuer = gens; + gen = NULL; + gens = NULL; + akeyid->serial = serial; + akeyid->keyid = ikeyid; + + return akeyid; + + err: + sk_GENERAL_NAME_free(gens); + GENERAL_NAME_free(gen); + X509_NAME_free(isname); + ASN1_INTEGER_free(serial); + ASN1_OCTET_STRING_free(ikeyid); + return NULL; +} diff --git a/crypto/x509/v3_akeya.c b/crypto/x509/v3_akeya.c new file mode 100644 index 0000000000..aae8c21e73 --- /dev/null +++ b/crypto/x509/v3_akeya.c @@ -0,0 +1,23 @@ +/* + * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> + +ASN1_SEQUENCE(AUTHORITY_KEYID) = { + ASN1_IMP_OPT(AUTHORITY_KEYID, keyid, ASN1_OCTET_STRING, 0), + ASN1_IMP_SEQUENCE_OF_OPT(AUTHORITY_KEYID, issuer, GENERAL_NAME, 1), + ASN1_IMP_OPT(AUTHORITY_KEYID, serial, ASN1_INTEGER, 2) +} ASN1_SEQUENCE_END(AUTHORITY_KEYID) + +IMPLEMENT_ASN1_FUNCTIONS(AUTHORITY_KEYID) diff --git a/crypto/x509/v3_alt.c b/crypto/x509/v3_alt.c new file mode 100644 index 0000000000..9b627e071c --- /dev/null +++ b/crypto/x509/v3_alt.c @@ -0,0 +1,591 @@ +/* + * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static GENERAL_NAMES *v2i_subject_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static GENERAL_NAMES *v2i_issuer_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static int copy_email(X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p); +static int copy_issuer(X509V3_CTX *ctx, GENERAL_NAMES *gens); +static int do_othername(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx); +static int do_dirname(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx); + +const X509V3_EXT_METHOD v3_alt[3] = { + {NID_subject_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_GENERAL_NAMES, + (X509V3_EXT_V2I)v2i_subject_alt, + NULL, NULL, NULL}, + + {NID_issuer_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_GENERAL_NAMES, + (X509V3_EXT_V2I)v2i_issuer_alt, + NULL, NULL, NULL}, + + {NID_certificate_issuer, 0, ASN1_ITEM_ref(GENERAL_NAMES), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_GENERAL_NAMES, + NULL, NULL, NULL, NULL}, +}; + +STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(X509V3_EXT_METHOD *method, + GENERAL_NAMES *gens, + STACK_OF(CONF_VALUE) *ret) +{ + int i; + GENERAL_NAME *gen; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + ret = i2v_GENERAL_NAME(method, gen, ret); + } + if (!ret) + return sk_CONF_VALUE_new_null(); + return ret; +} + +STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method, + GENERAL_NAME *gen, + STACK_OF(CONF_VALUE) *ret) +{ + unsigned char *p; + char oline[256], htmp[5]; + int i; + + switch (gen->type) { + case GEN_OTHERNAME: + if (!X509V3_add_value("othername", "<unsupported>", &ret)) + return NULL; + break; + + case GEN_X400: + if (!X509V3_add_value("X400Name", "<unsupported>", &ret)) + return NULL; + break; + + case GEN_EDIPARTY: + if (!X509V3_add_value("EdiPartyName", "<unsupported>", &ret)) + return NULL; + break; + + case GEN_EMAIL: + if (!X509V3_add_value_uchar("email", gen->d.ia5->data, &ret)) + return NULL; + break; + + case GEN_DNS: + if (!X509V3_add_value_uchar("DNS", gen->d.ia5->data, &ret)) + return NULL; + break; + + case GEN_URI: + if (!X509V3_add_value_uchar("URI", gen->d.ia5->data, &ret)) + return NULL; + break; + + case GEN_DIRNAME: + if (X509_NAME_oneline(gen->d.dirn, oline, sizeof(oline)) == NULL + || !X509V3_add_value("DirName", oline, &ret)) + return NULL; + break; + + case GEN_IPADD: + p = gen->d.ip->data; + if (gen->d.ip->length == 4) + BIO_snprintf(oline, sizeof(oline), "%d.%d.%d.%d", + p[0], p[1], p[2], p[3]); + else if (gen->d.ip->length == 16) { + oline[0] = 0; + for (i = 0; i < 8; i++) { + BIO_snprintf(htmp, sizeof(htmp), "%X", p[0] << 8 | p[1]); + p += 2; + strcat(oline, htmp); + if (i != 7) + strcat(oline, ":"); + } + } else { + if (!X509V3_add_value("IP Address", "<invalid>", &ret)) + return NULL; + break; + } + if (!X509V3_add_value("IP Address", oline, &ret)) + return NULL; + break; + + case GEN_RID: + i2t_ASN1_OBJECT(oline, 256, gen->d.rid); + if (!X509V3_add_value("Registered ID", oline, &ret)) + return NULL; + break; + } + return ret; +} + +int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen) +{ + unsigned char *p; + int i; + switch (gen->type) { + case GEN_OTHERNAME: + BIO_printf(out, "othername:<unsupported>"); + break; + + case GEN_X400: + BIO_printf(out, "X400Name:<unsupported>"); + break; + + case GEN_EDIPARTY: + /* Maybe fix this: it is supported now */ + BIO_printf(out, "EdiPartyName:<unsupported>"); + break; + + case GEN_EMAIL: + BIO_printf(out, "email:%s", gen->d.ia5->data); + break; + + case GEN_DNS: + BIO_printf(out, "DNS:%s", gen->d.ia5->data); + break; + + case GEN_URI: + BIO_printf(out, "URI:%s", gen->d.ia5->data); + break; + + case GEN_DIRNAME: + BIO_printf(out, "DirName:"); + X509_NAME_print_ex(out, gen->d.dirn, 0, XN_FLAG_ONELINE); + break; + + case GEN_IPADD: + p = gen->d.ip->data; + if (gen->d.ip->length == 4) + BIO_printf(out, "IP Address:%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + else if (gen->d.ip->length == 16) { + BIO_printf(out, "IP Address"); + for (i = 0; i < 8; i++) { + BIO_printf(out, ":%X", p[0] << 8 | p[1]); + p += 2; + } + } else { + BIO_printf(out, "IP Address:<invalid>"); + break; + } + break; + + case GEN_RID: + BIO_printf(out, "Registered ID:"); + i2a_ASN1_OBJECT(out, gen->d.rid); + break; + } + return 1; +} + +static GENERAL_NAMES *v2i_issuer_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + const int num = sk_CONF_VALUE_num(nval); + GENERAL_NAMES *gens = sk_GENERAL_NAME_new_reserve(NULL, num); + int i; + + if (gens == NULL) { + X509V3err(X509V3_F_V2I_ISSUER_ALT, ERR_R_MALLOC_FAILURE); + sk_GENERAL_NAME_free(gens); + return NULL; + } + for (i = 0; i < num; i++) { + CONF_VALUE *cnf = sk_CONF_VALUE_value(nval, i); + + if (!name_cmp(cnf->name, "issuer") + && cnf->value && strcmp(cnf->value, "copy") == 0) { + if (!copy_issuer(ctx, gens)) + goto err; + } else { + GENERAL_NAME *gen = v2i_GENERAL_NAME(method, ctx, cnf); + + if (gen == NULL) + goto err; + sk_GENERAL_NAME_push(gens, gen); /* no failure as it was reserved */ + } + } + return gens; + err: + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return NULL; +} + +/* Append subject altname of issuer to issuer alt name of subject */ + +static int copy_issuer(X509V3_CTX *ctx, GENERAL_NAMES *gens) +{ + GENERAL_NAMES *ialt; + GENERAL_NAME *gen; + X509_EXTENSION *ext; + int i, num; + + if (ctx && (ctx->flags == CTX_TEST)) + return 1; + if (!ctx || !ctx->issuer_cert) { + X509V3err(X509V3_F_COPY_ISSUER, X509V3_R_NO_ISSUER_DETAILS); + goto err; + } + i = X509_get_ext_by_NID(ctx->issuer_cert, NID_subject_alt_name, -1); + if (i < 0) + return 1; + if ((ext = X509_get_ext(ctx->issuer_cert, i)) == NULL + || (ialt = X509V3_EXT_d2i(ext)) == NULL) { + X509V3err(X509V3_F_COPY_ISSUER, X509V3_R_ISSUER_DECODE_ERROR); + goto err; + } + + num = sk_GENERAL_NAME_num(ialt); + if (!sk_GENERAL_NAME_reserve(gens, num)) { + X509V3err(X509V3_F_COPY_ISSUER, ERR_R_MALLOC_FAILURE); + goto err; + } + + for (i = 0; i < num; i++) { + gen = sk_GENERAL_NAME_value(ialt, i); + sk_GENERAL_NAME_push(gens, gen); /* no failure as it was reserved */ + } + sk_GENERAL_NAME_free(ialt); + + return 1; + + err: + return 0; + +} + +static GENERAL_NAMES *v2i_subject_alt(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + GENERAL_NAMES *gens; + CONF_VALUE *cnf; + const int num = sk_CONF_VALUE_num(nval); + int i; + + gens = sk_GENERAL_NAME_new_reserve(NULL, num); + if (gens == NULL) { + X509V3err(X509V3_F_V2I_SUBJECT_ALT, ERR_R_MALLOC_FAILURE); + sk_GENERAL_NAME_free(gens); + return NULL; + } + + for (i = 0; i < num; i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if (!name_cmp(cnf->name, "email") + && cnf->value && strcmp(cnf->value, "copy") == 0) { + if (!copy_email(ctx, gens, 0)) + goto err; + } else if (!name_cmp(cnf->name, "email") + && cnf->value && strcmp(cnf->value, "move") == 0) { + if (!copy_email(ctx, gens, 1)) + goto err; + } else { + GENERAL_NAME *gen; + if ((gen = v2i_GENERAL_NAME(method, ctx, cnf)) == NULL) + goto err; + sk_GENERAL_NAME_push(gens, gen); /* no failure as it was reserved */ + } + } + return gens; + err: + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return NULL; +} + +/* + * Copy any email addresses in a certificate or request to GENERAL_NAMES + */ + +static int copy_email(X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p) +{ + X509_NAME *nm; + ASN1_IA5STRING *email = NULL; + X509_NAME_ENTRY *ne; + GENERAL_NAME *gen = NULL; + int i = -1; + + if (ctx != NULL && ctx->flags == CTX_TEST) + return 1; + if (ctx == NULL + || (ctx->subject_cert == NULL && ctx->subject_req == NULL)) { + X509V3err(X509V3_F_COPY_EMAIL, X509V3_R_NO_SUBJECT_DETAILS); + goto err; + } + /* Find the subject name */ + if (ctx->subject_cert) + nm = X509_get_subject_name(ctx->subject_cert); + else + nm = X509_REQ_get_subject_name(ctx->subject_req); + + /* Now add any email address(es) to STACK */ + while ((i = X509_NAME_get_index_by_NID(nm, + NID_pkcs9_emailAddress, i)) >= 0) { + ne = X509_NAME_get_entry(nm, i); + email = ASN1_STRING_dup(X509_NAME_ENTRY_get_data(ne)); + if (move_p) { + X509_NAME_delete_entry(nm, i); + X509_NAME_ENTRY_free(ne); + i--; + } + if (email == NULL || (gen = GENERAL_NAME_new()) == NULL) { + X509V3err(X509V3_F_COPY_EMAIL, ERR_R_MALLOC_FAILURE); + goto err; + } + gen->d.ia5 = email; + email = NULL; + gen->type = GEN_EMAIL; + if (!sk_GENERAL_NAME_push(gens, gen)) { + X509V3err(X509V3_F_COPY_EMAIL, ERR_R_MALLOC_FAILURE); + goto err; + } + gen = NULL; + } + + return 1; + + err: + GENERAL_NAME_free(gen); + ASN1_IA5STRING_free(email); + return 0; + +} + +GENERAL_NAMES *v2i_GENERAL_NAMES(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + GENERAL_NAME *gen; + GENERAL_NAMES *gens; + CONF_VALUE *cnf; + const int num = sk_CONF_VALUE_num(nval); + int i; + + gens = sk_GENERAL_NAME_new_reserve(NULL, num); + if (gens == NULL) { + X509V3err(X509V3_F_V2I_GENERAL_NAMES, ERR_R_MALLOC_FAILURE); + sk_GENERAL_NAME_free(gens); + return NULL; + } + + for (i = 0; i < num; i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if ((gen = v2i_GENERAL_NAME(method, ctx, cnf)) == NULL) + goto err; + sk_GENERAL_NAME_push(gens, gen); /* no failure as it was reserved */ + } + return gens; + err: + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return NULL; +} + +GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, CONF_VALUE *cnf) +{ + return v2i_GENERAL_NAME_ex(NULL, method, ctx, cnf, 0); +} + +GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out, + const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, int gen_type, const char *value, + int is_nc) +{ + char is_string = 0; + GENERAL_NAME *gen = NULL; + + if (!value) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_MISSING_VALUE); + return NULL; + } + + if (out) + gen = out; + else { + gen = GENERAL_NAME_new(); + if (gen == NULL) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, ERR_R_MALLOC_FAILURE); + return NULL; + } + } + + switch (gen_type) { + case GEN_URI: + case GEN_EMAIL: + case GEN_DNS: + is_string = 1; + break; + + case GEN_RID: + { + ASN1_OBJECT *obj; + if ((obj = OBJ_txt2obj(value, 0)) == NULL) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_BAD_OBJECT); + ERR_add_error_data(2, "value=", value); + goto err; + } + gen->d.rid = obj; + } + break; + + case GEN_IPADD: + if (is_nc) + gen->d.ip = a2i_IPADDRESS_NC(value); + else + gen->d.ip = a2i_IPADDRESS(value); + if (gen->d.ip == NULL) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_BAD_IP_ADDRESS); + ERR_add_error_data(2, "value=", value); + goto err; + } + break; + + case GEN_DIRNAME: + if (!do_dirname(gen, value, ctx)) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_DIRNAME_ERROR); + goto err; + } + break; + + case GEN_OTHERNAME: + if (!do_othername(gen, value, ctx)) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_OTHERNAME_ERROR); + goto err; + } + break; + default: + X509V3err(X509V3_F_A2I_GENERAL_NAME, X509V3_R_UNSUPPORTED_TYPE); + goto err; + } + + if (is_string) { + if ((gen->d.ia5 = ASN1_IA5STRING_new()) == NULL || + !ASN1_STRING_set(gen->d.ia5, (unsigned char *)value, + strlen(value))) { + X509V3err(X509V3_F_A2I_GENERAL_NAME, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + gen->type = gen_type; + + return gen; + + err: + if (!out) + GENERAL_NAME_free(gen); + return NULL; +} + +GENERAL_NAME *v2i_GENERAL_NAME_ex(GENERAL_NAME *out, + const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, CONF_VALUE *cnf, int is_nc) +{ + int type; + + char *name, *value; + + name = cnf->name; + value = cnf->value; + + if (!value) { + X509V3err(X509V3_F_V2I_GENERAL_NAME_EX, X509V3_R_MISSING_VALUE); + return NULL; + } + + if (!name_cmp(name, "email")) + type = GEN_EMAIL; + else if (!name_cmp(name, "URI")) + type = GEN_URI; + else if (!name_cmp(name, "DNS")) + type = GEN_DNS; + else if (!name_cmp(name, "RID")) + type = GEN_RID; + else if (!name_cmp(name, "IP")) + type = GEN_IPADD; + else if (!name_cmp(name, "dirName")) + type = GEN_DIRNAME; + else if (!name_cmp(name, "otherName")) + type = GEN_OTHERNAME; + else { + X509V3err(X509V3_F_V2I_GENERAL_NAME_EX, X509V3_R_UNSUPPORTED_OPTION); + ERR_add_error_data(2, "name=", name); + return NULL; + } + + return a2i_GENERAL_NAME(out, method, ctx, type, value, is_nc); + +} + +static int do_othername(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx) +{ + char *objtmp = NULL, *p; + int objlen; + + if ((p = strchr(value, ';')) == NULL) + return 0; + if ((gen->d.otherName = OTHERNAME_new()) == NULL) + return 0; + /* + * Free this up because we will overwrite it. no need to free type_id + * because it is static + */ + ASN1_TYPE_free(gen->d.otherName->value); + if ((gen->d.otherName->value = ASN1_generate_v3(p + 1, ctx)) == NULL) + return 0; + objlen = p - value; + objtmp = OPENSSL_strndup(value, objlen); + if (objtmp == NULL) + return 0; + gen->d.otherName->type_id = OBJ_txt2obj(objtmp, 0); + OPENSSL_free(objtmp); + if (!gen->d.otherName->type_id) + return 0; + return 1; +} + +static int do_dirname(GENERAL_NAME *gen, const char *value, X509V3_CTX *ctx) +{ + int ret = 0; + STACK_OF(CONF_VALUE) *sk = NULL; + X509_NAME *nm; + + if ((nm = X509_NAME_new()) == NULL) + goto err; + sk = X509V3_get_section(ctx, value); + if (!sk) { + X509V3err(X509V3_F_DO_DIRNAME, X509V3_R_SECTION_NOT_FOUND); + ERR_add_error_data(2, "section=", value); + goto err; + } + /* FIXME: should allow other character types... */ + ret = X509V3_NAME_from_section(nm, sk, MBSTRING_ASC); + if (!ret) + goto err; + gen->d.dirn = nm; + +err: + if (ret == 0) + X509_NAME_free(nm); + X509V3_section_free(ctx, sk); + return ret; +} diff --git a/crypto/x509/v3_asid.c b/crypto/x509/v3_asid.c new file mode 100644 index 0000000000..1d41380c41 --- /dev/null +++ b/crypto/x509/v3_asid.c @@ -0,0 +1,880 @@ +/* + * Copyright 2006-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Implementation of RFC 3779 section 3.2. + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> +#include <openssl/x509.h> +#include "internal/x509_int.h" +#include <openssl/bn.h> +#include "ext_dat.h" + +#ifndef OPENSSL_NO_RFC3779 + +/* + * OpenSSL ASN.1 template translation of RFC 3779 3.2.3. + */ + +ASN1_SEQUENCE(ASRange) = { + ASN1_SIMPLE(ASRange, min, ASN1_INTEGER), + ASN1_SIMPLE(ASRange, max, ASN1_INTEGER) +} ASN1_SEQUENCE_END(ASRange) + +ASN1_CHOICE(ASIdOrRange) = { + ASN1_SIMPLE(ASIdOrRange, u.id, ASN1_INTEGER), + ASN1_SIMPLE(ASIdOrRange, u.range, ASRange) +} ASN1_CHOICE_END(ASIdOrRange) + +ASN1_CHOICE(ASIdentifierChoice) = { + ASN1_SIMPLE(ASIdentifierChoice, u.inherit, ASN1_NULL), + ASN1_SEQUENCE_OF(ASIdentifierChoice, u.asIdsOrRanges, ASIdOrRange) +} ASN1_CHOICE_END(ASIdentifierChoice) + +ASN1_SEQUENCE(ASIdentifiers) = { + ASN1_EXP_OPT(ASIdentifiers, asnum, ASIdentifierChoice, 0), + ASN1_EXP_OPT(ASIdentifiers, rdi, ASIdentifierChoice, 1) +} ASN1_SEQUENCE_END(ASIdentifiers) + +IMPLEMENT_ASN1_FUNCTIONS(ASRange) +IMPLEMENT_ASN1_FUNCTIONS(ASIdOrRange) +IMPLEMENT_ASN1_FUNCTIONS(ASIdentifierChoice) +IMPLEMENT_ASN1_FUNCTIONS(ASIdentifiers) + +/* + * i2r method for an ASIdentifierChoice. + */ +static int i2r_ASIdentifierChoice(BIO *out, + ASIdentifierChoice *choice, + int indent, const char *msg) +{ + int i; + char *s; + if (choice == NULL) + return 1; + BIO_printf(out, "%*s%s:\n", indent, "", msg); + switch (choice->type) { + case ASIdentifierChoice_inherit: + BIO_printf(out, "%*sinherit\n", indent + 2, ""); + break; + case ASIdentifierChoice_asIdsOrRanges: + for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges); i++) { + ASIdOrRange *aor = + sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + switch (aor->type) { + case ASIdOrRange_id: + if ((s = i2s_ASN1_INTEGER(NULL, aor->u.id)) == NULL) + return 0; + BIO_printf(out, "%*s%s\n", indent + 2, "", s); + OPENSSL_free(s); + break; + case ASIdOrRange_range: + if ((s = i2s_ASN1_INTEGER(NULL, aor->u.range->min)) == NULL) + return 0; + BIO_printf(out, "%*s%s-", indent + 2, "", s); + OPENSSL_free(s); + if ((s = i2s_ASN1_INTEGER(NULL, aor->u.range->max)) == NULL) + return 0; + BIO_printf(out, "%s\n", s); + OPENSSL_free(s); + break; + default: + return 0; + } + } + break; + default: + return 0; + } + return 1; +} + +/* + * i2r method for an ASIdentifier extension. + */ +static int i2r_ASIdentifiers(const X509V3_EXT_METHOD *method, + void *ext, BIO *out, int indent) +{ + ASIdentifiers *asid = ext; + return (i2r_ASIdentifierChoice(out, asid->asnum, indent, + "Autonomous System Numbers") && + i2r_ASIdentifierChoice(out, asid->rdi, indent, + "Routing Domain Identifiers")); +} + +/* + * Sort comparison function for a sequence of ASIdOrRange elements. + */ +static int ASIdOrRange_cmp(const ASIdOrRange *const *a_, + const ASIdOrRange *const *b_) +{ + const ASIdOrRange *a = *a_, *b = *b_; + + assert((a->type == ASIdOrRange_id && a->u.id != NULL) || + (a->type == ASIdOrRange_range && a->u.range != NULL && + a->u.range->min != NULL && a->u.range->max != NULL)); + + assert((b->type == ASIdOrRange_id && b->u.id != NULL) || + (b->type == ASIdOrRange_range && b->u.range != NULL && + b->u.range->min != NULL && b->u.range->max != NULL)); + + if (a->type == ASIdOrRange_id && b->type == ASIdOrRange_id) + return ASN1_INTEGER_cmp(a->u.id, b->u.id); + + if (a->type == ASIdOrRange_range && b->type == ASIdOrRange_range) { + int r = ASN1_INTEGER_cmp(a->u.range->min, b->u.range->min); + return r != 0 ? r : ASN1_INTEGER_cmp(a->u.range->max, + b->u.range->max); + } + + if (a->type == ASIdOrRange_id) + return ASN1_INTEGER_cmp(a->u.id, b->u.range->min); + else + return ASN1_INTEGER_cmp(a->u.range->min, b->u.id); +} + +/* + * Add an inherit element. + */ +int X509v3_asid_add_inherit(ASIdentifiers *asid, int which) +{ + ASIdentifierChoice **choice; + if (asid == NULL) + return 0; + switch (which) { + case V3_ASID_ASNUM: + choice = &asid->asnum; + break; + case V3_ASID_RDI: + choice = &asid->rdi; + break; + default: + return 0; + } + if (*choice == NULL) { + if ((*choice = ASIdentifierChoice_new()) == NULL) + return 0; + if (((*choice)->u.inherit = ASN1_NULL_new()) == NULL) + return 0; + (*choice)->type = ASIdentifierChoice_inherit; + } + return (*choice)->type == ASIdentifierChoice_inherit; +} + +/* + * Add an ID or range to an ASIdentifierChoice. + */ +int X509v3_asid_add_id_or_range(ASIdentifiers *asid, + int which, ASN1_INTEGER *min, ASN1_INTEGER *max) +{ + ASIdentifierChoice **choice; + ASIdOrRange *aor; + if (asid == NULL) + return 0; + switch (which) { + case V3_ASID_ASNUM: + choice = &asid->asnum; + break; + case V3_ASID_RDI: + choice = &asid->rdi; + break; + default: + return 0; + } + if (*choice != NULL && (*choice)->type == ASIdentifierChoice_inherit) + return 0; + if (*choice == NULL) { + if ((*choice = ASIdentifierChoice_new()) == NULL) + return 0; + (*choice)->u.asIdsOrRanges = sk_ASIdOrRange_new(ASIdOrRange_cmp); + if ((*choice)->u.asIdsOrRanges == NULL) + return 0; + (*choice)->type = ASIdentifierChoice_asIdsOrRanges; + } + if ((aor = ASIdOrRange_new()) == NULL) + return 0; + if (max == NULL) { + aor->type = ASIdOrRange_id; + aor->u.id = min; + } else { + aor->type = ASIdOrRange_range; + if ((aor->u.range = ASRange_new()) == NULL) + goto err; + ASN1_INTEGER_free(aor->u.range->min); + aor->u.range->min = min; + ASN1_INTEGER_free(aor->u.range->max); + aor->u.range->max = max; + } + if (!(sk_ASIdOrRange_push((*choice)->u.asIdsOrRanges, aor))) + goto err; + return 1; + + err: + ASIdOrRange_free(aor); + return 0; +} + +/* + * Extract min and max values from an ASIdOrRange. + */ +static int extract_min_max(ASIdOrRange *aor, + ASN1_INTEGER **min, ASN1_INTEGER **max) +{ + if (!ossl_assert(aor != NULL)) + return 0; + switch (aor->type) { + case ASIdOrRange_id: + *min = aor->u.id; + *max = aor->u.id; + return 1; + case ASIdOrRange_range: + *min = aor->u.range->min; + *max = aor->u.range->max; + return 1; + } + + return 0; +} + +/* + * Check whether an ASIdentifierChoice is in canonical form. + */ +static int ASIdentifierChoice_is_canonical(ASIdentifierChoice *choice) +{ + ASN1_INTEGER *a_max_plus_one = NULL; + BIGNUM *bn = NULL; + int i, ret = 0; + + /* + * Empty element or inheritance is canonical. + */ + if (choice == NULL || choice->type == ASIdentifierChoice_inherit) + return 1; + + /* + * If not a list, or if empty list, it's broken. + */ + if (choice->type != ASIdentifierChoice_asIdsOrRanges || + sk_ASIdOrRange_num(choice->u.asIdsOrRanges) == 0) + return 0; + + /* + * It's a list, check it. + */ + for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; i++) { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASIdOrRange *b = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i + 1); + ASN1_INTEGER *a_min = NULL, *a_max = NULL, *b_min = NULL, *b_max = + NULL; + + if (!extract_min_max(a, &a_min, &a_max) + || !extract_min_max(b, &b_min, &b_max)) + goto done; + + /* + * Punt misordered list, overlapping start, or inverted range. + */ + if (ASN1_INTEGER_cmp(a_min, b_min) >= 0 || + ASN1_INTEGER_cmp(a_min, a_max) > 0 || + ASN1_INTEGER_cmp(b_min, b_max) > 0) + goto done; + + /* + * Calculate a_max + 1 to check for adjacency. + */ + if ((bn == NULL && (bn = BN_new()) == NULL) || + ASN1_INTEGER_to_BN(a_max, bn) == NULL || + !BN_add_word(bn, 1) || + (a_max_plus_one = + BN_to_ASN1_INTEGER(bn, a_max_plus_one)) == NULL) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_IS_CANONICAL, + ERR_R_MALLOC_FAILURE); + goto done; + } + + /* + * Punt if adjacent or overlapping. + */ + if (ASN1_INTEGER_cmp(a_max_plus_one, b_min) >= 0) + goto done; + } + + /* + * Check for inverted range. + */ + i = sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; + { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASN1_INTEGER *a_min, *a_max; + if (a != NULL && a->type == ASIdOrRange_range) { + if (!extract_min_max(a, &a_min, &a_max) + || ASN1_INTEGER_cmp(a_min, a_max) > 0) + goto done; + } + } + + ret = 1; + + done: + ASN1_INTEGER_free(a_max_plus_one); + BN_free(bn); + return ret; +} + +/* + * Check whether an ASIdentifier extension is in canonical form. + */ +int X509v3_asid_is_canonical(ASIdentifiers *asid) +{ + return (asid == NULL || + (ASIdentifierChoice_is_canonical(asid->asnum) && + ASIdentifierChoice_is_canonical(asid->rdi))); +} + +/* + * Whack an ASIdentifierChoice into canonical form. + */ +static int ASIdentifierChoice_canonize(ASIdentifierChoice *choice) +{ + ASN1_INTEGER *a_max_plus_one = NULL; + BIGNUM *bn = NULL; + int i, ret = 0; + + /* + * Nothing to do for empty element or inheritance. + */ + if (choice == NULL || choice->type == ASIdentifierChoice_inherit) + return 1; + + /* + * If not a list, or if empty list, it's broken. + */ + if (choice->type != ASIdentifierChoice_asIdsOrRanges || + sk_ASIdOrRange_num(choice->u.asIdsOrRanges) == 0) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + X509V3_R_EXTENSION_VALUE_ERROR); + return 0; + } + + /* + * We have a non-empty list. Sort it. + */ + sk_ASIdOrRange_sort(choice->u.asIdsOrRanges); + + /* + * Now check for errors and suboptimal encoding, rejecting the + * former and fixing the latter. + */ + for (i = 0; i < sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; i++) { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASIdOrRange *b = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i + 1); + ASN1_INTEGER *a_min = NULL, *a_max = NULL, *b_min = NULL, *b_max = + NULL; + + if (!extract_min_max(a, &a_min, &a_max) + || !extract_min_max(b, &b_min, &b_max)) + goto done; + + /* + * Make sure we're properly sorted (paranoia). + */ + if (!ossl_assert(ASN1_INTEGER_cmp(a_min, b_min) <= 0)) + goto done; + + /* + * Punt inverted ranges. + */ + if (ASN1_INTEGER_cmp(a_min, a_max) > 0 || + ASN1_INTEGER_cmp(b_min, b_max) > 0) + goto done; + + /* + * Check for overlaps. + */ + if (ASN1_INTEGER_cmp(a_max, b_min) >= 0) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + X509V3_R_EXTENSION_VALUE_ERROR); + goto done; + } + + /* + * Calculate a_max + 1 to check for adjacency. + */ + if ((bn == NULL && (bn = BN_new()) == NULL) || + ASN1_INTEGER_to_BN(a_max, bn) == NULL || + !BN_add_word(bn, 1) || + (a_max_plus_one = + BN_to_ASN1_INTEGER(bn, a_max_plus_one)) == NULL) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + ERR_R_MALLOC_FAILURE); + goto done; + } + + /* + * If a and b are adjacent, merge them. + */ + if (ASN1_INTEGER_cmp(a_max_plus_one, b_min) == 0) { + ASRange *r; + switch (a->type) { + case ASIdOrRange_id: + if ((r = OPENSSL_malloc(sizeof(*r))) == NULL) { + X509V3err(X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, + ERR_R_MALLOC_FAILURE); + goto done; + } + r->min = a_min; + r->max = b_max; + a->type = ASIdOrRange_range; + a->u.range = r; + break; + case ASIdOrRange_range: + ASN1_INTEGER_free(a->u.range->max); + a->u.range->max = b_max; + break; + } + switch (b->type) { + case ASIdOrRange_id: + b->u.id = NULL; + break; + case ASIdOrRange_range: + b->u.range->max = NULL; + break; + } + ASIdOrRange_free(b); + (void)sk_ASIdOrRange_delete(choice->u.asIdsOrRanges, i + 1); + i--; + continue; + } + } + + /* + * Check for final inverted range. + */ + i = sk_ASIdOrRange_num(choice->u.asIdsOrRanges) - 1; + { + ASIdOrRange *a = sk_ASIdOrRange_value(choice->u.asIdsOrRanges, i); + ASN1_INTEGER *a_min, *a_max; + if (a != NULL && a->type == ASIdOrRange_range) { + if (!extract_min_max(a, &a_min, &a_max) + || ASN1_INTEGER_cmp(a_min, a_max) > 0) + goto done; + } + } + + /* Paranoia */ + if (!ossl_assert(ASIdentifierChoice_is_canonical(choice))) + goto done; + + ret = 1; + + done: + ASN1_INTEGER_free(a_max_plus_one); + BN_free(bn); + return ret; +} + +/* + * Whack an ASIdentifier extension into canonical form. + */ +int X509v3_asid_canonize(ASIdentifiers *asid) +{ + return (asid == NULL || + (ASIdentifierChoice_canonize(asid->asnum) && + ASIdentifierChoice_canonize(asid->rdi))); +} + +/* + * v2i method for an ASIdentifier extension. + */ +static void *v2i_ASIdentifiers(const struct v3_ext_method *method, + struct v3_ext_ctx *ctx, + STACK_OF(CONF_VALUE) *values) +{ + ASN1_INTEGER *min = NULL, *max = NULL; + ASIdentifiers *asid = NULL; + int i; + + if ((asid = ASIdentifiers_new()) == NULL) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + CONF_VALUE *val = sk_CONF_VALUE_value(values, i); + int i1 = 0, i2 = 0, i3 = 0, is_range = 0, which = 0; + + /* + * Figure out whether this is an AS or an RDI. + */ + if (!name_cmp(val->name, "AS")) { + which = V3_ASID_ASNUM; + } else if (!name_cmp(val->name, "RDI")) { + which = V3_ASID_RDI; + } else { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_EXTENSION_NAME_ERROR); + X509V3_conf_err(val); + goto err; + } + + /* + * Handle inheritance. + */ + if (strcmp(val->value, "inherit") == 0) { + if (X509v3_asid_add_inherit(asid, which)) + continue; + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_INVALID_INHERITANCE); + X509V3_conf_err(val); + goto err; + } + + /* + * Number, range, or mistake, pick it apart and figure out which. + */ + i1 = strspn(val->value, "0123456789"); + if (val->value[i1] == '\0') { + is_range = 0; + } else { + is_range = 1; + i2 = i1 + strspn(val->value + i1, " \t"); + if (val->value[i2] != '-') { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_INVALID_ASNUMBER); + X509V3_conf_err(val); + goto err; + } + i2++; + i2 = i2 + strspn(val->value + i2, " \t"); + i3 = i2 + strspn(val->value + i2, "0123456789"); + if (val->value[i3] != '\0') { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_INVALID_ASRANGE); + X509V3_conf_err(val); + goto err; + } + } + + /* + * Syntax is ok, read and add it. + */ + if (!is_range) { + if (!X509V3_get_value_int(val, &min)) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + } else { + char *s = OPENSSL_strdup(val->value); + if (s == NULL) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + s[i1] = '\0'; + min = s2i_ASN1_INTEGER(NULL, s); + max = s2i_ASN1_INTEGER(NULL, s + i2); + OPENSSL_free(s); + if (min == NULL || max == NULL) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + if (ASN1_INTEGER_cmp(min, max) > 0) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, + X509V3_R_EXTENSION_VALUE_ERROR); + goto err; + } + } + if (!X509v3_asid_add_id_or_range(asid, which, min, max)) { + X509V3err(X509V3_F_V2I_ASIDENTIFIERS, ERR_R_MALLOC_FAILURE); + goto err; + } + min = max = NULL; + } + + /* + * Canonize the result, then we're done. + */ + if (!X509v3_asid_canonize(asid)) + goto err; + return asid; + + err: + ASIdentifiers_free(asid); + ASN1_INTEGER_free(min); + ASN1_INTEGER_free(max); + return NULL; +} + +/* + * OpenSSL dispatch. + */ +const X509V3_EXT_METHOD v3_asid = { + NID_sbgp_autonomousSysNum, /* nid */ + 0, /* flags */ + ASN1_ITEM_ref(ASIdentifiers), /* template */ + 0, 0, 0, 0, /* old functions, ignored */ + 0, /* i2s */ + 0, /* s2i */ + 0, /* i2v */ + v2i_ASIdentifiers, /* v2i */ + i2r_ASIdentifiers, /* i2r */ + 0, /* r2i */ + NULL /* extension-specific data */ +}; + +/* + * Figure out whether extension uses inheritance. + */ +int X509v3_asid_inherits(ASIdentifiers *asid) +{ + return (asid != NULL && + ((asid->asnum != NULL && + asid->asnum->type == ASIdentifierChoice_inherit) || + (asid->rdi != NULL && + asid->rdi->type == ASIdentifierChoice_inherit))); +} + +/* + * Figure out whether parent contains child. + */ +static int asid_contains(ASIdOrRanges *parent, ASIdOrRanges *child) +{ + ASN1_INTEGER *p_min = NULL, *p_max = NULL, *c_min = NULL, *c_max = NULL; + int p, c; + + if (child == NULL || parent == child) + return 1; + if (parent == NULL) + return 0; + + p = 0; + for (c = 0; c < sk_ASIdOrRange_num(child); c++) { + if (!extract_min_max(sk_ASIdOrRange_value(child, c), &c_min, &c_max)) + return 0; + for (;; p++) { + if (p >= sk_ASIdOrRange_num(parent)) + return 0; + if (!extract_min_max(sk_ASIdOrRange_value(parent, p), &p_min, + &p_max)) + return 0; + if (ASN1_INTEGER_cmp(p_max, c_max) < 0) + continue; + if (ASN1_INTEGER_cmp(p_min, c_min) > 0) + return 0; + break; + } + } + + return 1; +} + +/* + * Test whether a is a subset of b. + */ +int X509v3_asid_subset(ASIdentifiers *a, ASIdentifiers *b) +{ + return (a == NULL || + a == b || + (b != NULL && + !X509v3_asid_inherits(a) && + !X509v3_asid_inherits(b) && + asid_contains(b->asnum->u.asIdsOrRanges, + a->asnum->u.asIdsOrRanges) && + asid_contains(b->rdi->u.asIdsOrRanges, + a->rdi->u.asIdsOrRanges))); +} + +/* + * Validation error handling via callback. + */ +#define validation_err(_err_) \ + do { \ + if (ctx != NULL) { \ + ctx->error = _err_; \ + ctx->error_depth = i; \ + ctx->current_cert = x; \ + ret = ctx->verify_cb(0, ctx); \ + } else { \ + ret = 0; \ + } \ + if (!ret) \ + goto done; \ + } while (0) + +/* + * Core code for RFC 3779 3.3 path validation. + */ +static int asid_validate_path_internal(X509_STORE_CTX *ctx, + STACK_OF(X509) *chain, + ASIdentifiers *ext) +{ + ASIdOrRanges *child_as = NULL, *child_rdi = NULL; + int i, ret = 1, inherit_as = 0, inherit_rdi = 0; + X509 *x; + + if (!ossl_assert(chain != NULL && sk_X509_num(chain) > 0) + || !ossl_assert(ctx != NULL || ext != NULL) + || !ossl_assert(ctx == NULL || ctx->verify_cb != NULL)) { + if (ctx != NULL) + ctx->error = X509_V_ERR_UNSPECIFIED; + return 0; + } + + + /* + * Figure out where to start. If we don't have an extension to + * check, we're done. Otherwise, check canonical form and + * set up for walking up the chain. + */ + if (ext != NULL) { + i = -1; + x = NULL; + } else { + i = 0; + x = sk_X509_value(chain, i); + if ((ext = x->rfc3779_asid) == NULL) + goto done; + } + if (!X509v3_asid_is_canonical(ext)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + if (ext->asnum != NULL) { + switch (ext->asnum->type) { + case ASIdentifierChoice_inherit: + inherit_as = 1; + break; + case ASIdentifierChoice_asIdsOrRanges: + child_as = ext->asnum->u.asIdsOrRanges; + break; + } + } + if (ext->rdi != NULL) { + switch (ext->rdi->type) { + case ASIdentifierChoice_inherit: + inherit_rdi = 1; + break; + case ASIdentifierChoice_asIdsOrRanges: + child_rdi = ext->rdi->u.asIdsOrRanges; + break; + } + } + + /* + * Now walk up the chain. Extensions must be in canonical form, no + * cert may list resources that its parent doesn't list. + */ + for (i++; i < sk_X509_num(chain); i++) { + x = sk_X509_value(chain, i); + if (!ossl_assert(x != NULL)) { + if (ctx != NULL) + ctx->error = X509_V_ERR_UNSPECIFIED; + return 0; + } + if (x->rfc3779_asid == NULL) { + if (child_as != NULL || child_rdi != NULL) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + continue; + } + if (!X509v3_asid_is_canonical(x->rfc3779_asid)) + validation_err(X509_V_ERR_INVALID_EXTENSION); + if (x->rfc3779_asid->asnum == NULL && child_as != NULL) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + child_as = NULL; + inherit_as = 0; + } + if (x->rfc3779_asid->asnum != NULL && + x->rfc3779_asid->asnum->type == + ASIdentifierChoice_asIdsOrRanges) { + if (inherit_as + || asid_contains(x->rfc3779_asid->asnum->u.asIdsOrRanges, + child_as)) { + child_as = x->rfc3779_asid->asnum->u.asIdsOrRanges; + inherit_as = 0; + } else { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + if (x->rfc3779_asid->rdi == NULL && child_rdi != NULL) { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + child_rdi = NULL; + inherit_rdi = 0; + } + if (x->rfc3779_asid->rdi != NULL && + x->rfc3779_asid->rdi->type == ASIdentifierChoice_asIdsOrRanges) { + if (inherit_rdi || + asid_contains(x->rfc3779_asid->rdi->u.asIdsOrRanges, + child_rdi)) { + child_rdi = x->rfc3779_asid->rdi->u.asIdsOrRanges; + inherit_rdi = 0; + } else { + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + } + + /* + * Trust anchor can't inherit. + */ + if (!ossl_assert(x != NULL)) { + if (ctx != NULL) + ctx->error = X509_V_ERR_UNSPECIFIED; + return 0; + } + if (x->rfc3779_asid != NULL) { + if (x->rfc3779_asid->asnum != NULL && + x->rfc3779_asid->asnum->type == ASIdentifierChoice_inherit) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + if (x->rfc3779_asid->rdi != NULL && + x->rfc3779_asid->rdi->type == ASIdentifierChoice_inherit) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + + done: + return ret; +} + +#undef validation_err + +/* + * RFC 3779 3.3 path validation -- called from X509_verify_cert(). + */ +int X509v3_asid_validate_path(X509_STORE_CTX *ctx) +{ + if (ctx->chain == NULL + || sk_X509_num(ctx->chain) == 0 + || ctx->verify_cb == NULL) { + ctx->error = X509_V_ERR_UNSPECIFIED; + return 0; + } + return asid_validate_path_internal(ctx, ctx->chain, NULL); +} + +/* + * RFC 3779 3.3 path validation of an extension. + * Test whether chain covers extension. + */ +int X509v3_asid_validate_resource_set(STACK_OF(X509) *chain, + ASIdentifiers *ext, int allow_inheritance) +{ + if (ext == NULL) + return 1; + if (chain == NULL || sk_X509_num(chain) == 0) + return 0; + if (!allow_inheritance && X509v3_asid_inherits(ext)) + return 0; + return asid_validate_path_internal(NULL, chain, ext); +} + +#endif /* OPENSSL_NO_RFC3779 */ diff --git a/crypto/x509/v3_bcons.c b/crypto/x509/v3_bcons.c new file mode 100644 index 0000000000..02e300229c --- /dev/null +++ b/crypto/x509/v3_bcons.c @@ -0,0 +1,84 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + BASIC_CONSTRAINTS *bcons, + STACK_OF(CONF_VALUE) + *extlist); +static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values); + +const X509V3_EXT_METHOD v3_bcons = { + NID_basic_constraints, 0, + ASN1_ITEM_ref(BASIC_CONSTRAINTS), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_BASIC_CONSTRAINTS, + (X509V3_EXT_V2I)v2i_BASIC_CONSTRAINTS, + NULL, NULL, + NULL +}; + +ASN1_SEQUENCE(BASIC_CONSTRAINTS) = { + ASN1_OPT(BASIC_CONSTRAINTS, ca, ASN1_FBOOLEAN), + ASN1_OPT(BASIC_CONSTRAINTS, pathlen, ASN1_INTEGER) +} ASN1_SEQUENCE_END(BASIC_CONSTRAINTS) + +IMPLEMENT_ASN1_FUNCTIONS(BASIC_CONSTRAINTS) + +static STACK_OF(CONF_VALUE) *i2v_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + BASIC_CONSTRAINTS *bcons, + STACK_OF(CONF_VALUE) + *extlist) +{ + X509V3_add_value_bool("CA", bcons->ca, &extlist); + X509V3_add_value_int("pathlen", bcons->pathlen, &extlist); + return extlist; +} + +static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values) +{ + BASIC_CONSTRAINTS *bcons = NULL; + CONF_VALUE *val; + int i; + + if ((bcons = BASIC_CONSTRAINTS_new()) == NULL) { + X509V3err(X509V3_F_V2I_BASIC_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + val = sk_CONF_VALUE_value(values, i); + if (strcmp(val->name, "CA") == 0) { + if (!X509V3_get_value_bool(val, &bcons->ca)) + goto err; + } else if (strcmp(val->name, "pathlen") == 0) { + if (!X509V3_get_value_int(val, &bcons->pathlen)) + goto err; + } else { + X509V3err(X509V3_F_V2I_BASIC_CONSTRAINTS, X509V3_R_INVALID_NAME); + X509V3_conf_err(val); + goto err; + } + } + return bcons; + err: + BASIC_CONSTRAINTS_free(bcons); + return NULL; +} diff --git a/crypto/x509/v3_bitst.c b/crypto/x509/v3_bitst.c new file mode 100644 index 0000000000..81cdcfb6cf --- /dev/null +++ b/crypto/x509/v3_bitst.c @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static BIT_STRING_BITNAME ns_cert_type_table[] = { + {0, "SSL Client", "client"}, + {1, "SSL Server", "server"}, + {2, "S/MIME", "email"}, + {3, "Object Signing", "objsign"}, + {4, "Unused", "reserved"}, + {5, "SSL CA", "sslCA"}, + {6, "S/MIME CA", "emailCA"}, + {7, "Object Signing CA", "objCA"}, + {-1, NULL, NULL} +}; + +static BIT_STRING_BITNAME key_usage_type_table[] = { + {0, "Digital Signature", "digitalSignature"}, + {1, "Non Repudiation", "nonRepudiation"}, + {2, "Key Encipherment", "keyEncipherment"}, + {3, "Data Encipherment", "dataEncipherment"}, + {4, "Key Agreement", "keyAgreement"}, + {5, "Certificate Sign", "keyCertSign"}, + {6, "CRL Sign", "cRLSign"}, + {7, "Encipher Only", "encipherOnly"}, + {8, "Decipher Only", "decipherOnly"}, + {-1, NULL, NULL} +}; + +const X509V3_EXT_METHOD v3_nscert = +EXT_BITSTRING(NID_netscape_cert_type, ns_cert_type_table); +const X509V3_EXT_METHOD v3_key_usage = +EXT_BITSTRING(NID_key_usage, key_usage_type_table); + +STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method, + ASN1_BIT_STRING *bits, + STACK_OF(CONF_VALUE) *ret) +{ + BIT_STRING_BITNAME *bnam; + for (bnam = method->usr_data; bnam->lname; bnam++) { + if (ASN1_BIT_STRING_get_bit(bits, bnam->bitnum)) + X509V3_add_value(bnam->lname, NULL, &ret); + } + return ret; +} + +ASN1_BIT_STRING *v2i_ASN1_BIT_STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + CONF_VALUE *val; + ASN1_BIT_STRING *bs; + int i; + BIT_STRING_BITNAME *bnam; + if ((bs = ASN1_BIT_STRING_new()) == NULL) { + X509V3err(X509V3_F_V2I_ASN1_BIT_STRING, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + for (bnam = method->usr_data; bnam->lname; bnam++) { + if (strcmp(bnam->sname, val->name) == 0 + || strcmp(bnam->lname, val->name) == 0) { + if (!ASN1_BIT_STRING_set_bit(bs, bnam->bitnum, 1)) { + X509V3err(X509V3_F_V2I_ASN1_BIT_STRING, + ERR_R_MALLOC_FAILURE); + ASN1_BIT_STRING_free(bs); + return NULL; + } + break; + } + } + if (!bnam->lname) { + X509V3err(X509V3_F_V2I_ASN1_BIT_STRING, + X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT); + X509V3_conf_err(val); + ASN1_BIT_STRING_free(bs); + return NULL; + } + } + return bs; +} diff --git a/crypto/x509/v3_conf.c b/crypto/x509/v3_conf.c new file mode 100644 index 0000000000..b7b74c4f25 --- /dev/null +++ b/crypto/x509/v3_conf.c @@ -0,0 +1,511 @@ +/* + * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* extension creation utilities */ + +#include <stdio.h> +#include "internal/ctype.h" +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/x509.h> +#include "internal/x509_int.h" +#include <openssl/x509v3.h> + +static int v3_check_critical(const char **value); +static int v3_check_generic(const char **value); +static X509_EXTENSION *do_ext_nconf(CONF *conf, X509V3_CTX *ctx, int ext_nid, + int crit, const char *value); +static X509_EXTENSION *v3_generic_extension(const char *ext, const char *value, + int crit, int type, + X509V3_CTX *ctx); +static char *conf_lhash_get_string(void *db, const char *section, const char *value); +static STACK_OF(CONF_VALUE) *conf_lhash_get_section(void *db, const char *section); +static X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, + int ext_nid, int crit, void *ext_struc); +static unsigned char *generic_asn1(const char *value, X509V3_CTX *ctx, + long *ext_len); +/* CONF *conf: Config file */ +/* char *name: Name */ +/* char *value: Value */ +X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, const char *name, + const char *value) +{ + int crit; + int ext_type; + X509_EXTENSION *ret; + crit = v3_check_critical(&value); + if ((ext_type = v3_check_generic(&value))) + return v3_generic_extension(name, value, crit, ext_type, ctx); + ret = do_ext_nconf(conf, ctx, OBJ_sn2nid(name), crit, value); + if (!ret) { + X509V3err(X509V3_F_X509V3_EXT_NCONF, X509V3_R_ERROR_IN_EXTENSION); + ERR_add_error_data(4, "name=", name, ", value=", value); + } + return ret; +} + +/* CONF *conf: Config file */ +/* char *value: Value */ +X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid, + const char *value) +{ + int crit; + int ext_type; + crit = v3_check_critical(&value); + if ((ext_type = v3_check_generic(&value))) + return v3_generic_extension(OBJ_nid2sn(ext_nid), + value, crit, ext_type, ctx); + return do_ext_nconf(conf, ctx, ext_nid, crit, value); +} + +/* CONF *conf: Config file */ +/* char *value: Value */ +static X509_EXTENSION *do_ext_nconf(CONF *conf, X509V3_CTX *ctx, int ext_nid, + int crit, const char *value) +{ + const X509V3_EXT_METHOD *method; + X509_EXTENSION *ext; + STACK_OF(CONF_VALUE) *nval; + void *ext_struc; + + if (ext_nid == NID_undef) { + X509V3err(X509V3_F_DO_EXT_NCONF, X509V3_R_UNKNOWN_EXTENSION_NAME); + return NULL; + } + if ((method = X509V3_EXT_get_nid(ext_nid)) == NULL) { + X509V3err(X509V3_F_DO_EXT_NCONF, X509V3_R_UNKNOWN_EXTENSION); + return NULL; + } + /* Now get internal extension representation based on type */ + if (method->v2i) { + if (*value == '@') + nval = NCONF_get_section(conf, value + 1); + else + nval = X509V3_parse_list(value); + if (nval == NULL || sk_CONF_VALUE_num(nval) <= 0) { + X509V3err(X509V3_F_DO_EXT_NCONF, + X509V3_R_INVALID_EXTENSION_STRING); + ERR_add_error_data(4, "name=", OBJ_nid2sn(ext_nid), ",section=", + value); + if (*value != '@') + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + return NULL; + } + ext_struc = method->v2i(method, ctx, nval); + if (*value != '@') + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + if (!ext_struc) + return NULL; + } else if (method->s2i) { + if ((ext_struc = method->s2i(method, ctx, value)) == NULL) + return NULL; + } else if (method->r2i) { + if (!ctx->db || !ctx->db_meth) { + X509V3err(X509V3_F_DO_EXT_NCONF, X509V3_R_NO_CONFIG_DATABASE); + return NULL; + } + if ((ext_struc = method->r2i(method, ctx, value)) == NULL) + return NULL; + } else { + X509V3err(X509V3_F_DO_EXT_NCONF, + X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED); + ERR_add_error_data(2, "name=", OBJ_nid2sn(ext_nid)); + return NULL; + } + + ext = do_ext_i2d(method, ext_nid, crit, ext_struc); + if (method->it) + ASN1_item_free(ext_struc, ASN1_ITEM_ptr(method->it)); + else + method->ext_free(ext_struc); + return ext; + +} + +static X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, + int ext_nid, int crit, void *ext_struc) +{ + unsigned char *ext_der = NULL; + int ext_len; + ASN1_OCTET_STRING *ext_oct = NULL; + X509_EXTENSION *ext; + /* Convert internal representation to DER */ + if (method->it) { + ext_der = NULL; + ext_len = + ASN1_item_i2d(ext_struc, &ext_der, ASN1_ITEM_ptr(method->it)); + if (ext_len < 0) + goto merr; + } else { + unsigned char *p; + + ext_len = method->i2d(ext_struc, NULL); + if ((ext_der = OPENSSL_malloc(ext_len)) == NULL) + goto merr; + p = ext_der; + method->i2d(ext_struc, &p); + } + if ((ext_oct = ASN1_OCTET_STRING_new()) == NULL) + goto merr; + ext_oct->data = ext_der; + ext_der = NULL; + ext_oct->length = ext_len; + + ext = X509_EXTENSION_create_by_NID(NULL, ext_nid, crit, ext_oct); + if (!ext) + goto merr; + ASN1_OCTET_STRING_free(ext_oct); + + return ext; + + merr: + X509V3err(X509V3_F_DO_EXT_I2D, ERR_R_MALLOC_FAILURE); + OPENSSL_free(ext_der); + ASN1_OCTET_STRING_free(ext_oct); + return NULL; + +} + +/* Given an internal structure, nid and critical flag create an extension */ + +X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc) +{ + const X509V3_EXT_METHOD *method; + + if ((method = X509V3_EXT_get_nid(ext_nid)) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_I2D, X509V3_R_UNKNOWN_EXTENSION); + return NULL; + } + return do_ext_i2d(method, ext_nid, crit, ext_struc); +} + +/* Check the extension string for critical flag */ +static int v3_check_critical(const char **value) +{ + const char *p = *value; + if ((strlen(p) < 9) || strncmp(p, "critical,", 9)) + return 0; + p += 9; + while (ossl_isspace(*p)) + p++; + *value = p; + return 1; +} + +/* Check extension string for generic extension and return the type */ +static int v3_check_generic(const char **value) +{ + int gen_type = 0; + const char *p = *value; + if ((strlen(p) >= 4) && strncmp(p, "DER:", 4) == 0) { + p += 4; + gen_type = 1; + } else if ((strlen(p) >= 5) && strncmp(p, "ASN1:", 5) == 0) { + p += 5; + gen_type = 2; + } else + return 0; + + while (ossl_isspace(*p)) + p++; + *value = p; + return gen_type; +} + +/* Create a generic extension: for now just handle DER type */ +static X509_EXTENSION *v3_generic_extension(const char *ext, const char *value, + int crit, int gen_type, + X509V3_CTX *ctx) +{ + unsigned char *ext_der = NULL; + long ext_len = 0; + ASN1_OBJECT *obj = NULL; + ASN1_OCTET_STRING *oct = NULL; + X509_EXTENSION *extension = NULL; + + if ((obj = OBJ_txt2obj(ext, 0)) == NULL) { + X509V3err(X509V3_F_V3_GENERIC_EXTENSION, + X509V3_R_EXTENSION_NAME_ERROR); + ERR_add_error_data(2, "name=", ext); + goto err; + } + + if (gen_type == 1) + ext_der = OPENSSL_hexstr2buf(value, &ext_len); + else if (gen_type == 2) + ext_der = generic_asn1(value, ctx, &ext_len); + + if (ext_der == NULL) { + X509V3err(X509V3_F_V3_GENERIC_EXTENSION, + X509V3_R_EXTENSION_VALUE_ERROR); + ERR_add_error_data(2, "value=", value); + goto err; + } + + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + X509V3err(X509V3_F_V3_GENERIC_EXTENSION, ERR_R_MALLOC_FAILURE); + goto err; + } + + oct->data = ext_der; + oct->length = ext_len; + ext_der = NULL; + + extension = X509_EXTENSION_create_by_OBJ(NULL, obj, crit, oct); + + err: + ASN1_OBJECT_free(obj); + ASN1_OCTET_STRING_free(oct); + OPENSSL_free(ext_der); + return extension; + +} + +static unsigned char *generic_asn1(const char *value, X509V3_CTX *ctx, + long *ext_len) +{ + ASN1_TYPE *typ; + unsigned char *ext_der = NULL; + typ = ASN1_generate_v3(value, ctx); + if (typ == NULL) + return NULL; + *ext_len = i2d_ASN1_TYPE(typ, &ext_der); + ASN1_TYPE_free(typ); + return ext_der; +} + +static void delete_ext(STACK_OF(X509_EXTENSION) *sk, X509_EXTENSION *dext) +{ + int idx; + ASN1_OBJECT *obj; + obj = X509_EXTENSION_get_object(dext); + while ((idx = X509v3_get_ext_by_OBJ(sk, obj, -1)) >= 0) { + X509_EXTENSION *tmpext = X509v3_get_ext(sk, idx); + X509v3_delete_ext(sk, idx); + X509_EXTENSION_free(tmpext); + } +} + +/* + * This is the main function: add a bunch of extensions based on a config + * file section to an extension STACK. + */ + +int X509V3_EXT_add_nconf_sk(CONF *conf, X509V3_CTX *ctx, const char *section, + STACK_OF(X509_EXTENSION) **sk) +{ + X509_EXTENSION *ext; + STACK_OF(CONF_VALUE) *nval; + CONF_VALUE *val; + int i; + + if ((nval = NCONF_get_section(conf, section)) == NULL) + return 0; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if ((ext = X509V3_EXT_nconf(conf, ctx, val->name, val->value)) == NULL) + return 0; + if (ctx->flags == X509V3_CTX_REPLACE) + delete_ext(*sk, ext); + if (sk != NULL) { + if (X509v3_add_ext(sk, ext, -1) == NULL) { + X509_EXTENSION_free(ext); + return 0; + } + } + X509_EXTENSION_free(ext); + } + return 1; +} + +/* + * Convenience functions to add extensions to a certificate, CRL and request + */ + +int X509V3_EXT_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509 *cert) +{ + STACK_OF(X509_EXTENSION) **sk = NULL; + if (cert) + sk = &cert->cert_info.extensions; + return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); +} + +/* Same as above but for a CRL */ + +int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509_CRL *crl) +{ + STACK_OF(X509_EXTENSION) **sk = NULL; + if (crl) + sk = &crl->crl.extensions; + return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); +} + +/* Add extensions to certificate request */ + +int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509_REQ *req) +{ + STACK_OF(X509_EXTENSION) *extlist = NULL, **sk = NULL; + int i; + if (req) + sk = &extlist; + i = X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); + if (!i || !sk) + return i; + i = X509_REQ_add_extensions(req, extlist); + sk_X509_EXTENSION_pop_free(extlist, X509_EXTENSION_free); + return i; +} + +/* Config database functions */ + +char *X509V3_get_string(X509V3_CTX *ctx, const char *name, const char *section) +{ + if (!ctx->db || !ctx->db_meth || !ctx->db_meth->get_string) { + X509V3err(X509V3_F_X509V3_GET_STRING, X509V3_R_OPERATION_NOT_DEFINED); + return NULL; + } + if (ctx->db_meth->get_string) + return ctx->db_meth->get_string(ctx->db, name, section); + return NULL; +} + +STACK_OF(CONF_VALUE) *X509V3_get_section(X509V3_CTX *ctx, const char *section) +{ + if (!ctx->db || !ctx->db_meth || !ctx->db_meth->get_section) { + X509V3err(X509V3_F_X509V3_GET_SECTION, + X509V3_R_OPERATION_NOT_DEFINED); + return NULL; + } + if (ctx->db_meth->get_section) + return ctx->db_meth->get_section(ctx->db, section); + return NULL; +} + +void X509V3_string_free(X509V3_CTX *ctx, char *str) +{ + if (!str) + return; + if (ctx->db_meth->free_string) + ctx->db_meth->free_string(ctx->db, str); +} + +void X509V3_section_free(X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *section) +{ + if (!section) + return; + if (ctx->db_meth->free_section) + ctx->db_meth->free_section(ctx->db, section); +} + +static char *nconf_get_string(void *db, const char *section, const char *value) +{ + return NCONF_get_string(db, section, value); +} + +static STACK_OF(CONF_VALUE) *nconf_get_section(void *db, const char *section) +{ + return NCONF_get_section(db, section); +} + +static X509V3_CONF_METHOD nconf_method = { + nconf_get_string, + nconf_get_section, + NULL, + NULL +}; + +void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf) +{ + ctx->db_meth = &nconf_method; + ctx->db = conf; +} + +void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subj, X509_REQ *req, + X509_CRL *crl, int flags) +{ + ctx->issuer_cert = issuer; + ctx->subject_cert = subj; + ctx->crl = crl; + ctx->subject_req = req; + ctx->flags = flags; +} + +/* Old conf compatibility functions */ + +X509_EXTENSION *X509V3_EXT_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *name, const char *value) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_nconf(&ctmp, ctx, name, value); +} + +/* LHASH *conf: Config file */ +/* char *value: Value */ +X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, + X509V3_CTX *ctx, int ext_nid, const char *value) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_nconf_nid(&ctmp, ctx, ext_nid, value); +} + +static char *conf_lhash_get_string(void *db, const char *section, const char *value) +{ + return CONF_get_string(db, section, value); +} + +static STACK_OF(CONF_VALUE) *conf_lhash_get_section(void *db, const char *section) +{ + return CONF_get_section(db, section); +} + +static X509V3_CONF_METHOD conf_lhash_method = { + conf_lhash_get_string, + conf_lhash_get_section, + NULL, + NULL +}; + +void X509V3_set_conf_lhash(X509V3_CTX *ctx, LHASH_OF(CONF_VALUE) *lhash) +{ + ctx->db_meth = &conf_lhash_method; + ctx->db = lhash; +} + +int X509V3_EXT_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *section, X509 *cert) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_add_nconf(&ctmp, ctx, section, cert); +} + +/* Same as above but for a CRL */ + +int X509V3_EXT_CRL_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *section, X509_CRL *crl) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_CRL_add_nconf(&ctmp, ctx, section, crl); +} + +/* Add extensions to certificate request */ + +int X509V3_EXT_REQ_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, + const char *section, X509_REQ *req) +{ + CONF ctmp; + CONF_set_nconf(&ctmp, conf); + return X509V3_EXT_REQ_add_nconf(&ctmp, ctx, section, req); +} diff --git a/crypto/x509/v3_cpols.c b/crypto/x509/v3_cpols.c new file mode 100644 index 0000000000..595de62361 --- /dev/null +++ b/crypto/x509/v3_cpols.c @@ -0,0 +1,498 @@ +/* + * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> + +#include "pcy_int.h" +#include "ext_dat.h" + +/* Certificate policies extension support: this one is a bit complex... */ + +static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol, + BIO *out, int indent); +static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *value); +static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, + int indent); +static void print_notice(BIO *out, USERNOTICE *notice, int indent); +static POLICYINFO *policy_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *polstrs, int ia5org); +static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *unot, int ia5org); +static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos); +static int displaytext_str2tag(const char *tagstr, unsigned int *tag_len); +static int displaytext_get_tag_len(const char *tagstr); + +const X509V3_EXT_METHOD v3_cpols = { + NID_certificate_policies, 0, ASN1_ITEM_ref(CERTIFICATEPOLICIES), + 0, 0, 0, 0, + 0, 0, + 0, 0, + (X509V3_EXT_I2R)i2r_certpol, + (X509V3_EXT_R2I)r2i_certpol, + NULL +}; + +ASN1_ITEM_TEMPLATE(CERTIFICATEPOLICIES) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, CERTIFICATEPOLICIES, POLICYINFO) +ASN1_ITEM_TEMPLATE_END(CERTIFICATEPOLICIES) + +IMPLEMENT_ASN1_FUNCTIONS(CERTIFICATEPOLICIES) + +ASN1_SEQUENCE(POLICYINFO) = { + ASN1_SIMPLE(POLICYINFO, policyid, ASN1_OBJECT), + ASN1_SEQUENCE_OF_OPT(POLICYINFO, qualifiers, POLICYQUALINFO) +} ASN1_SEQUENCE_END(POLICYINFO) + +IMPLEMENT_ASN1_FUNCTIONS(POLICYINFO) + +ASN1_ADB_TEMPLATE(policydefault) = ASN1_SIMPLE(POLICYQUALINFO, d.other, ASN1_ANY); + +ASN1_ADB(POLICYQUALINFO) = { + ADB_ENTRY(NID_id_qt_cps, ASN1_SIMPLE(POLICYQUALINFO, d.cpsuri, ASN1_IA5STRING)), + ADB_ENTRY(NID_id_qt_unotice, ASN1_SIMPLE(POLICYQUALINFO, d.usernotice, USERNOTICE)) +} ASN1_ADB_END(POLICYQUALINFO, 0, pqualid, 0, &policydefault_tt, NULL); + +ASN1_SEQUENCE(POLICYQUALINFO) = { + ASN1_SIMPLE(POLICYQUALINFO, pqualid, ASN1_OBJECT), + ASN1_ADB_OBJECT(POLICYQUALINFO) +} ASN1_SEQUENCE_END(POLICYQUALINFO) + +IMPLEMENT_ASN1_FUNCTIONS(POLICYQUALINFO) + +ASN1_SEQUENCE(USERNOTICE) = { + ASN1_OPT(USERNOTICE, noticeref, NOTICEREF), + ASN1_OPT(USERNOTICE, exptext, DISPLAYTEXT) +} ASN1_SEQUENCE_END(USERNOTICE) + +IMPLEMENT_ASN1_FUNCTIONS(USERNOTICE) + +ASN1_SEQUENCE(NOTICEREF) = { + ASN1_SIMPLE(NOTICEREF, organization, DISPLAYTEXT), + ASN1_SEQUENCE_OF(NOTICEREF, noticenos, ASN1_INTEGER) +} ASN1_SEQUENCE_END(NOTICEREF) + +IMPLEMENT_ASN1_FUNCTIONS(NOTICEREF) + +static STACK_OF(POLICYINFO) *r2i_certpol(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *value) +{ + STACK_OF(POLICYINFO) *pols; + char *pstr; + POLICYINFO *pol; + ASN1_OBJECT *pobj; + STACK_OF(CONF_VALUE) *vals = X509V3_parse_list(value); + CONF_VALUE *cnf; + const int num = sk_CONF_VALUE_num(vals); + int i, ia5org; + + if (vals == NULL) { + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_X509V3_LIB); + return NULL; + } + + pols = sk_POLICYINFO_new_reserve(NULL, num); + if (pols == NULL) { + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_MALLOC_FAILURE); + goto err; + } + + ia5org = 0; + for (i = 0; i < num; i++) { + cnf = sk_CONF_VALUE_value(vals, i); + + if (cnf->value || !cnf->name) { + X509V3err(X509V3_F_R2I_CERTPOL, + X509V3_R_INVALID_POLICY_IDENTIFIER); + X509V3_conf_err(cnf); + goto err; + } + pstr = cnf->name; + if (strcmp(pstr, "ia5org") == 0) { + ia5org = 1; + continue; + } else if (*pstr == '@') { + STACK_OF(CONF_VALUE) *polsect; + polsect = X509V3_get_section(ctx, pstr + 1); + if (!polsect) { + X509V3err(X509V3_F_R2I_CERTPOL, X509V3_R_INVALID_SECTION); + + X509V3_conf_err(cnf); + goto err; + } + pol = policy_section(ctx, polsect, ia5org); + X509V3_section_free(ctx, polsect); + if (pol == NULL) + goto err; + } else { + if ((pobj = OBJ_txt2obj(cnf->name, 0)) == NULL) { + X509V3err(X509V3_F_R2I_CERTPOL, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(cnf); + goto err; + } + pol = POLICYINFO_new(); + if (pol == NULL) { + ASN1_OBJECT_free(pobj); + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_MALLOC_FAILURE); + goto err; + } + pol->policyid = pobj; + } + if (!sk_POLICYINFO_push(pols, pol)) { + POLICYINFO_free(pol); + X509V3err(X509V3_F_R2I_CERTPOL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + return pols; + err: + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + sk_POLICYINFO_pop_free(pols, POLICYINFO_free); + return NULL; +} + +static POLICYINFO *policy_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *polstrs, int ia5org) +{ + int i; + CONF_VALUE *cnf; + POLICYINFO *pol; + POLICYQUALINFO *qual; + + if ((pol = POLICYINFO_new()) == NULL) + goto merr; + for (i = 0; i < sk_CONF_VALUE_num(polstrs); i++) { + cnf = sk_CONF_VALUE_value(polstrs, i); + if (strcmp(cnf->name, "policyIdentifier") == 0) { + ASN1_OBJECT *pobj; + if ((pobj = OBJ_txt2obj(cnf->value, 0)) == NULL) { + X509V3err(X509V3_F_POLICY_SECTION, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(cnf); + goto err; + } + pol->policyid = pobj; + + } else if (!name_cmp(cnf->name, "CPS")) { + if (pol->qualifiers == NULL) + pol->qualifiers = sk_POLICYQUALINFO_new_null(); + if ((qual = POLICYQUALINFO_new()) == NULL) + goto merr; + if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual)) + goto merr; + if ((qual->pqualid = OBJ_nid2obj(NID_id_qt_cps)) == NULL) { + X509V3err(X509V3_F_POLICY_SECTION, ERR_R_INTERNAL_ERROR); + goto err; + } + if ((qual->d.cpsuri = ASN1_IA5STRING_new()) == NULL) + goto merr; + if (!ASN1_STRING_set(qual->d.cpsuri, cnf->value, + strlen(cnf->value))) + goto merr; + } else if (!name_cmp(cnf->name, "userNotice")) { + STACK_OF(CONF_VALUE) *unot; + if (*cnf->value != '@') { + X509V3err(X509V3_F_POLICY_SECTION, + X509V3_R_EXPECTED_A_SECTION_NAME); + X509V3_conf_err(cnf); + goto err; + } + unot = X509V3_get_section(ctx, cnf->value + 1); + if (!unot) { + X509V3err(X509V3_F_POLICY_SECTION, X509V3_R_INVALID_SECTION); + + X509V3_conf_err(cnf); + goto err; + } + qual = notice_section(ctx, unot, ia5org); + X509V3_section_free(ctx, unot); + if (!qual) + goto err; + if (!pol->qualifiers) + pol->qualifiers = sk_POLICYQUALINFO_new_null(); + if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual)) + goto merr; + } else { + X509V3err(X509V3_F_POLICY_SECTION, X509V3_R_INVALID_OPTION); + + X509V3_conf_err(cnf); + goto err; + } + } + if (!pol->policyid) { + X509V3err(X509V3_F_POLICY_SECTION, X509V3_R_NO_POLICY_IDENTIFIER); + goto err; + } + + return pol; + + merr: + X509V3err(X509V3_F_POLICY_SECTION, ERR_R_MALLOC_FAILURE); + + err: + POLICYINFO_free(pol); + return NULL; +} + +static int displaytext_get_tag_len(const char *tagstr) +{ + char *colon = strchr(tagstr, ':'); + + return (colon == NULL) ? -1 : colon - tagstr; +} + +static int displaytext_str2tag(const char *tagstr, unsigned int *tag_len) +{ + int len; + + *tag_len = 0; + len = displaytext_get_tag_len(tagstr); + + if (len == -1) + return V_ASN1_VISIBLESTRING; + *tag_len = len; + if (len == sizeof("UTF8") - 1 && strncmp(tagstr, "UTF8", len) == 0) + return V_ASN1_UTF8STRING; + if (len == sizeof("UTF8String") - 1 && strncmp(tagstr, "UTF8String", len) == 0) + return V_ASN1_UTF8STRING; + if (len == sizeof("BMP") - 1 && strncmp(tagstr, "BMP", len) == 0) + return V_ASN1_BMPSTRING; + if (len == sizeof("BMPSTRING") - 1 && strncmp(tagstr, "BMPSTRING", len) == 0) + return V_ASN1_BMPSTRING; + if (len == sizeof("VISIBLE") - 1 && strncmp(tagstr, "VISIBLE", len) == 0) + return V_ASN1_VISIBLESTRING; + if (len == sizeof("VISIBLESTRING") - 1 && strncmp(tagstr, "VISIBLESTRING", len) == 0) + return V_ASN1_VISIBLESTRING; + *tag_len = 0; + return V_ASN1_VISIBLESTRING; +} + +static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *unot, int ia5org) +{ + int i, ret, len, tag; + unsigned int tag_len; + CONF_VALUE *cnf; + USERNOTICE *not; + POLICYQUALINFO *qual; + char *value = NULL; + + if ((qual = POLICYQUALINFO_new()) == NULL) + goto merr; + if ((qual->pqualid = OBJ_nid2obj(NID_id_qt_unotice)) == NULL) { + X509V3err(X509V3_F_NOTICE_SECTION, ERR_R_INTERNAL_ERROR); + goto err; + } + if ((not = USERNOTICE_new()) == NULL) + goto merr; + qual->d.usernotice = not; + for (i = 0; i < sk_CONF_VALUE_num(unot); i++) { + cnf = sk_CONF_VALUE_value(unot, i); + value = cnf->value; + if (strcmp(cnf->name, "explicitText") == 0) { + tag = displaytext_str2tag(value, &tag_len); + if ((not->exptext = ASN1_STRING_type_new(tag)) == NULL) + goto merr; + if (tag_len != 0) + value += tag_len + 1; + len = strlen(value); + if (!ASN1_STRING_set(not->exptext, value, len)) + goto merr; + } else if (strcmp(cnf->name, "organization") == 0) { + NOTICEREF *nref; + if (!not->noticeref) { + if ((nref = NOTICEREF_new()) == NULL) + goto merr; + not->noticeref = nref; + } else + nref = not->noticeref; + if (ia5org) + nref->organization->type = V_ASN1_IA5STRING; + else + nref->organization->type = V_ASN1_VISIBLESTRING; + if (!ASN1_STRING_set(nref->organization, cnf->value, + strlen(cnf->value))) + goto merr; + } else if (strcmp(cnf->name, "noticeNumbers") == 0) { + NOTICEREF *nref; + STACK_OF(CONF_VALUE) *nos; + if (!not->noticeref) { + if ((nref = NOTICEREF_new()) == NULL) + goto merr; + not->noticeref = nref; + } else + nref = not->noticeref; + nos = X509V3_parse_list(cnf->value); + if (!nos || !sk_CONF_VALUE_num(nos)) { + X509V3err(X509V3_F_NOTICE_SECTION, X509V3_R_INVALID_NUMBERS); + X509V3_conf_err(cnf); + sk_CONF_VALUE_pop_free(nos, X509V3_conf_free); + goto err; + } + ret = nref_nos(nref->noticenos, nos); + sk_CONF_VALUE_pop_free(nos, X509V3_conf_free); + if (!ret) + goto err; + } else { + X509V3err(X509V3_F_NOTICE_SECTION, X509V3_R_INVALID_OPTION); + X509V3_conf_err(cnf); + goto err; + } + } + + if (not->noticeref && + (!not->noticeref->noticenos || !not->noticeref->organization)) { + X509V3err(X509V3_F_NOTICE_SECTION, + X509V3_R_NEED_ORGANIZATION_AND_NUMBERS); + goto err; + } + + return qual; + + merr: + X509V3err(X509V3_F_NOTICE_SECTION, ERR_R_MALLOC_FAILURE); + + err: + POLICYQUALINFO_free(qual); + return NULL; +} + +static int nref_nos(STACK_OF(ASN1_INTEGER) *nnums, STACK_OF(CONF_VALUE) *nos) +{ + CONF_VALUE *cnf; + ASN1_INTEGER *aint; + + int i; + + for (i = 0; i < sk_CONF_VALUE_num(nos); i++) { + cnf = sk_CONF_VALUE_value(nos, i); + if ((aint = s2i_ASN1_INTEGER(NULL, cnf->name)) == NULL) { + X509V3err(X509V3_F_NREF_NOS, X509V3_R_INVALID_NUMBER); + goto err; + } + if (!sk_ASN1_INTEGER_push(nnums, aint)) + goto merr; + } + return 1; + + merr: + ASN1_INTEGER_free(aint); + X509V3err(X509V3_F_NREF_NOS, ERR_R_MALLOC_FAILURE); + + err: + return 0; +} + +static int i2r_certpol(X509V3_EXT_METHOD *method, STACK_OF(POLICYINFO) *pol, + BIO *out, int indent) +{ + int i; + POLICYINFO *pinfo; + /* First print out the policy OIDs */ + for (i = 0; i < sk_POLICYINFO_num(pol); i++) { + if (i > 0) + BIO_puts(out, "\n"); + pinfo = sk_POLICYINFO_value(pol, i); + BIO_printf(out, "%*sPolicy: ", indent, ""); + i2a_ASN1_OBJECT(out, pinfo->policyid); + if (pinfo->qualifiers) { + BIO_puts(out, "\n"); + print_qualifiers(out, pinfo->qualifiers, indent + 2); + } + } + return 1; +} + +static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, + int indent) +{ + POLICYQUALINFO *qualinfo; + int i; + for (i = 0; i < sk_POLICYQUALINFO_num(quals); i++) { + if (i > 0) + BIO_puts(out, "\n"); + qualinfo = sk_POLICYQUALINFO_value(quals, i); + switch (OBJ_obj2nid(qualinfo->pqualid)) { + case NID_id_qt_cps: + BIO_printf(out, "%*sCPS: %s", indent, "", + qualinfo->d.cpsuri->data); + break; + + case NID_id_qt_unotice: + BIO_printf(out, "%*sUser Notice:\n", indent, ""); + print_notice(out, qualinfo->d.usernotice, indent + 2); + break; + + default: + BIO_printf(out, "%*sUnknown Qualifier: ", indent + 2, ""); + + i2a_ASN1_OBJECT(out, qualinfo->pqualid); + break; + } + } +} + +static void print_notice(BIO *out, USERNOTICE *notice, int indent) +{ + int i; + if (notice->noticeref) { + NOTICEREF *ref; + ref = notice->noticeref; + BIO_printf(out, "%*sOrganization: %s\n", indent, "", + ref->organization->data); + BIO_printf(out, "%*sNumber%s: ", indent, "", + sk_ASN1_INTEGER_num(ref->noticenos) > 1 ? "s" : ""); + for (i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) { + ASN1_INTEGER *num; + char *tmp; + num = sk_ASN1_INTEGER_value(ref->noticenos, i); + if (i) + BIO_puts(out, ", "); + if (num == NULL) + BIO_puts(out, "(null)"); + else { + tmp = i2s_ASN1_INTEGER(NULL, num); + if (tmp == NULL) + return; + BIO_puts(out, tmp); + OPENSSL_free(tmp); + } + } + if (notice->exptext) + BIO_puts(out, "\n"); + } + if (notice->exptext) + BIO_printf(out, "%*sExplicit Text: %s", indent, "", + notice->exptext->data); +} + +void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent) +{ + const X509_POLICY_DATA *dat = node->data; + + BIO_printf(out, "%*sPolicy: ", indent, ""); + + i2a_ASN1_OBJECT(out, dat->valid_policy); + BIO_puts(out, "\n"); + BIO_printf(out, "%*s%s\n", indent + 2, "", + node_data_critical(dat) ? "Critical" : "Non Critical"); + if (dat->qualifier_set) { + print_qualifiers(out, dat->qualifier_set, indent + 2); + BIO_puts(out, "\n"); + } + else + BIO_printf(out, "%*sNo Qualifiers\n", indent + 2, ""); +} diff --git a/crypto/x509/v3_crld.c b/crypto/x509/v3_crld.c new file mode 100644 index 0000000000..766bf0e5c8 --- /dev/null +++ b/crypto/x509/v3_crld.c @@ -0,0 +1,508 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> + +#include "internal/x509_int.h" +#include "ext_dat.h" + +static void *v2i_crld(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); +static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out, + int indent); + +const X509V3_EXT_METHOD v3_crld = { + NID_crl_distribution_points, 0, ASN1_ITEM_ref(CRL_DIST_POINTS), + 0, 0, 0, 0, + 0, 0, + 0, + v2i_crld, + i2r_crldp, 0, + NULL +}; + +const X509V3_EXT_METHOD v3_freshest_crl = { + NID_freshest_crl, 0, ASN1_ITEM_ref(CRL_DIST_POINTS), + 0, 0, 0, 0, + 0, 0, + 0, + v2i_crld, + i2r_crldp, 0, + NULL +}; + +static STACK_OF(GENERAL_NAME) *gnames_from_sectname(X509V3_CTX *ctx, + char *sect) +{ + STACK_OF(CONF_VALUE) *gnsect; + STACK_OF(GENERAL_NAME) *gens; + if (*sect == '@') + gnsect = X509V3_get_section(ctx, sect + 1); + else + gnsect = X509V3_parse_list(sect); + if (!gnsect) { + X509V3err(X509V3_F_GNAMES_FROM_SECTNAME, X509V3_R_SECTION_NOT_FOUND); + return NULL; + } + gens = v2i_GENERAL_NAMES(NULL, ctx, gnsect); + if (*sect == '@') + X509V3_section_free(ctx, gnsect); + else + sk_CONF_VALUE_pop_free(gnsect, X509V3_conf_free); + return gens; +} + +static int set_dist_point_name(DIST_POINT_NAME **pdp, X509V3_CTX *ctx, + CONF_VALUE *cnf) +{ + STACK_OF(GENERAL_NAME) *fnm = NULL; + STACK_OF(X509_NAME_ENTRY) *rnm = NULL; + + if (strncmp(cnf->name, "fullname", 9) == 0) { + fnm = gnames_from_sectname(ctx, cnf->value); + if (!fnm) + goto err; + } else if (strcmp(cnf->name, "relativename") == 0) { + int ret; + STACK_OF(CONF_VALUE) *dnsect; + X509_NAME *nm; + nm = X509_NAME_new(); + if (nm == NULL) + return -1; + dnsect = X509V3_get_section(ctx, cnf->value); + if (!dnsect) { + X509V3err(X509V3_F_SET_DIST_POINT_NAME, + X509V3_R_SECTION_NOT_FOUND); + return -1; + } + ret = X509V3_NAME_from_section(nm, dnsect, MBSTRING_ASC); + X509V3_section_free(ctx, dnsect); + rnm = nm->entries; + nm->entries = NULL; + X509_NAME_free(nm); + if (!ret || sk_X509_NAME_ENTRY_num(rnm) <= 0) + goto err; + /* + * Since its a name fragment can't have more than one RDNSequence + */ + if (sk_X509_NAME_ENTRY_value(rnm, + sk_X509_NAME_ENTRY_num(rnm) - 1)->set) { + X509V3err(X509V3_F_SET_DIST_POINT_NAME, + X509V3_R_INVALID_MULTIPLE_RDNS); + goto err; + } + } else + return 0; + + if (*pdp) { + X509V3err(X509V3_F_SET_DIST_POINT_NAME, + X509V3_R_DISTPOINT_ALREADY_SET); + goto err; + } + + *pdp = DIST_POINT_NAME_new(); + if (*pdp == NULL) + goto err; + if (fnm) { + (*pdp)->type = 0; + (*pdp)->name.fullname = fnm; + } else { + (*pdp)->type = 1; + (*pdp)->name.relativename = rnm; + } + + return 1; + + err: + sk_GENERAL_NAME_pop_free(fnm, GENERAL_NAME_free); + sk_X509_NAME_ENTRY_pop_free(rnm, X509_NAME_ENTRY_free); + return -1; +} + +static const BIT_STRING_BITNAME reason_flags[] = { + {0, "Unused", "unused"}, + {1, "Key Compromise", "keyCompromise"}, + {2, "CA Compromise", "CACompromise"}, + {3, "Affiliation Changed", "affiliationChanged"}, + {4, "Superseded", "superseded"}, + {5, "Cessation Of Operation", "cessationOfOperation"}, + {6, "Certificate Hold", "certificateHold"}, + {7, "Privilege Withdrawn", "privilegeWithdrawn"}, + {8, "AA Compromise", "AACompromise"}, + {-1, NULL, NULL} +}; + +static int set_reasons(ASN1_BIT_STRING **preas, char *value) +{ + STACK_OF(CONF_VALUE) *rsk = NULL; + const BIT_STRING_BITNAME *pbn; + const char *bnam; + int i, ret = 0; + rsk = X509V3_parse_list(value); + if (rsk == NULL) + return 0; + if (*preas != NULL) + goto err; + for (i = 0; i < sk_CONF_VALUE_num(rsk); i++) { + bnam = sk_CONF_VALUE_value(rsk, i)->name; + if (*preas == NULL) { + *preas = ASN1_BIT_STRING_new(); + if (*preas == NULL) + goto err; + } + for (pbn = reason_flags; pbn->lname; pbn++) { + if (strcmp(pbn->sname, bnam) == 0) { + if (!ASN1_BIT_STRING_set_bit(*preas, pbn->bitnum, 1)) + goto err; + break; + } + } + if (!pbn->lname) + goto err; + } + ret = 1; + + err: + sk_CONF_VALUE_pop_free(rsk, X509V3_conf_free); + return ret; +} + +static int print_reasons(BIO *out, const char *rname, + ASN1_BIT_STRING *rflags, int indent) +{ + int first = 1; + const BIT_STRING_BITNAME *pbn; + BIO_printf(out, "%*s%s:\n%*s", indent, "", rname, indent + 2, ""); + for (pbn = reason_flags; pbn->lname; pbn++) { + if (ASN1_BIT_STRING_get_bit(rflags, pbn->bitnum)) { + if (first) + first = 0; + else + BIO_puts(out, ", "); + BIO_puts(out, pbn->lname); + } + } + if (first) + BIO_puts(out, "<EMPTY>\n"); + else + BIO_puts(out, "\n"); + return 1; +} + +static DIST_POINT *crldp_from_section(X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + int i; + CONF_VALUE *cnf; + DIST_POINT *point = DIST_POINT_new(); + + if (point == NULL) + goto err; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + int ret; + cnf = sk_CONF_VALUE_value(nval, i); + ret = set_dist_point_name(&point->distpoint, ctx, cnf); + if (ret > 0) + continue; + if (ret < 0) + goto err; + if (strcmp(cnf->name, "reasons") == 0) { + if (!set_reasons(&point->reasons, cnf->value)) + goto err; + } else if (strcmp(cnf->name, "CRLissuer") == 0) { + point->CRLissuer = gnames_from_sectname(ctx, cnf->value); + if (!point->CRLissuer) + goto err; + } + } + + return point; + + err: + DIST_POINT_free(point); + return NULL; +} + +static void *v2i_crld(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + STACK_OF(DIST_POINT) *crld; + GENERAL_NAMES *gens = NULL; + GENERAL_NAME *gen = NULL; + CONF_VALUE *cnf; + const int num = sk_CONF_VALUE_num(nval); + int i; + + crld = sk_DIST_POINT_new_reserve(NULL, num); + if (crld == NULL) + goto merr; + for (i = 0; i < num; i++) { + DIST_POINT *point; + + cnf = sk_CONF_VALUE_value(nval, i); + if (!cnf->value) { + STACK_OF(CONF_VALUE) *dpsect; + dpsect = X509V3_get_section(ctx, cnf->name); + if (!dpsect) + goto err; + point = crldp_from_section(ctx, dpsect); + X509V3_section_free(ctx, dpsect); + if (!point) + goto err; + sk_DIST_POINT_push(crld, point); /* no failure as it was reserved */ + } else { + if ((gen = v2i_GENERAL_NAME(method, ctx, cnf)) == NULL) + goto err; + if ((gens = GENERAL_NAMES_new()) == NULL) + goto merr; + if (!sk_GENERAL_NAME_push(gens, gen)) + goto merr; + gen = NULL; + if ((point = DIST_POINT_new()) == NULL) + goto merr; + sk_DIST_POINT_push(crld, point); /* no failure as it was reserved */ + if ((point->distpoint = DIST_POINT_NAME_new()) == NULL) + goto merr; + point->distpoint->name.fullname = gens; + point->distpoint->type = 0; + gens = NULL; + } + } + return crld; + + merr: + X509V3err(X509V3_F_V2I_CRLD, ERR_R_MALLOC_FAILURE); + err: + GENERAL_NAME_free(gen); + GENERAL_NAMES_free(gens); + sk_DIST_POINT_pop_free(crld, DIST_POINT_free); + return NULL; +} + +static int dpn_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + DIST_POINT_NAME *dpn = (DIST_POINT_NAME *)*pval; + + switch (operation) { + case ASN1_OP_NEW_POST: + dpn->dpname = NULL; + break; + + case ASN1_OP_FREE_POST: + X509_NAME_free(dpn->dpname); + break; + } + return 1; +} + + +ASN1_CHOICE_cb(DIST_POINT_NAME, dpn_cb) = { + ASN1_IMP_SEQUENCE_OF(DIST_POINT_NAME, name.fullname, GENERAL_NAME, 0), + ASN1_IMP_SET_OF(DIST_POINT_NAME, name.relativename, X509_NAME_ENTRY, 1) +} ASN1_CHOICE_END_cb(DIST_POINT_NAME, DIST_POINT_NAME, type) + + +IMPLEMENT_ASN1_FUNCTIONS(DIST_POINT_NAME) + +ASN1_SEQUENCE(DIST_POINT) = { + ASN1_EXP_OPT(DIST_POINT, distpoint, DIST_POINT_NAME, 0), + ASN1_IMP_OPT(DIST_POINT, reasons, ASN1_BIT_STRING, 1), + ASN1_IMP_SEQUENCE_OF_OPT(DIST_POINT, CRLissuer, GENERAL_NAME, 2) +} ASN1_SEQUENCE_END(DIST_POINT) + +IMPLEMENT_ASN1_FUNCTIONS(DIST_POINT) + +ASN1_ITEM_TEMPLATE(CRL_DIST_POINTS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, CRLDistributionPoints, DIST_POINT) +ASN1_ITEM_TEMPLATE_END(CRL_DIST_POINTS) + +IMPLEMENT_ASN1_FUNCTIONS(CRL_DIST_POINTS) + +ASN1_SEQUENCE(ISSUING_DIST_POINT) = { + ASN1_EXP_OPT(ISSUING_DIST_POINT, distpoint, DIST_POINT_NAME, 0), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyuser, ASN1_FBOOLEAN, 1), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyCA, ASN1_FBOOLEAN, 2), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlysomereasons, ASN1_BIT_STRING, 3), + ASN1_IMP_OPT(ISSUING_DIST_POINT, indirectCRL, ASN1_FBOOLEAN, 4), + ASN1_IMP_OPT(ISSUING_DIST_POINT, onlyattr, ASN1_FBOOLEAN, 5) +} ASN1_SEQUENCE_END(ISSUING_DIST_POINT) + +IMPLEMENT_ASN1_FUNCTIONS(ISSUING_DIST_POINT) + +static int i2r_idp(const X509V3_EXT_METHOD *method, void *pidp, BIO *out, + int indent); +static void *v2i_idp(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); + +const X509V3_EXT_METHOD v3_idp = { + NID_issuing_distribution_point, X509V3_EXT_MULTILINE, + ASN1_ITEM_ref(ISSUING_DIST_POINT), + 0, 0, 0, 0, + 0, 0, + 0, + v2i_idp, + i2r_idp, 0, + NULL +}; + +static void *v2i_idp(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + ISSUING_DIST_POINT *idp = NULL; + CONF_VALUE *cnf; + char *name, *val; + int i, ret; + idp = ISSUING_DIST_POINT_new(); + if (idp == NULL) + goto merr; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + name = cnf->name; + val = cnf->value; + ret = set_dist_point_name(&idp->distpoint, ctx, cnf); + if (ret > 0) + continue; + if (ret < 0) + goto err; + if (strcmp(name, "onlyuser") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->onlyuser)) + goto err; + } else if (strcmp(name, "onlyCA") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->onlyCA)) + goto err; + } else if (strcmp(name, "onlyAA") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->onlyattr)) + goto err; + } else if (strcmp(name, "indirectCRL") == 0) { + if (!X509V3_get_value_bool(cnf, &idp->indirectCRL)) + goto err; + } else if (strcmp(name, "onlysomereasons") == 0) { + if (!set_reasons(&idp->onlysomereasons, val)) + goto err; + } else { + X509V3err(X509V3_F_V2I_IDP, X509V3_R_INVALID_NAME); + X509V3_conf_err(cnf); + goto err; + } + } + return idp; + + merr: + X509V3err(X509V3_F_V2I_IDP, ERR_R_MALLOC_FAILURE); + err: + ISSUING_DIST_POINT_free(idp); + return NULL; +} + +static int print_gens(BIO *out, STACK_OF(GENERAL_NAME) *gens, int indent) +{ + int i; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + if (i > 0) + BIO_puts(out, "\n"); + BIO_printf(out, "%*s", indent + 2, ""); + GENERAL_NAME_print(out, sk_GENERAL_NAME_value(gens, i)); + } + return 1; +} + +static int print_distpoint(BIO *out, DIST_POINT_NAME *dpn, int indent) +{ + if (dpn->type == 0) { + BIO_printf(out, "%*sFull Name:\n", indent, ""); + print_gens(out, dpn->name.fullname, indent); + } else { + X509_NAME ntmp; + ntmp.entries = dpn->name.relativename; + BIO_printf(out, "%*sRelative Name:\n%*s", indent, "", indent + 2, ""); + X509_NAME_print_ex(out, &ntmp, 0, XN_FLAG_ONELINE); + BIO_puts(out, "\n"); + } + return 1; +} + +static int i2r_idp(const X509V3_EXT_METHOD *method, void *pidp, BIO *out, + int indent) +{ + ISSUING_DIST_POINT *idp = pidp; + if (idp->distpoint) + print_distpoint(out, idp->distpoint, indent); + if (idp->onlyuser > 0) + BIO_printf(out, "%*sOnly User Certificates\n", indent, ""); + if (idp->onlyCA > 0) + BIO_printf(out, "%*sOnly CA Certificates\n", indent, ""); + if (idp->indirectCRL > 0) + BIO_printf(out, "%*sIndirect CRL\n", indent, ""); + if (idp->onlysomereasons) + print_reasons(out, "Only Some Reasons", idp->onlysomereasons, indent); + if (idp->onlyattr > 0) + BIO_printf(out, "%*sOnly Attribute Certificates\n", indent, ""); + if (!idp->distpoint && (idp->onlyuser <= 0) && (idp->onlyCA <= 0) + && (idp->indirectCRL <= 0) && !idp->onlysomereasons + && (idp->onlyattr <= 0)) + BIO_printf(out, "%*s<EMPTY>\n", indent, ""); + + return 1; +} + +static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out, + int indent) +{ + STACK_OF(DIST_POINT) *crld = pcrldp; + DIST_POINT *point; + int i; + for (i = 0; i < sk_DIST_POINT_num(crld); i++) { + if (i > 0) + BIO_puts(out, "\n"); + point = sk_DIST_POINT_value(crld, i); + if (point->distpoint) + print_distpoint(out, point->distpoint, indent); + if (point->reasons) + print_reasons(out, "Reasons", point->reasons, indent); + if (point->CRLissuer) { + BIO_printf(out, "%*sCRL Issuer:\n", indent, ""); + print_gens(out, point->CRLissuer, indent); + } + } + return 1; +} + +int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname) +{ + int i; + STACK_OF(X509_NAME_ENTRY) *frag; + X509_NAME_ENTRY *ne; + if (!dpn || (dpn->type != 1)) + return 1; + frag = dpn->name.relativename; + dpn->dpname = X509_NAME_dup(iname); + if (!dpn->dpname) + return 0; + for (i = 0; i < sk_X509_NAME_ENTRY_num(frag); i++) { + ne = sk_X509_NAME_ENTRY_value(frag, i); + if (!X509_NAME_add_entry(dpn->dpname, ne, -1, i ? 0 : 1)) { + X509_NAME_free(dpn->dpname); + dpn->dpname = NULL; + return 0; + } + } + /* generate cached encoding of name */ + if (i2d_X509_NAME(dpn->dpname, NULL) < 0) { + X509_NAME_free(dpn->dpname); + dpn->dpname = NULL; + return 0; + } + return 1; +} diff --git a/crypto/x509/v3_enum.c b/crypto/x509/v3_enum.c new file mode 100644 index 0000000000..5dfb257fb4 --- /dev/null +++ b/crypto/x509/v3_enum.c @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static ENUMERATED_NAMES crl_reasons[] = { + {CRL_REASON_UNSPECIFIED, "Unspecified", "unspecified"}, + {CRL_REASON_KEY_COMPROMISE, "Key Compromise", "keyCompromise"}, + {CRL_REASON_CA_COMPROMISE, "CA Compromise", "CACompromise"}, + {CRL_REASON_AFFILIATION_CHANGED, "Affiliation Changed", + "affiliationChanged"}, + {CRL_REASON_SUPERSEDED, "Superseded", "superseded"}, + {CRL_REASON_CESSATION_OF_OPERATION, + "Cessation Of Operation", "cessationOfOperation"}, + {CRL_REASON_CERTIFICATE_HOLD, "Certificate Hold", "certificateHold"}, + {CRL_REASON_REMOVE_FROM_CRL, "Remove From CRL", "removeFromCRL"}, + {CRL_REASON_PRIVILEGE_WITHDRAWN, "Privilege Withdrawn", + "privilegeWithdrawn"}, + {CRL_REASON_AA_COMPROMISE, "AA Compromise", "AACompromise"}, + {-1, NULL, NULL} +}; + +const X509V3_EXT_METHOD v3_crl_reason = { + NID_crl_reason, 0, ASN1_ITEM_ref(ASN1_ENUMERATED), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_ENUMERATED_TABLE, + 0, + 0, 0, 0, 0, + crl_reasons +}; + +char *i2s_ASN1_ENUMERATED_TABLE(X509V3_EXT_METHOD *method, + const ASN1_ENUMERATED *e) +{ + ENUMERATED_NAMES *enam; + long strval; + + strval = ASN1_ENUMERATED_get(e); + for (enam = method->usr_data; enam->lname; enam++) { + if (strval == enam->bitnum) + return OPENSSL_strdup(enam->lname); + } + return i2s_ASN1_ENUMERATED(method, e); +} diff --git a/crypto/x509/v3_extku.c b/crypto/x509/v3_extku.c new file mode 100644 index 0000000000..8d0dfcf300 --- /dev/null +++ b/crypto/x509/v3_extku.c @@ -0,0 +1,103 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static void *v2i_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static STACK_OF(CONF_VALUE) *i2v_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD + *method, void *eku, STACK_OF(CONF_VALUE) + *extlist); + +const X509V3_EXT_METHOD v3_ext_ku = { + NID_ext_key_usage, 0, + ASN1_ITEM_ref(EXTENDED_KEY_USAGE), + 0, 0, 0, 0, + 0, 0, + i2v_EXTENDED_KEY_USAGE, + v2i_EXTENDED_KEY_USAGE, + 0, 0, + NULL +}; + +/* NB OCSP acceptable responses also is a SEQUENCE OF OBJECT */ +const X509V3_EXT_METHOD v3_ocsp_accresp = { + NID_id_pkix_OCSP_acceptableResponses, 0, + ASN1_ITEM_ref(EXTENDED_KEY_USAGE), + 0, 0, 0, 0, + 0, 0, + i2v_EXTENDED_KEY_USAGE, + v2i_EXTENDED_KEY_USAGE, + 0, 0, + NULL +}; + +ASN1_ITEM_TEMPLATE(EXTENDED_KEY_USAGE) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, EXTENDED_KEY_USAGE, ASN1_OBJECT) +ASN1_ITEM_TEMPLATE_END(EXTENDED_KEY_USAGE) + +IMPLEMENT_ASN1_FUNCTIONS(EXTENDED_KEY_USAGE) + +static STACK_OF(CONF_VALUE) *i2v_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD + *method, void *a, STACK_OF(CONF_VALUE) + *ext_list) +{ + EXTENDED_KEY_USAGE *eku = a; + int i; + ASN1_OBJECT *obj; + char obj_tmp[80]; + for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) { + obj = sk_ASN1_OBJECT_value(eku, i); + i2t_ASN1_OBJECT(obj_tmp, 80, obj); + X509V3_add_value(NULL, obj_tmp, &ext_list); + } + return ext_list; +} + +static void *v2i_EXTENDED_KEY_USAGE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + EXTENDED_KEY_USAGE *extku; + char *extval; + ASN1_OBJECT *objtmp; + CONF_VALUE *val; + const int num = sk_CONF_VALUE_num(nval); + int i; + + extku = sk_ASN1_OBJECT_new_reserve(NULL, num); + if (extku == NULL) { + X509V3err(X509V3_F_V2I_EXTENDED_KEY_USAGE, ERR_R_MALLOC_FAILURE); + sk_ASN1_OBJECT_free(extku); + return NULL; + } + + for (i = 0; i < num; i++) { + val = sk_CONF_VALUE_value(nval, i); + if (val->value) + extval = val->value; + else + extval = val->name; + if ((objtmp = OBJ_txt2obj(extval, 0)) == NULL) { + sk_ASN1_OBJECT_pop_free(extku, ASN1_OBJECT_free); + X509V3err(X509V3_F_V2I_EXTENDED_KEY_USAGE, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + return NULL; + } + sk_ASN1_OBJECT_push(extku, objtmp); /* no failure as it was reserved */ + } + return extku; +} diff --git a/crypto/x509/v3_genn.c b/crypto/x509/v3_genn.c new file mode 100644 index 0000000000..867b9a9070 --- /dev/null +++ b/crypto/x509/v3_genn.c @@ -0,0 +1,201 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> + +ASN1_SEQUENCE(OTHERNAME) = { + ASN1_SIMPLE(OTHERNAME, type_id, ASN1_OBJECT), + /* Maybe have a true ANY DEFINED BY later */ + ASN1_EXP(OTHERNAME, value, ASN1_ANY, 0) +} ASN1_SEQUENCE_END(OTHERNAME) + +IMPLEMENT_ASN1_FUNCTIONS(OTHERNAME) + +ASN1_SEQUENCE(EDIPARTYNAME) = { + ASN1_IMP_OPT(EDIPARTYNAME, nameAssigner, DIRECTORYSTRING, 0), + ASN1_IMP_OPT(EDIPARTYNAME, partyName, DIRECTORYSTRING, 1) +} ASN1_SEQUENCE_END(EDIPARTYNAME) + +IMPLEMENT_ASN1_FUNCTIONS(EDIPARTYNAME) + +ASN1_CHOICE(GENERAL_NAME) = { + ASN1_IMP(GENERAL_NAME, d.otherName, OTHERNAME, GEN_OTHERNAME), + ASN1_IMP(GENERAL_NAME, d.rfc822Name, ASN1_IA5STRING, GEN_EMAIL), + ASN1_IMP(GENERAL_NAME, d.dNSName, ASN1_IA5STRING, GEN_DNS), + /* Don't decode this */ + ASN1_IMP(GENERAL_NAME, d.x400Address, ASN1_SEQUENCE, GEN_X400), + /* X509_NAME is a CHOICE type so use EXPLICIT */ + ASN1_EXP(GENERAL_NAME, d.directoryName, X509_NAME, GEN_DIRNAME), + ASN1_IMP(GENERAL_NAME, d.ediPartyName, EDIPARTYNAME, GEN_EDIPARTY), + ASN1_IMP(GENERAL_NAME, d.uniformResourceIdentifier, ASN1_IA5STRING, GEN_URI), + ASN1_IMP(GENERAL_NAME, d.iPAddress, ASN1_OCTET_STRING, GEN_IPADD), + ASN1_IMP(GENERAL_NAME, d.registeredID, ASN1_OBJECT, GEN_RID) +} ASN1_CHOICE_END(GENERAL_NAME) + +IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAME) + +ASN1_ITEM_TEMPLATE(GENERAL_NAMES) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, GeneralNames, GENERAL_NAME) +ASN1_ITEM_TEMPLATE_END(GENERAL_NAMES) + +IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAMES) + +GENERAL_NAME *GENERAL_NAME_dup(const GENERAL_NAME *a) +{ + return (GENERAL_NAME *)ASN1_dup((i2d_of_void *)i2d_GENERAL_NAME, + (d2i_of_void *)d2i_GENERAL_NAME, + (char *)a); +} + +/* Returns 0 if they are equal, != 0 otherwise. */ +int GENERAL_NAME_cmp(GENERAL_NAME *a, GENERAL_NAME *b) +{ + int result = -1; + + if (!a || !b || a->type != b->type) + return -1; + switch (a->type) { + case GEN_X400: + case GEN_EDIPARTY: + result = ASN1_TYPE_cmp(a->d.other, b->d.other); + break; + + case GEN_OTHERNAME: + result = OTHERNAME_cmp(a->d.otherName, b->d.otherName); + break; + + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + result = ASN1_STRING_cmp(a->d.ia5, b->d.ia5); + break; + + case GEN_DIRNAME: + result = X509_NAME_cmp(a->d.dirn, b->d.dirn); + break; + + case GEN_IPADD: + result = ASN1_OCTET_STRING_cmp(a->d.ip, b->d.ip); + break; + + case GEN_RID: + result = OBJ_cmp(a->d.rid, b->d.rid); + break; + } + return result; +} + +/* Returns 0 if they are equal, != 0 otherwise. */ +int OTHERNAME_cmp(OTHERNAME *a, OTHERNAME *b) +{ + int result = -1; + + if (!a || !b) + return -1; + /* Check their type first. */ + if ((result = OBJ_cmp(a->type_id, b->type_id)) != 0) + return result; + /* Check the value. */ + result = ASN1_TYPE_cmp(a->value, b->value); + return result; +} + +void GENERAL_NAME_set0_value(GENERAL_NAME *a, int type, void *value) +{ + switch (type) { + case GEN_X400: + case GEN_EDIPARTY: + a->d.other = value; + break; + + case GEN_OTHERNAME: + a->d.otherName = value; + break; + + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + a->d.ia5 = value; + break; + + case GEN_DIRNAME: + a->d.dirn = value; + break; + + case GEN_IPADD: + a->d.ip = value; + break; + + case GEN_RID: + a->d.rid = value; + break; + } + a->type = type; +} + +void *GENERAL_NAME_get0_value(const GENERAL_NAME *a, int *ptype) +{ + if (ptype) + *ptype = a->type; + switch (a->type) { + case GEN_X400: + case GEN_EDIPARTY: + return a->d.other; + + case GEN_OTHERNAME: + return a->d.otherName; + + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + return a->d.ia5; + + case GEN_DIRNAME: + return a->d.dirn; + + case GEN_IPADD: + return a->d.ip; + + case GEN_RID: + return a->d.rid; + + default: + return NULL; + } +} + +int GENERAL_NAME_set0_othername(GENERAL_NAME *gen, + ASN1_OBJECT *oid, ASN1_TYPE *value) +{ + OTHERNAME *oth; + oth = OTHERNAME_new(); + if (oth == NULL) + return 0; + ASN1_TYPE_free(oth->value); + oth->type_id = oid; + oth->value = value; + GENERAL_NAME_set0_value(gen, GEN_OTHERNAME, oth); + return 1; +} + +int GENERAL_NAME_get0_otherName(const GENERAL_NAME *gen, + ASN1_OBJECT **poid, ASN1_TYPE **pvalue) +{ + if (gen->type != GEN_OTHERNAME) + return 0; + if (poid) + *poid = gen->d.otherName->type_id; + if (pvalue) + *pvalue = gen->d.otherName->value; + return 1; +} diff --git a/crypto/x509/v3_ia5.c b/crypto/x509/v3_ia5.c new file mode 100644 index 0000000000..23c24e03b2 --- /dev/null +++ b/crypto/x509/v3_ia5.c @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/asn1.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +const X509V3_EXT_METHOD v3_ns_ia5_list[8] = { + EXT_IA5STRING(NID_netscape_base_url), + EXT_IA5STRING(NID_netscape_revocation_url), + EXT_IA5STRING(NID_netscape_ca_revocation_url), + EXT_IA5STRING(NID_netscape_renewal_url), + EXT_IA5STRING(NID_netscape_ca_policy_url), + EXT_IA5STRING(NID_netscape_ssl_server_name), + EXT_IA5STRING(NID_netscape_comment), + EXT_END +}; + +char *i2s_ASN1_IA5STRING(X509V3_EXT_METHOD *method, ASN1_IA5STRING *ia5) +{ + char *tmp; + + if (!ia5 || !ia5->length) + return NULL; + if ((tmp = OPENSSL_malloc(ia5->length + 1)) == NULL) { + X509V3err(X509V3_F_I2S_ASN1_IA5STRING, ERR_R_MALLOC_FAILURE); + return NULL; + } + memcpy(tmp, ia5->data, ia5->length); + tmp[ia5->length] = 0; + return tmp; +} + +ASN1_IA5STRING *s2i_ASN1_IA5STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *str) +{ + ASN1_IA5STRING *ia5; + if (!str) { + X509V3err(X509V3_F_S2I_ASN1_IA5STRING, + X509V3_R_INVALID_NULL_ARGUMENT); + return NULL; + } + if ((ia5 = ASN1_IA5STRING_new()) == NULL) + goto err; + if (!ASN1_STRING_set((ASN1_STRING *)ia5, str, strlen(str))) { + ASN1_IA5STRING_free(ia5); + return NULL; + } +#ifdef CHARSET_EBCDIC + ebcdic2ascii(ia5->data, ia5->data, ia5->length); +#endif /* CHARSET_EBCDIC */ + return ia5; + err: + X509V3err(X509V3_F_S2I_ASN1_IA5STRING, ERR_R_MALLOC_FAILURE); + return NULL; +} diff --git a/crypto/x509/v3_info.c b/crypto/x509/v3_info.c new file mode 100644 index 0000000000..0b97c9663f --- /dev/null +++ b/crypto/x509/v3_info.c @@ -0,0 +1,162 @@ +/* + * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD + *method, AUTHORITY_INFO_ACCESS + *ainfo, STACK_OF(CONF_VALUE) + *ret); +static AUTHORITY_INFO_ACCESS *v2i_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD + *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) + *nval); + +const X509V3_EXT_METHOD v3_info = { NID_info_access, X509V3_EXT_MULTILINE, + ASN1_ITEM_ref(AUTHORITY_INFO_ACCESS), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_AUTHORITY_INFO_ACCESS, + (X509V3_EXT_V2I)v2i_AUTHORITY_INFO_ACCESS, + 0, 0, + NULL +}; + +const X509V3_EXT_METHOD v3_sinfo = { NID_sinfo_access, X509V3_EXT_MULTILINE, + ASN1_ITEM_ref(AUTHORITY_INFO_ACCESS), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V) i2v_AUTHORITY_INFO_ACCESS, + (X509V3_EXT_V2I)v2i_AUTHORITY_INFO_ACCESS, + 0, 0, + NULL +}; + +ASN1_SEQUENCE(ACCESS_DESCRIPTION) = { + ASN1_SIMPLE(ACCESS_DESCRIPTION, method, ASN1_OBJECT), + ASN1_SIMPLE(ACCESS_DESCRIPTION, location, GENERAL_NAME) +} ASN1_SEQUENCE_END(ACCESS_DESCRIPTION) + +IMPLEMENT_ASN1_FUNCTIONS(ACCESS_DESCRIPTION) + +ASN1_ITEM_TEMPLATE(AUTHORITY_INFO_ACCESS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, GeneralNames, ACCESS_DESCRIPTION) +ASN1_ITEM_TEMPLATE_END(AUTHORITY_INFO_ACCESS) + +IMPLEMENT_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS) + +static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_INFO_ACCESS( + X509V3_EXT_METHOD *method, AUTHORITY_INFO_ACCESS *ainfo, + STACK_OF(CONF_VALUE) *ret) +{ + ACCESS_DESCRIPTION *desc; + int i, nlen; + char objtmp[80], *ntmp; + CONF_VALUE *vtmp; + STACK_OF(CONF_VALUE) *tret = ret; + + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ainfo); i++) { + STACK_OF(CONF_VALUE) *tmp; + + desc = sk_ACCESS_DESCRIPTION_value(ainfo, i); + tmp = i2v_GENERAL_NAME(method, desc->location, tret); + if (tmp == NULL) + goto err; + tret = tmp; + vtmp = sk_CONF_VALUE_value(tret, i); + i2t_ASN1_OBJECT(objtmp, sizeof(objtmp), desc->method); + nlen = strlen(objtmp) + 3 + strlen(vtmp->name) + 1; + ntmp = OPENSSL_malloc(nlen); + if (ntmp == NULL) + goto err; + BIO_snprintf(ntmp, nlen, "%s - %s", objtmp, vtmp->name); + OPENSSL_free(vtmp->name); + vtmp->name = ntmp; + } + if (ret == NULL && tret == NULL) + return sk_CONF_VALUE_new_null(); + + return tret; + err: + X509V3err(X509V3_F_I2V_AUTHORITY_INFO_ACCESS, ERR_R_MALLOC_FAILURE); + if (ret == NULL && tret != NULL) + sk_CONF_VALUE_pop_free(tret, X509V3_conf_free); + return NULL; +} + +static AUTHORITY_INFO_ACCESS *v2i_AUTHORITY_INFO_ACCESS(X509V3_EXT_METHOD + *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) + *nval) +{ + AUTHORITY_INFO_ACCESS *ainfo = NULL; + CONF_VALUE *cnf, ctmp; + ACCESS_DESCRIPTION *acc; + int i, objlen; + const int num = sk_CONF_VALUE_num(nval); + char *objtmp, *ptmp; + + if ((ainfo = sk_ACCESS_DESCRIPTION_new_reserve(NULL, num)) == NULL) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < num; i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if ((acc = ACCESS_DESCRIPTION_new()) == NULL) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + ERR_R_MALLOC_FAILURE); + goto err; + } + sk_ACCESS_DESCRIPTION_push(ainfo, acc); /* Cannot fail due to reserve */ + ptmp = strchr(cnf->name, ';'); + if (!ptmp) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + X509V3_R_INVALID_SYNTAX); + goto err; + } + objlen = ptmp - cnf->name; + ctmp.name = ptmp + 1; + ctmp.value = cnf->value; + if (!v2i_GENERAL_NAME_ex(acc->location, method, ctx, &ctmp, 0)) + goto err; + if ((objtmp = OPENSSL_strndup(cnf->name, objlen)) == NULL) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + ERR_R_MALLOC_FAILURE); + goto err; + } + acc->method = OBJ_txt2obj(objtmp, 0); + if (!acc->method) { + X509V3err(X509V3_F_V2I_AUTHORITY_INFO_ACCESS, + X509V3_R_BAD_OBJECT); + ERR_add_error_data(2, "value=", objtmp); + OPENSSL_free(objtmp); + goto err; + } + OPENSSL_free(objtmp); + + } + return ainfo; + err: + sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free); + return NULL; +} + +int i2a_ACCESS_DESCRIPTION(BIO *bp, const ACCESS_DESCRIPTION *a) +{ + i2a_ASN1_OBJECT(bp, a->method); + return 2; +} diff --git a/crypto/x509/v3_int.c b/crypto/x509/v3_int.c new file mode 100644 index 0000000000..bd8debfafa --- /dev/null +++ b/crypto/x509/v3_int.c @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/x509v3.h> +#include "ext_dat.h" + +const X509V3_EXT_METHOD v3_crl_num = { + NID_crl_number, 0, ASN1_ITEM_ref(ASN1_INTEGER), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_INTEGER, + 0, + 0, 0, 0, 0, NULL +}; + +const X509V3_EXT_METHOD v3_delta_crl = { + NID_delta_crl, 0, ASN1_ITEM_ref(ASN1_INTEGER), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_INTEGER, + 0, + 0, 0, 0, 0, NULL +}; + +static void *s2i_asn1_int(X509V3_EXT_METHOD *meth, X509V3_CTX *ctx, + const char *value) +{ + return s2i_ASN1_INTEGER(meth, value); +} + +const X509V3_EXT_METHOD v3_inhibit_anyp = { + NID_inhibit_any_policy, 0, ASN1_ITEM_ref(ASN1_INTEGER), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_INTEGER, + (X509V3_EXT_S2I)s2i_asn1_int, + 0, 0, 0, 0, NULL +}; diff --git a/crypto/x509/v3_lib.c b/crypto/x509/v3_lib.c new file mode 100644 index 0000000000..71ba7a3282 --- /dev/null +++ b/crypto/x509/v3_lib.c @@ -0,0 +1,303 @@ +/* + * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* X509 v3 extension utilities */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/x509v3.h> + +#include "ext_dat.h" + +static STACK_OF(X509V3_EXT_METHOD) *ext_list = NULL; + +static int ext_cmp(const X509V3_EXT_METHOD *const *a, + const X509V3_EXT_METHOD *const *b); +static void ext_list_free(X509V3_EXT_METHOD *ext); + +int X509V3_EXT_add(X509V3_EXT_METHOD *ext) +{ + if (ext_list == NULL + && (ext_list = sk_X509V3_EXT_METHOD_new(ext_cmp)) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_ADD, ERR_R_MALLOC_FAILURE); + return 0; + } + if (!sk_X509V3_EXT_METHOD_push(ext_list, ext)) { + X509V3err(X509V3_F_X509V3_EXT_ADD, ERR_R_MALLOC_FAILURE); + return 0; + } + return 1; +} + +static int ext_cmp(const X509V3_EXT_METHOD *const *a, + const X509V3_EXT_METHOD *const *b) +{ + return ((*a)->ext_nid - (*b)->ext_nid); +} + +DECLARE_OBJ_BSEARCH_CMP_FN(const X509V3_EXT_METHOD *, + const X509V3_EXT_METHOD *, ext); +IMPLEMENT_OBJ_BSEARCH_CMP_FN(const X509V3_EXT_METHOD *, + const X509V3_EXT_METHOD *, ext); + +#include "standard_exts.h" + +const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid) +{ + X509V3_EXT_METHOD tmp; + const X509V3_EXT_METHOD *t = &tmp, *const *ret; + int idx; + + if (nid < 0) + return NULL; + tmp.ext_nid = nid; + ret = OBJ_bsearch_ext(&t, standard_exts, STANDARD_EXTENSION_COUNT); + if (ret) + return *ret; + if (!ext_list) + return NULL; + idx = sk_X509V3_EXT_METHOD_find(ext_list, &tmp); + return sk_X509V3_EXT_METHOD_value(ext_list, idx); +} + +const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *ext) +{ + int nid; + if ((nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext))) == NID_undef) + return NULL; + return X509V3_EXT_get_nid(nid); +} + +int X509V3_EXT_add_list(X509V3_EXT_METHOD *extlist) +{ + for (; extlist->ext_nid != -1; extlist++) + if (!X509V3_EXT_add(extlist)) + return 0; + return 1; +} + +int X509V3_EXT_add_alias(int nid_to, int nid_from) +{ + const X509V3_EXT_METHOD *ext; + X509V3_EXT_METHOD *tmpext; + + if ((ext = X509V3_EXT_get_nid(nid_from)) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_ADD_ALIAS, X509V3_R_EXTENSION_NOT_FOUND); + return 0; + } + if ((tmpext = OPENSSL_malloc(sizeof(*tmpext))) == NULL) { + X509V3err(X509V3_F_X509V3_EXT_ADD_ALIAS, ERR_R_MALLOC_FAILURE); + return 0; + } + *tmpext = *ext; + tmpext->ext_nid = nid_to; + tmpext->ext_flags |= X509V3_EXT_DYNAMIC; + return X509V3_EXT_add(tmpext); +} + +void X509V3_EXT_cleanup(void) +{ + sk_X509V3_EXT_METHOD_pop_free(ext_list, ext_list_free); + ext_list = NULL; +} + +static void ext_list_free(X509V3_EXT_METHOD *ext) +{ + if (ext->ext_flags & X509V3_EXT_DYNAMIC) + OPENSSL_free(ext); +} + +/* + * Legacy function: we don't need to add standard extensions any more because + * they are now kept in ext_dat.h. + */ + +int X509V3_add_standard_extensions(void) +{ + return 1; +} + +/* Return an extension internal structure */ + +void *X509V3_EXT_d2i(X509_EXTENSION *ext) +{ + const X509V3_EXT_METHOD *method; + const unsigned char *p; + ASN1_STRING *extvalue; + int extlen; + + if ((method = X509V3_EXT_get(ext)) == NULL) + return NULL; + extvalue = X509_EXTENSION_get_data(ext); + p = ASN1_STRING_get0_data(extvalue); + extlen = ASN1_STRING_length(extvalue); + if (method->it) + return ASN1_item_d2i(NULL, &p, extlen, ASN1_ITEM_ptr(method->it)); + return method->d2i(NULL, &p, extlen); +} + +/*- + * Get critical flag and decoded version of extension from a NID. + * The "idx" variable returns the last found extension and can + * be used to retrieve multiple extensions of the same NID. + * However multiple extensions with the same NID is usually + * due to a badly encoded certificate so if idx is NULL we + * choke if multiple extensions exist. + * The "crit" variable is set to the critical value. + * The return value is the decoded extension or NULL on + * error. The actual error can have several different causes, + * the value of *crit reflects the cause: + * >= 0, extension found but not decoded (reflects critical value). + * -1 extension not found. + * -2 extension occurs more than once. + */ + +void *X509V3_get_d2i(const STACK_OF(X509_EXTENSION) *x, int nid, int *crit, + int *idx) +{ + int lastpos, i; + X509_EXTENSION *ex, *found_ex = NULL; + + if (!x) { + if (idx) + *idx = -1; + if (crit) + *crit = -1; + return NULL; + } + if (idx) + lastpos = *idx + 1; + else + lastpos = 0; + if (lastpos < 0) + lastpos = 0; + for (i = lastpos; i < sk_X509_EXTENSION_num(x); i++) { + ex = sk_X509_EXTENSION_value(x, i); + if (OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == nid) { + if (idx) { + *idx = i; + found_ex = ex; + break; + } else if (found_ex) { + /* Found more than one */ + if (crit) + *crit = -2; + return NULL; + } + found_ex = ex; + } + } + if (found_ex) { + /* Found it */ + if (crit) + *crit = X509_EXTENSION_get_critical(found_ex); + return X509V3_EXT_d2i(found_ex); + } + + /* Extension not found */ + if (idx) + *idx = -1; + if (crit) + *crit = -1; + return NULL; +} + +/* + * This function is a general extension append, replace and delete utility. + * The precise operation is governed by the 'flags' value. The 'crit' and + * 'value' arguments (if relevant) are the extensions internal structure. + */ + +int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value, + int crit, unsigned long flags) +{ + int errcode, extidx = -1; + X509_EXTENSION *ext = NULL, *extmp; + STACK_OF(X509_EXTENSION) *ret = NULL; + unsigned long ext_op = flags & X509V3_ADD_OP_MASK; + + /* + * If appending we don't care if it exists, otherwise look for existing + * extension. + */ + if (ext_op != X509V3_ADD_APPEND) + extidx = X509v3_get_ext_by_NID(*x, nid, -1); + + /* See if extension exists */ + if (extidx >= 0) { + /* If keep existing, nothing to do */ + if (ext_op == X509V3_ADD_KEEP_EXISTING) + return 1; + /* If default then its an error */ + if (ext_op == X509V3_ADD_DEFAULT) { + errcode = X509V3_R_EXTENSION_EXISTS; + goto err; + } + /* If delete, just delete it */ + if (ext_op == X509V3_ADD_DELETE) { + if (!sk_X509_EXTENSION_delete(*x, extidx)) + return -1; + return 1; + } + } else { + /* + * If replace existing or delete, error since extension must exist + */ + if ((ext_op == X509V3_ADD_REPLACE_EXISTING) || + (ext_op == X509V3_ADD_DELETE)) { + errcode = X509V3_R_EXTENSION_NOT_FOUND; + goto err; + } + } + + /* + * If we get this far then we have to create an extension: could have + * some flags for alternative encoding schemes... + */ + + ext = X509V3_EXT_i2d(nid, crit, value); + + if (!ext) { + X509V3err(X509V3_F_X509V3_ADD1_I2D, + X509V3_R_ERROR_CREATING_EXTENSION); + return 0; + } + + /* If extension exists replace it.. */ + if (extidx >= 0) { + extmp = sk_X509_EXTENSION_value(*x, extidx); + X509_EXTENSION_free(extmp); + if (!sk_X509_EXTENSION_set(*x, extidx, ext)) + return -1; + return 1; + } + + ret = *x; + if (*x == NULL + && (ret = sk_X509_EXTENSION_new_null()) == NULL) + goto m_fail; + if (!sk_X509_EXTENSION_push(ret, ext)) + goto m_fail; + + *x = ret; + return 1; + + m_fail: + /* X509V3err(X509V3_F_X509V3_ADD1_I2D, ERR_R_MALLOC_FAILURE); */ + if (ret != *x) + sk_X509_EXTENSION_free(ret); + X509_EXTENSION_free(ext); + return -1; + + err: + if (!(flags & X509V3_ADD_SILENT)) + X509V3err(X509V3_F_X509V3_ADD1_I2D, errcode); + return 0; +} diff --git a/crypto/x509/v3_ncons.c b/crypto/x509/v3_ncons.c new file mode 100644 index 0000000000..9a8ca16685 --- /dev/null +++ b/crypto/x509/v3_ncons.c @@ -0,0 +1,700 @@ +/* + * Copyright 2003-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include <stdio.h> +#include "internal/asn1_int.h" +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include <openssl/bn.h> + +#include "internal/x509_int.h" +#include "ext_dat.h" + +static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a, + BIO *bp, int ind); +static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method, + STACK_OF(GENERAL_SUBTREE) *trees, BIO *bp, + int ind, const char *name); +static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip); + +static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc); +static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen); +static int nc_dn(X509_NAME *sub, X509_NAME *nm); +static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns); +static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml); +static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base); +static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base); + +const X509V3_EXT_METHOD v3_name_constraints = { + NID_name_constraints, 0, + ASN1_ITEM_ref(NAME_CONSTRAINTS), + 0, 0, 0, 0, + 0, 0, + 0, v2i_NAME_CONSTRAINTS, + i2r_NAME_CONSTRAINTS, 0, + NULL +}; + +ASN1_SEQUENCE(GENERAL_SUBTREE) = { + ASN1_SIMPLE(GENERAL_SUBTREE, base, GENERAL_NAME), + ASN1_IMP_OPT(GENERAL_SUBTREE, minimum, ASN1_INTEGER, 0), + ASN1_IMP_OPT(GENERAL_SUBTREE, maximum, ASN1_INTEGER, 1) +} ASN1_SEQUENCE_END(GENERAL_SUBTREE) + +ASN1_SEQUENCE(NAME_CONSTRAINTS) = { + ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, permittedSubtrees, + GENERAL_SUBTREE, 0), + ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, excludedSubtrees, + GENERAL_SUBTREE, 1), +} ASN1_SEQUENCE_END(NAME_CONSTRAINTS) + + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS) + +/* + * We cannot use strncasecmp here because that applies locale specific rules. + * For example in Turkish 'I' is not the uppercase character for 'i'. We need to + * do a simple ASCII case comparison ignoring the locale (that is why we use + * numeric constants below). + */ +static int ia5ncasecmp(const char *s1, const char *s2, size_t n) +{ + for (; n > 0; n--, s1++, s2++) { + if (*s1 != *s2) { + unsigned char c1 = (unsigned char)*s1, c2 = (unsigned char)*s2; + + /* Convert to lower case */ + if (c1 >= 0x41 /* A */ && c1 <= 0x5A /* Z */) + c1 += 0x20; + if (c2 >= 0x41 /* A */ && c2 <= 0x5A /* Z */) + c2 += 0x20; + + if (c1 == c2) + continue; + + if (c1 < c2) + return -1; + + /* c1 > c2 */ + return 1; + } else if (*s1 == 0) { + /* If we get here we know that *s2 == 0 too */ + return 0; + } + } + + return 0; +} + +static int ia5casecmp(const char *s1, const char *s2) +{ + return ia5ncasecmp(s1, s2, SIZE_MAX); +} + +static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + int i; + CONF_VALUE tval, *val; + STACK_OF(GENERAL_SUBTREE) **ptree = NULL; + NAME_CONSTRAINTS *ncons = NULL; + GENERAL_SUBTREE *sub = NULL; + + ncons = NAME_CONSTRAINTS_new(); + if (ncons == NULL) + goto memerr; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if (strncmp(val->name, "permitted", 9) == 0 && val->name[9]) { + ptree = &ncons->permittedSubtrees; + tval.name = val->name + 10; + } else if (strncmp(val->name, "excluded", 8) == 0 && val->name[8]) { + ptree = &ncons->excludedSubtrees; + tval.name = val->name + 9; + } else { + X509V3err(X509V3_F_V2I_NAME_CONSTRAINTS, X509V3_R_INVALID_SYNTAX); + goto err; + } + tval.value = val->value; + sub = GENERAL_SUBTREE_new(); + if (sub == NULL) + goto memerr; + if (!v2i_GENERAL_NAME_ex(sub->base, method, ctx, &tval, 1)) + goto err; + if (*ptree == NULL) + *ptree = sk_GENERAL_SUBTREE_new_null(); + if (*ptree == NULL || !sk_GENERAL_SUBTREE_push(*ptree, sub)) + goto memerr; + sub = NULL; + } + + return ncons; + + memerr: + X509V3err(X509V3_F_V2I_NAME_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + err: + NAME_CONSTRAINTS_free(ncons); + GENERAL_SUBTREE_free(sub); + + return NULL; +} + +static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a, + BIO *bp, int ind) +{ + NAME_CONSTRAINTS *ncons = a; + do_i2r_name_constraints(method, ncons->permittedSubtrees, + bp, ind, "Permitted"); + if (ncons->permittedSubtrees && ncons->excludedSubtrees) + BIO_puts(bp, "\n"); + do_i2r_name_constraints(method, ncons->excludedSubtrees, + bp, ind, "Excluded"); + return 1; +} + +static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method, + STACK_OF(GENERAL_SUBTREE) *trees, + BIO *bp, int ind, const char *name) +{ + GENERAL_SUBTREE *tree; + int i; + if (sk_GENERAL_SUBTREE_num(trees) > 0) + BIO_printf(bp, "%*s%s:\n", ind, "", name); + for (i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++) { + if (i > 0) + BIO_puts(bp, "\n"); + tree = sk_GENERAL_SUBTREE_value(trees, i); + BIO_printf(bp, "%*s", ind + 2, ""); + if (tree->base->type == GEN_IPADD) + print_nc_ipadd(bp, tree->base->d.ip); + else + GENERAL_NAME_print(bp, tree->base); + } + return 1; +} + +static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip) +{ + int i, len; + unsigned char *p; + p = ip->data; + len = ip->length; + BIO_puts(bp, "IP:"); + if (len == 8) { + BIO_printf(bp, "%d.%d.%d.%d/%d.%d.%d.%d", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + } else if (len == 32) { + for (i = 0; i < 16; i++) { + BIO_printf(bp, "%X", p[0] << 8 | p[1]); + p += 2; + if (i == 7) + BIO_puts(bp, "/"); + else if (i != 15) + BIO_puts(bp, ":"); + } + } else + BIO_printf(bp, "IP Address:<invalid>"); + return 1; +} + +#define NAME_CHECK_MAX (1 << 20) + +static int add_lengths(int *out, int a, int b) +{ + /* sk_FOO_num(NULL) returns -1 but is effectively 0 when iterating. */ + if (a < 0) + a = 0; + if (b < 0) + b = 0; + + if (a > INT_MAX - b) + return 0; + *out = a + b; + return 1; +} + +/*- + * Check a certificate conforms to a specified set of constraints. + * Return values: + * X509_V_OK: All constraints obeyed. + * X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation. + * X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation. + * X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type. + * X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type. + * X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax. + * X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name + */ + +int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc) +{ + int r, i, name_count, constraint_count; + X509_NAME *nm; + + nm = X509_get_subject_name(x); + + /* + * Guard against certificates with an excessive number of names or + * constraints causing a computationally expensive name constraints check. + */ + if (!add_lengths(&name_count, X509_NAME_entry_count(nm), + sk_GENERAL_NAME_num(x->altname)) + || !add_lengths(&constraint_count, + sk_GENERAL_SUBTREE_num(nc->permittedSubtrees), + sk_GENERAL_SUBTREE_num(nc->excludedSubtrees)) + || (name_count > 0 && constraint_count > NAME_CHECK_MAX / name_count)) + return X509_V_ERR_UNSPECIFIED; + + if (X509_NAME_entry_count(nm) > 0) { + GENERAL_NAME gntmp; + gntmp.type = GEN_DIRNAME; + gntmp.d.directoryName = nm; + + r = nc_match(&gntmp, nc); + + if (r != X509_V_OK) + return r; + + gntmp.type = GEN_EMAIL; + + /* Process any email address attributes in subject name */ + + for (i = -1;;) { + const X509_NAME_ENTRY *ne; + + i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i); + if (i == -1) + break; + ne = X509_NAME_get_entry(nm, i); + gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne); + if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + r = nc_match(&gntmp, nc); + + if (r != X509_V_OK) + return r; + } + + } + + for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++) { + GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i); + r = nc_match(gen, nc); + if (r != X509_V_OK) + return r; + } + + return X509_V_OK; + +} + +static int cn2dnsid(ASN1_STRING *cn, unsigned char **dnsid, size_t *idlen) +{ + int utf8_length; + unsigned char *utf8_value; + int i; + int isdnsname = 0; + + /* Don't leave outputs uninitialized */ + *dnsid = NULL; + *idlen = 0; + + /*- + * Per RFC 6125, DNS-IDs representing internationalized domain names appear + * in certificates in A-label encoded form: + * + * https://tools.ietf.org/html/rfc6125#section-6.4.2 + * + * The same applies to CNs which are intended to represent DNS names. + * However, while in the SAN DNS-IDs are IA5Strings, as CNs they may be + * needlessly encoded in 16-bit Unicode. We perform a conversion to UTF-8 + * to ensure that we get an ASCII representation of any CNs that are + * representable as ASCII, but just not encoded as ASCII. The UTF-8 form + * may contain some non-ASCII octets, and that's fine, such CNs are not + * valid legacy DNS names. + * + * Note, 'int' is the return type of ASN1_STRING_to_UTF8() so that's what + * we must use for 'utf8_length'. + */ + if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, cn)) < 0) + return X509_V_ERR_OUT_OF_MEM; + + /* + * Some certificates have had names that include a *trailing* NUL byte. + * Remove these harmless NUL characters. They would otherwise yield false + * alarms with the following embedded NUL check. + */ + while (utf8_length > 0 && utf8_value[utf8_length - 1] == '\0') + --utf8_length; + + /* Reject *embedded* NULs */ + if ((size_t)utf8_length != strlen((char *)utf8_value)) { + OPENSSL_free(utf8_value); + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + } + + /* + * XXX: Deviation from strict DNS name syntax, also check names with '_' + * Check DNS name syntax, any '-' or '.' must be internal, + * and on either side of each '.' we can't have a '-' or '.'. + * + * If the name has just one label, we don't consider it a DNS name. This + * means that "CN=sometld" cannot be precluded by DNS name constraints, but + * that is not a problem. + */ + for (i = 0; i < utf8_length; ++i) { + unsigned char c = utf8_value[i]; + + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '_') + continue; + + /* Dot and hyphen cannot be first or last. */ + if (i > 0 && i < utf8_length - 1) { + if (c == '-') + continue; + /* + * Next to a dot the preceding and following characters must not be + * another dot or a hyphen. Otherwise, record that the name is + * plausible, since it has two or more labels. + */ + if (c == '.' + && utf8_value[i + 1] != '.' + && utf8_value[i - 1] != '-' + && utf8_value[i + 1] != '-') { + isdnsname = 1; + continue; + } + } + isdnsname = 0; + break; + } + + if (isdnsname) { + *dnsid = utf8_value; + *idlen = (size_t)utf8_length; + return X509_V_OK; + } + OPENSSL_free(utf8_value); + return X509_V_OK; +} + +/* + * Check CN against DNS-ID name constraints. + */ +int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc) +{ + int r, i; + X509_NAME *nm = X509_get_subject_name(x); + ASN1_STRING stmp; + GENERAL_NAME gntmp; + + stmp.flags = 0; + stmp.type = V_ASN1_IA5STRING; + gntmp.type = GEN_DNS; + gntmp.d.dNSName = &stmp; + + /* Process any commonName attributes in subject name */ + + for (i = -1;;) { + X509_NAME_ENTRY *ne; + ASN1_STRING *cn; + unsigned char *idval; + size_t idlen; + + i = X509_NAME_get_index_by_NID(nm, NID_commonName, i); + if (i == -1) + break; + ne = X509_NAME_get_entry(nm, i); + cn = X509_NAME_ENTRY_get_data(ne); + + /* Only process attributes that look like host names */ + if ((r = cn2dnsid(cn, &idval, &idlen)) != X509_V_OK) + return r; + if (idlen == 0) + continue; + + stmp.length = idlen; + stmp.data = idval; + r = nc_match(&gntmp, nc); + OPENSSL_free(idval); + if (r != X509_V_OK) + return r; + } + return X509_V_OK; +} + +/* + * Return nonzero if the GeneralSubtree has valid 'minimum' field + * (must be absent or 0) and valid 'maximum' field (must be absent). + */ +static int nc_minmax_valid(GENERAL_SUBTREE *sub) { + BIGNUM *bn = NULL; + int ok = 1; + + if (sub->maximum) + ok = 0; + + if (sub->minimum) { + bn = ASN1_INTEGER_to_BN(sub->minimum, NULL); + if (bn == NULL || !BN_is_zero(bn)) + ok = 0; + BN_free(bn); + } + + return ok; +} + +static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) +{ + GENERAL_SUBTREE *sub; + int i, r, match = 0; + + /* + * Permitted subtrees: if any subtrees exist of matching the type at + * least one subtree must match. + */ + + for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) { + sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); + if (gen->type != sub->base->type) + continue; + if (!nc_minmax_valid(sub)) + return X509_V_ERR_SUBTREE_MINMAX; + /* If we already have a match don't bother trying any more */ + if (match == 2) + continue; + if (match == 0) + match = 1; + r = nc_match_single(gen, sub->base); + if (r == X509_V_OK) + match = 2; + else if (r != X509_V_ERR_PERMITTED_VIOLATION) + return r; + } + + if (match == 1) + return X509_V_ERR_PERMITTED_VIOLATION; + + /* Excluded subtrees: must not match any of these */ + + for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) { + sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); + if (gen->type != sub->base->type) + continue; + if (!nc_minmax_valid(sub)) + return X509_V_ERR_SUBTREE_MINMAX; + + r = nc_match_single(gen, sub->base); + if (r == X509_V_OK) + return X509_V_ERR_EXCLUDED_VIOLATION; + else if (r != X509_V_ERR_PERMITTED_VIOLATION) + return r; + + } + + return X509_V_OK; + +} + +static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base) +{ + switch (base->type) { + case GEN_DIRNAME: + return nc_dn(gen->d.directoryName, base->d.directoryName); + + case GEN_DNS: + return nc_dns(gen->d.dNSName, base->d.dNSName); + + case GEN_EMAIL: + return nc_email(gen->d.rfc822Name, base->d.rfc822Name); + + case GEN_URI: + return nc_uri(gen->d.uniformResourceIdentifier, + base->d.uniformResourceIdentifier); + + case GEN_IPADD: + return nc_ip(gen->d.iPAddress, base->d.iPAddress); + + default: + return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE; + } + +} + +/* + * directoryName name constraint matching. The canonical encoding of + * X509_NAME makes this comparison easy. It is matched if the subtree is a + * subset of the name. + */ + +static int nc_dn(X509_NAME *nm, X509_NAME *base) +{ + /* Ensure canonical encodings are up to date. */ + if (nm->modified && i2d_X509_NAME(nm, NULL) < 0) + return X509_V_ERR_OUT_OF_MEM; + if (base->modified && i2d_X509_NAME(base, NULL) < 0) + return X509_V_ERR_OUT_OF_MEM; + if (base->canon_enclen > nm->canon_enclen) + return X509_V_ERR_PERMITTED_VIOLATION; + if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen)) + return X509_V_ERR_PERMITTED_VIOLATION; + return X509_V_OK; +} + +static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) +{ + char *baseptr = (char *)base->data; + char *dnsptr = (char *)dns->data; + /* Empty matches everything */ + if (!*baseptr) + return X509_V_OK; + /* + * Otherwise can add zero or more components on the left so compare RHS + * and if dns is longer and expect '.' as preceding character. + */ + if (dns->length > base->length) { + dnsptr += dns->length - base->length; + if (*baseptr != '.' && dnsptr[-1] != '.') + return X509_V_ERR_PERMITTED_VIOLATION; + } + + if (ia5casecmp(baseptr, dnsptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + +static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) +{ + const char *baseptr = (char *)base->data; + const char *emlptr = (char *)eml->data; + + const char *baseat = strchr(baseptr, '@'); + const char *emlat = strchr(emlptr, '@'); + if (!emlat) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + /* Special case: initial '.' is RHS match */ + if (!baseat && (*baseptr == '.')) { + if (eml->length > base->length) { + emlptr += eml->length - base->length; + if (ia5casecmp(baseptr, emlptr) == 0) + return X509_V_OK; + } + return X509_V_ERR_PERMITTED_VIOLATION; + } + + /* If we have anything before '@' match local part */ + + if (baseat) { + if (baseat != baseptr) { + if ((baseat - baseptr) != (emlat - emlptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + /* Case sensitive match of local part */ + if (strncmp(baseptr, emlptr, emlat - emlptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + } + /* Position base after '@' */ + baseptr = baseat + 1; + } + emlptr = emlat + 1; + /* Just have hostname left to match: case insensitive */ + if (ia5casecmp(baseptr, emlptr)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + +static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) +{ + const char *baseptr = (char *)base->data; + const char *hostptr = (char *)uri->data; + const char *p = strchr(hostptr, ':'); + int hostlen; + /* Check for foo:// and skip past it */ + if (!p || (p[1] != '/') || (p[2] != '/')) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + hostptr = p + 3; + + /* Determine length of hostname part of URI */ + + /* Look for a port indicator as end of hostname first */ + + p = strchr(hostptr, ':'); + /* Otherwise look for trailing slash */ + if (!p) + p = strchr(hostptr, '/'); + + if (!p) + hostlen = strlen(hostptr); + else + hostlen = p - hostptr; + + if (hostlen == 0) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + /* Special case: initial '.' is RHS match */ + if (*baseptr == '.') { + if (hostlen > base->length) { + p = hostptr + hostlen - base->length; + if (ia5ncasecmp(p, baseptr, base->length) == 0) + return X509_V_OK; + } + return X509_V_ERR_PERMITTED_VIOLATION; + } + + if ((base->length != (int)hostlen) + || ia5ncasecmp(hostptr, baseptr, hostlen)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + +static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base) +{ + int hostlen, baselen, i; + unsigned char *hostptr, *baseptr, *maskptr; + hostptr = ip->data; + hostlen = ip->length; + baseptr = base->data; + baselen = base->length; + + /* Invalid if not IPv4 or IPv6 */ + if (!((hostlen == 4) || (hostlen == 16))) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + if (!((baselen == 8) || (baselen == 32))) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + /* Do not match IPv4 with IPv6 */ + if (hostlen * 2 != baselen) + return X509_V_ERR_PERMITTED_VIOLATION; + + maskptr = base->data + hostlen; + + /* Considering possible not aligned base ipAddress */ + /* Not checking for wrong mask definition: i.e.: 255.0.255.0 */ + for (i = 0; i < hostlen; i++) + if ((hostptr[i] & maskptr[i]) != (baseptr[i] & maskptr[i])) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} diff --git a/crypto/x509/v3_pci.c b/crypto/x509/v3_pci.c new file mode 100644 index 0000000000..856b70d9b5 --- /dev/null +++ b/crypto/x509/v3_pci.c @@ -0,0 +1,324 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * This file is dual-licensed and is also available under the following + * terms: + * + * Copyright (c) 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * 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. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS 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 INSTITUTE OR 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. + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static int i2r_pci(X509V3_EXT_METHOD *method, PROXY_CERT_INFO_EXTENSION *ext, + BIO *out, int indent); +static PROXY_CERT_INFO_EXTENSION *r2i_pci(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *str); + +const X509V3_EXT_METHOD v3_pci = + { NID_proxyCertInfo, 0, ASN1_ITEM_ref(PROXY_CERT_INFO_EXTENSION), + 0, 0, 0, 0, + 0, 0, + NULL, NULL, + (X509V3_EXT_I2R)i2r_pci, + (X509V3_EXT_R2I)r2i_pci, + NULL, +}; + +static int i2r_pci(X509V3_EXT_METHOD *method, PROXY_CERT_INFO_EXTENSION *pci, + BIO *out, int indent) +{ + BIO_printf(out, "%*sPath Length Constraint: ", indent, ""); + if (pci->pcPathLengthConstraint) + i2a_ASN1_INTEGER(out, pci->pcPathLengthConstraint); + else + BIO_printf(out, "infinite"); + BIO_puts(out, "\n"); + BIO_printf(out, "%*sPolicy Language: ", indent, ""); + i2a_ASN1_OBJECT(out, pci->proxyPolicy->policyLanguage); + if (pci->proxyPolicy->policy && pci->proxyPolicy->policy->data) + BIO_printf(out, "\n%*sPolicy Text: %s", indent, "", + pci->proxyPolicy->policy->data); + return 1; +} + +static int process_pci_value(CONF_VALUE *val, + ASN1_OBJECT **language, ASN1_INTEGER **pathlen, + ASN1_OCTET_STRING **policy) +{ + int free_policy = 0; + + if (strcmp(val->name, "language") == 0) { + if (*language) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED); + X509V3_conf_err(val); + return 0; + } + if ((*language = OBJ_txt2obj(val->value, 0)) == NULL) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + return 0; + } + } else if (strcmp(val->name, "pathlen") == 0) { + if (*pathlen) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED); + X509V3_conf_err(val); + return 0; + } + if (!X509V3_get_value_int(val, pathlen)) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_POLICY_PATH_LENGTH); + X509V3_conf_err(val); + return 0; + } + } else if (strcmp(val->name, "policy") == 0) { + unsigned char *tmp_data = NULL; + long val_len; + if (!*policy) { + *policy = ASN1_OCTET_STRING_new(); + if (*policy == NULL) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + return 0; + } + free_policy = 1; + } + if (strncmp(val->value, "hex:", 4) == 0) { + unsigned char *tmp_data2 = + OPENSSL_hexstr2buf(val->value + 4, &val_len); + + if (!tmp_data2) { + X509V3_conf_err(val); + goto err; + } + + tmp_data = OPENSSL_realloc((*policy)->data, + (*policy)->length + val_len + 1); + if (tmp_data) { + (*policy)->data = tmp_data; + memcpy(&(*policy)->data[(*policy)->length], + tmp_data2, val_len); + (*policy)->length += val_len; + (*policy)->data[(*policy)->length] = '\0'; + } else { + OPENSSL_free(tmp_data2); + /* + * realloc failure implies the original data space is b0rked + * too! + */ + OPENSSL_free((*policy)->data); + (*policy)->data = NULL; + (*policy)->length = 0; + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + goto err; + } + OPENSSL_free(tmp_data2); + } else if (strncmp(val->value, "file:", 5) == 0) { + unsigned char buf[2048]; + int n; + BIO *b = BIO_new_file(val->value + 5, "r"); + if (!b) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_BIO_LIB); + X509V3_conf_err(val); + goto err; + } + while ((n = BIO_read(b, buf, sizeof(buf))) > 0 + || (n == 0 && BIO_should_retry(b))) { + if (!n) + continue; + + tmp_data = OPENSSL_realloc((*policy)->data, + (*policy)->length + n + 1); + + if (!tmp_data) { + OPENSSL_free((*policy)->data); + (*policy)->data = NULL; + (*policy)->length = 0; + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + BIO_free_all(b); + goto err; + } + + (*policy)->data = tmp_data; + memcpy(&(*policy)->data[(*policy)->length], buf, n); + (*policy)->length += n; + (*policy)->data[(*policy)->length] = '\0'; + } + BIO_free_all(b); + + if (n < 0) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_BIO_LIB); + X509V3_conf_err(val); + goto err; + } + } else if (strncmp(val->value, "text:", 5) == 0) { + val_len = strlen(val->value + 5); + tmp_data = OPENSSL_realloc((*policy)->data, + (*policy)->length + val_len + 1); + if (tmp_data) { + (*policy)->data = tmp_data; + memcpy(&(*policy)->data[(*policy)->length], + val->value + 5, val_len); + (*policy)->length += val_len; + (*policy)->data[(*policy)->length] = '\0'; + } else { + /* + * realloc failure implies the original data space is b0rked + * too! + */ + OPENSSL_free((*policy)->data); + (*policy)->data = NULL; + (*policy)->length = 0; + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + goto err; + } + } else { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, + X509V3_R_INCORRECT_POLICY_SYNTAX_TAG); + X509V3_conf_err(val); + goto err; + } + if (!tmp_data) { + X509V3err(X509V3_F_PROCESS_PCI_VALUE, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + goto err; + } + } + return 1; + err: + if (free_policy) { + ASN1_OCTET_STRING_free(*policy); + *policy = NULL; + } + return 0; +} + +static PROXY_CERT_INFO_EXTENSION *r2i_pci(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *value) +{ + PROXY_CERT_INFO_EXTENSION *pci = NULL; + STACK_OF(CONF_VALUE) *vals; + ASN1_OBJECT *language = NULL; + ASN1_INTEGER *pathlen = NULL; + ASN1_OCTET_STRING *policy = NULL; + int i, j; + + vals = X509V3_parse_list(value); + for (i = 0; i < sk_CONF_VALUE_num(vals); i++) { + CONF_VALUE *cnf = sk_CONF_VALUE_value(vals, i); + if (!cnf->name || (*cnf->name != '@' && !cnf->value)) { + X509V3err(X509V3_F_R2I_PCI, + X509V3_R_INVALID_PROXY_POLICY_SETTING); + X509V3_conf_err(cnf); + goto err; + } + if (*cnf->name == '@') { + STACK_OF(CONF_VALUE) *sect; + int success_p = 1; + + sect = X509V3_get_section(ctx, cnf->name + 1); + if (!sect) { + X509V3err(X509V3_F_R2I_PCI, X509V3_R_INVALID_SECTION); + X509V3_conf_err(cnf); + goto err; + } + for (j = 0; success_p && j < sk_CONF_VALUE_num(sect); j++) { + success_p = + process_pci_value(sk_CONF_VALUE_value(sect, j), + &language, &pathlen, &policy); + } + X509V3_section_free(ctx, sect); + if (!success_p) + goto err; + } else { + if (!process_pci_value(cnf, &language, &pathlen, &policy)) { + X509V3_conf_err(cnf); + goto err; + } + } + } + + /* Language is mandatory */ + if (!language) { + X509V3err(X509V3_F_R2I_PCI, + X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED); + goto err; + } + i = OBJ_obj2nid(language); + if ((i == NID_Independent || i == NID_id_ppl_inheritAll) && policy) { + X509V3err(X509V3_F_R2I_PCI, + X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY); + goto err; + } + + pci = PROXY_CERT_INFO_EXTENSION_new(); + if (pci == NULL) { + X509V3err(X509V3_F_R2I_PCI, ERR_R_MALLOC_FAILURE); + goto err; + } + + pci->proxyPolicy->policyLanguage = language; + language = NULL; + pci->proxyPolicy->policy = policy; + policy = NULL; + pci->pcPathLengthConstraint = pathlen; + pathlen = NULL; + goto end; + err: + ASN1_OBJECT_free(language); + ASN1_INTEGER_free(pathlen); + pathlen = NULL; + ASN1_OCTET_STRING_free(policy); + policy = NULL; + PROXY_CERT_INFO_EXTENSION_free(pci); + pci = NULL; + end: + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + return pci; +} diff --git a/crypto/x509/v3_pcia.c b/crypto/x509/v3_pcia.c new file mode 100644 index 0000000000..7f5985f5e8 --- /dev/null +++ b/crypto/x509/v3_pcia.c @@ -0,0 +1,64 @@ +/* + * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * This file is dual-licensed and is also available under the following + * terms: + * + * Copyright (c) 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * 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. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS 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 INSTITUTE OR 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. + */ + +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> + +ASN1_SEQUENCE(PROXY_POLICY) = + { + ASN1_SIMPLE(PROXY_POLICY,policyLanguage,ASN1_OBJECT), + ASN1_OPT(PROXY_POLICY,policy,ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(PROXY_POLICY) + +IMPLEMENT_ASN1_FUNCTIONS(PROXY_POLICY) + +ASN1_SEQUENCE(PROXY_CERT_INFO_EXTENSION) = + { + ASN1_OPT(PROXY_CERT_INFO_EXTENSION,pcPathLengthConstraint,ASN1_INTEGER), + ASN1_SIMPLE(PROXY_CERT_INFO_EXTENSION,proxyPolicy,PROXY_POLICY) +} ASN1_SEQUENCE_END(PROXY_CERT_INFO_EXTENSION) + +IMPLEMENT_ASN1_FUNCTIONS(PROXY_CERT_INFO_EXTENSION) diff --git a/crypto/x509/v3_pcons.c b/crypto/x509/v3_pcons.c new file mode 100644 index 0000000000..e6d50a6fe7 --- /dev/null +++ b/crypto/x509/v3_pcons.c @@ -0,0 +1,91 @@ +/* + * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD + *method, void *bcons, STACK_OF(CONF_VALUE) + *extlist); +static void *v2i_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values); + +const X509V3_EXT_METHOD v3_policy_constraints = { + NID_policy_constraints, 0, + ASN1_ITEM_ref(POLICY_CONSTRAINTS), + 0, 0, 0, 0, + 0, 0, + i2v_POLICY_CONSTRAINTS, + v2i_POLICY_CONSTRAINTS, + NULL, NULL, + NULL +}; + +ASN1_SEQUENCE(POLICY_CONSTRAINTS) = { + ASN1_IMP_OPT(POLICY_CONSTRAINTS, requireExplicitPolicy, ASN1_INTEGER,0), + ASN1_IMP_OPT(POLICY_CONSTRAINTS, inhibitPolicyMapping, ASN1_INTEGER,1) +} ASN1_SEQUENCE_END(POLICY_CONSTRAINTS) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(POLICY_CONSTRAINTS) + +static STACK_OF(CONF_VALUE) *i2v_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD + *method, void *a, STACK_OF(CONF_VALUE) + *extlist) +{ + POLICY_CONSTRAINTS *pcons = a; + X509V3_add_value_int("Require Explicit Policy", + pcons->requireExplicitPolicy, &extlist); + X509V3_add_value_int("Inhibit Policy Mapping", + pcons->inhibitPolicyMapping, &extlist); + return extlist; +} + +static void *v2i_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *values) +{ + POLICY_CONSTRAINTS *pcons = NULL; + CONF_VALUE *val; + int i; + + if ((pcons = POLICY_CONSTRAINTS_new()) == NULL) { + X509V3err(X509V3_F_V2I_POLICY_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(values); i++) { + val = sk_CONF_VALUE_value(values, i); + if (strcmp(val->name, "requireExplicitPolicy") == 0) { + if (!X509V3_get_value_int(val, &pcons->requireExplicitPolicy)) + goto err; + } else if (strcmp(val->name, "inhibitPolicyMapping") == 0) { + if (!X509V3_get_value_int(val, &pcons->inhibitPolicyMapping)) + goto err; + } else { + X509V3err(X509V3_F_V2I_POLICY_CONSTRAINTS, X509V3_R_INVALID_NAME); + X509V3_conf_err(val); + goto err; + } + } + if (!pcons->inhibitPolicyMapping && !pcons->requireExplicitPolicy) { + X509V3err(X509V3_F_V2I_POLICY_CONSTRAINTS, + X509V3_R_ILLEGAL_EMPTY_EXTENSION); + goto err; + } + + return pcons; + err: + POLICY_CONSTRAINTS_free(pcons); + return NULL; +} diff --git a/crypto/x509/v3_pku.c b/crypto/x509/v3_pku.c new file mode 100644 index 0000000000..484dda813f --- /dev/null +++ b/crypto/x509/v3_pku.c @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static int i2r_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method, + PKEY_USAGE_PERIOD *usage, BIO *out, + int indent); + +const X509V3_EXT_METHOD v3_pkey_usage_period = { + NID_private_key_usage_period, 0, ASN1_ITEM_ref(PKEY_USAGE_PERIOD), + 0, 0, 0, 0, + 0, 0, 0, 0, + (X509V3_EXT_I2R)i2r_PKEY_USAGE_PERIOD, NULL, + NULL +}; + +ASN1_SEQUENCE(PKEY_USAGE_PERIOD) = { + ASN1_IMP_OPT(PKEY_USAGE_PERIOD, notBefore, ASN1_GENERALIZEDTIME, 0), + ASN1_IMP_OPT(PKEY_USAGE_PERIOD, notAfter, ASN1_GENERALIZEDTIME, 1) +} ASN1_SEQUENCE_END(PKEY_USAGE_PERIOD) + +IMPLEMENT_ASN1_FUNCTIONS(PKEY_USAGE_PERIOD) + +static int i2r_PKEY_USAGE_PERIOD(X509V3_EXT_METHOD *method, + PKEY_USAGE_PERIOD *usage, BIO *out, + int indent) +{ + BIO_printf(out, "%*s", indent, ""); + if (usage->notBefore) { + BIO_write(out, "Not Before: ", 12); + ASN1_GENERALIZEDTIME_print(out, usage->notBefore); + if (usage->notAfter) + BIO_write(out, ", ", 2); + } + if (usage->notAfter) { + BIO_write(out, "Not After: ", 11); + ASN1_GENERALIZEDTIME_print(out, usage->notAfter); + } + return 1; +} diff --git a/crypto/x509/v3_pmaps.c b/crypto/x509/v3_pmaps.c new file mode 100644 index 0000000000..2b4784027c --- /dev/null +++ b/crypto/x509/v3_pmaps.c @@ -0,0 +1,112 @@ +/* + * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static void *v2i_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); +static STACK_OF(CONF_VALUE) *i2v_POLICY_MAPPINGS(const X509V3_EXT_METHOD + *method, void *pmps, STACK_OF(CONF_VALUE) + *extlist); + +const X509V3_EXT_METHOD v3_policy_mappings = { + NID_policy_mappings, 0, + ASN1_ITEM_ref(POLICY_MAPPINGS), + 0, 0, 0, 0, + 0, 0, + i2v_POLICY_MAPPINGS, + v2i_POLICY_MAPPINGS, + 0, 0, + NULL +}; + +ASN1_SEQUENCE(POLICY_MAPPING) = { + ASN1_SIMPLE(POLICY_MAPPING, issuerDomainPolicy, ASN1_OBJECT), + ASN1_SIMPLE(POLICY_MAPPING, subjectDomainPolicy, ASN1_OBJECT) +} ASN1_SEQUENCE_END(POLICY_MAPPING) + +ASN1_ITEM_TEMPLATE(POLICY_MAPPINGS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, POLICY_MAPPINGS, + POLICY_MAPPING) +ASN1_ITEM_TEMPLATE_END(POLICY_MAPPINGS) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(POLICY_MAPPING) + +static STACK_OF(CONF_VALUE) *i2v_POLICY_MAPPINGS(const X509V3_EXT_METHOD + *method, void *a, STACK_OF(CONF_VALUE) + *ext_list) +{ + POLICY_MAPPINGS *pmaps = a; + POLICY_MAPPING *pmap; + int i; + char obj_tmp1[80]; + char obj_tmp2[80]; + + for (i = 0; i < sk_POLICY_MAPPING_num(pmaps); i++) { + pmap = sk_POLICY_MAPPING_value(pmaps, i); + i2t_ASN1_OBJECT(obj_tmp1, 80, pmap->issuerDomainPolicy); + i2t_ASN1_OBJECT(obj_tmp2, 80, pmap->subjectDomainPolicy); + X509V3_add_value(obj_tmp1, obj_tmp2, &ext_list); + } + return ext_list; +} + +static void *v2i_POLICY_MAPPINGS(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + POLICY_MAPPING *pmap = NULL; + ASN1_OBJECT *obj1 = NULL, *obj2 = NULL; + CONF_VALUE *val; + POLICY_MAPPINGS *pmaps; + const int num = sk_CONF_VALUE_num(nval); + int i; + + if ((pmaps = sk_POLICY_MAPPING_new_reserve(NULL, num)) == NULL) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < num; i++) { + val = sk_CONF_VALUE_value(nval, i); + if (!val->value || !val->name) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + goto err; + } + obj1 = OBJ_txt2obj(val->name, 0); + obj2 = OBJ_txt2obj(val->value, 0); + if (!obj1 || !obj2) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, + X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + goto err; + } + pmap = POLICY_MAPPING_new(); + if (pmap == NULL) { + X509V3err(X509V3_F_V2I_POLICY_MAPPINGS, ERR_R_MALLOC_FAILURE); + goto err; + } + pmap->issuerDomainPolicy = obj1; + pmap->subjectDomainPolicy = obj2; + obj1 = obj2 = NULL; + sk_POLICY_MAPPING_push(pmaps, pmap); /* no failure as it was reserved */ + } + return pmaps; + err: + ASN1_OBJECT_free(obj1); + ASN1_OBJECT_free(obj2); + sk_POLICY_MAPPING_pop_free(pmaps, POLICY_MAPPING_free); + return NULL; +} diff --git a/crypto/x509/v3_prn.c b/crypto/x509/v3_prn.c new file mode 100644 index 0000000000..2ef76c1b5b --- /dev/null +++ b/crypto/x509/v3_prn.c @@ -0,0 +1,211 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* X509 v3 extension utilities */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/x509v3.h> + +/* Extension printing routines */ + +static int unknown_ext_print(BIO *out, const unsigned char *ext, int extlen, + unsigned long flag, int indent, int supported); + +/* Print out a name+value stack */ + +void X509V3_EXT_val_prn(BIO *out, STACK_OF(CONF_VALUE) *val, int indent, + int ml) +{ + int i; + CONF_VALUE *nval; + if (!val) + return; + if (!ml || !sk_CONF_VALUE_num(val)) { + BIO_printf(out, "%*s", indent, ""); + if (!sk_CONF_VALUE_num(val)) + BIO_puts(out, "<EMPTY>\n"); + } + for (i = 0; i < sk_CONF_VALUE_num(val); i++) { + if (ml) { + if (i > 0) + BIO_printf(out, "\n"); + BIO_printf(out, "%*s", indent, ""); + } + else if (i > 0) + BIO_printf(out, ", "); + nval = sk_CONF_VALUE_value(val, i); + if (!nval->name) + BIO_puts(out, nval->value); + else if (!nval->value) + BIO_puts(out, nval->name); +#ifndef CHARSET_EBCDIC + else + BIO_printf(out, "%s:%s", nval->name, nval->value); +#else + else { + int len; + char *tmp; + len = strlen(nval->value) + 1; + tmp = OPENSSL_malloc(len); + if (tmp != NULL) { + ascii2ebcdic(tmp, nval->value, len); + BIO_printf(out, "%s:%s", nval->name, tmp); + OPENSSL_free(tmp); + } + } +#endif + } +} + +/* Main routine: print out a general extension */ + +int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, + int indent) +{ + void *ext_str = NULL; + char *value = NULL; + ASN1_OCTET_STRING *extoct; + const unsigned char *p; + int extlen; + const X509V3_EXT_METHOD *method; + STACK_OF(CONF_VALUE) *nval = NULL; + int ok = 1; + + extoct = X509_EXTENSION_get_data(ext); + p = ASN1_STRING_get0_data(extoct); + extlen = ASN1_STRING_length(extoct); + + if ((method = X509V3_EXT_get(ext)) == NULL) + return unknown_ext_print(out, p, extlen, flag, indent, 0); + if (method->it) + ext_str = ASN1_item_d2i(NULL, &p, extlen, ASN1_ITEM_ptr(method->it)); + else + ext_str = method->d2i(NULL, &p, extlen); + + if (!ext_str) + return unknown_ext_print(out, p, extlen, flag, indent, 1); + + if (method->i2s) { + if ((value = method->i2s(method, ext_str)) == NULL) { + ok = 0; + goto err; + } +#ifndef CHARSET_EBCDIC + BIO_printf(out, "%*s%s", indent, "", value); +#else + { + int len; + char *tmp; + len = strlen(value) + 1; + tmp = OPENSSL_malloc(len); + if (tmp != NULL) { + ascii2ebcdic(tmp, value, len); + BIO_printf(out, "%*s%s", indent, "", tmp); + OPENSSL_free(tmp); + } + } +#endif + } else if (method->i2v) { + if ((nval = method->i2v(method, ext_str, NULL)) == NULL) { + ok = 0; + goto err; + } + X509V3_EXT_val_prn(out, nval, indent, + method->ext_flags & X509V3_EXT_MULTILINE); + } else if (method->i2r) { + if (!method->i2r(method, ext_str, out, indent)) + ok = 0; + } else + ok = 0; + + err: + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + OPENSSL_free(value); + if (method->it) + ASN1_item_free(ext_str, ASN1_ITEM_ptr(method->it)); + else + method->ext_free(ext_str); + return ok; +} + +int X509V3_extensions_print(BIO *bp, const char *title, + const STACK_OF(X509_EXTENSION) *exts, + unsigned long flag, int indent) +{ + int i, j; + + if (sk_X509_EXTENSION_num(exts) <= 0) + return 1; + + if (title) { + BIO_printf(bp, "%*s%s:\n", indent, "", title); + indent += 4; + } + + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { + ASN1_OBJECT *obj; + X509_EXTENSION *ex; + ex = sk_X509_EXTENSION_value(exts, i); + if (indent && BIO_printf(bp, "%*s", indent, "") <= 0) + return 0; + obj = X509_EXTENSION_get_object(ex); + i2a_ASN1_OBJECT(bp, obj); + j = X509_EXTENSION_get_critical(ex); + if (BIO_printf(bp, ": %s\n", j ? "critical" : "") <= 0) + return 0; + if (!X509V3_EXT_print(bp, ex, flag, indent + 4)) { + BIO_printf(bp, "%*s", indent + 4, ""); + ASN1_STRING_print(bp, X509_EXTENSION_get_data(ex)); + } + if (BIO_write(bp, "\n", 1) <= 0) + return 0; + } + return 1; +} + +static int unknown_ext_print(BIO *out, const unsigned char *ext, int extlen, + unsigned long flag, int indent, int supported) +{ + switch (flag & X509V3_EXT_UNKNOWN_MASK) { + + case X509V3_EXT_DEFAULT: + return 0; + + case X509V3_EXT_ERROR_UNKNOWN: + if (supported) + BIO_printf(out, "%*s<Parse Error>", indent, ""); + else + BIO_printf(out, "%*s<Not Supported>", indent, ""); + return 1; + + case X509V3_EXT_PARSE_UNKNOWN: + return ASN1_parse_dump(out, ext, extlen, indent, -1); + case X509V3_EXT_DUMP_UNKNOWN: + return BIO_dump_indent(out, (const char *)ext, extlen, indent); + + default: + return 1; + } +} + +#ifndef OPENSSL_NO_STDIO +int X509V3_EXT_print_fp(FILE *fp, X509_EXTENSION *ext, int flag, int indent) +{ + BIO *bio_tmp; + int ret; + + if ((bio_tmp = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) + return 0; + ret = X509V3_EXT_print(bio_tmp, ext, flag, indent); + BIO_free(bio_tmp); + return ret; +} +#endif diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c new file mode 100644 index 0000000000..d0ac7e6ed9 --- /dev/null +++ b/crypto/x509/v3_purp.c @@ -0,0 +1,909 @@ +/* + * Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include <openssl/x509v3.h> +#include <openssl/x509_vfy.h> +#include "internal/x509_int.h" +#include "internal/tsan_assist.h" + +static void x509v3_cache_extensions(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); +static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int purpose_smime(const X509 *x, int ca); +static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, + int ca); +static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca); +static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca); + +static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b); +static void xptable_free(X509_PURPOSE *p); + +static X509_PURPOSE xstandard[] = { + {X509_PURPOSE_SSL_CLIENT, X509_TRUST_SSL_CLIENT, 0, + check_purpose_ssl_client, "SSL client", "sslclient", NULL}, + {X509_PURPOSE_SSL_SERVER, X509_TRUST_SSL_SERVER, 0, + check_purpose_ssl_server, "SSL server", "sslserver", NULL}, + {X509_PURPOSE_NS_SSL_SERVER, X509_TRUST_SSL_SERVER, 0, + check_purpose_ns_ssl_server, "Netscape SSL server", "nssslserver", NULL}, + {X509_PURPOSE_SMIME_SIGN, X509_TRUST_EMAIL, 0, check_purpose_smime_sign, + "S/MIME signing", "smimesign", NULL}, + {X509_PURPOSE_SMIME_ENCRYPT, X509_TRUST_EMAIL, 0, + check_purpose_smime_encrypt, "S/MIME encryption", "smimeencrypt", NULL}, + {X509_PURPOSE_CRL_SIGN, X509_TRUST_COMPAT, 0, check_purpose_crl_sign, + "CRL signing", "crlsign", NULL}, + {X509_PURPOSE_ANY, X509_TRUST_DEFAULT, 0, no_check, "Any Purpose", "any", + NULL}, + {X509_PURPOSE_OCSP_HELPER, X509_TRUST_COMPAT, 0, ocsp_helper, + "OCSP helper", "ocsphelper", NULL}, + {X509_PURPOSE_TIMESTAMP_SIGN, X509_TRUST_TSA, 0, + check_purpose_timestamp_sign, "Time Stamp signing", "timestampsign", + NULL}, +}; + +#define X509_PURPOSE_COUNT OSSL_NELEM(xstandard) + +static STACK_OF(X509_PURPOSE) *xptable = NULL; + +static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b) +{ + return (*a)->purpose - (*b)->purpose; +} + +/* + * As much as I'd like to make X509_check_purpose use a "const" X509* I + * really can't because it does recalculate hashes and do other non-const + * things. + */ +int X509_check_purpose(X509 *x, int id, int ca) +{ + int idx; + const X509_PURPOSE *pt; + + x509v3_cache_extensions(x); + + /* Return if side-effect only call */ + if (id == -1) + return 1; + idx = X509_PURPOSE_get_by_id(id); + if (idx == -1) + return -1; + pt = X509_PURPOSE_get0(idx); + return pt->check_purpose(pt, x, ca); +} + +int X509_PURPOSE_set(int *p, int purpose) +{ + if (X509_PURPOSE_get_by_id(purpose) == -1) { + X509V3err(X509V3_F_X509_PURPOSE_SET, X509V3_R_INVALID_PURPOSE); + return 0; + } + *p = purpose; + return 1; +} + +int X509_PURPOSE_get_count(void) +{ + if (!xptable) + return X509_PURPOSE_COUNT; + return sk_X509_PURPOSE_num(xptable) + X509_PURPOSE_COUNT; +} + +X509_PURPOSE *X509_PURPOSE_get0(int idx) +{ + if (idx < 0) + return NULL; + if (idx < (int)X509_PURPOSE_COUNT) + return xstandard + idx; + return sk_X509_PURPOSE_value(xptable, idx - X509_PURPOSE_COUNT); +} + +int X509_PURPOSE_get_by_sname(const char *sname) +{ + int i; + X509_PURPOSE *xptmp; + for (i = 0; i < X509_PURPOSE_get_count(); i++) { + xptmp = X509_PURPOSE_get0(i); + if (strcmp(xptmp->sname, sname) == 0) + return i; + } + return -1; +} + +int X509_PURPOSE_get_by_id(int purpose) +{ + X509_PURPOSE tmp; + int idx; + + if ((purpose >= X509_PURPOSE_MIN) && (purpose <= X509_PURPOSE_MAX)) + return purpose - X509_PURPOSE_MIN; + if (xptable == NULL) + return -1; + tmp.purpose = purpose; + idx = sk_X509_PURPOSE_find(xptable, &tmp); + if (idx < 0) + return -1; + return idx + X509_PURPOSE_COUNT; +} + +int X509_PURPOSE_add(int id, int trust, int flags, + int (*ck) (const X509_PURPOSE *, const X509 *, int), + const char *name, const char *sname, void *arg) +{ + int idx; + X509_PURPOSE *ptmp; + /* + * This is set according to what we change: application can't set it + */ + flags &= ~X509_PURPOSE_DYNAMIC; + /* This will always be set for application modified trust entries */ + flags |= X509_PURPOSE_DYNAMIC_NAME; + /* Get existing entry if any */ + idx = X509_PURPOSE_get_by_id(id); + /* Need a new entry */ + if (idx == -1) { + if ((ptmp = OPENSSL_malloc(sizeof(*ptmp))) == NULL) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + return 0; + } + ptmp->flags = X509_PURPOSE_DYNAMIC; + } else + ptmp = X509_PURPOSE_get0(idx); + + /* OPENSSL_free existing name if dynamic */ + if (ptmp->flags & X509_PURPOSE_DYNAMIC_NAME) { + OPENSSL_free(ptmp->name); + OPENSSL_free(ptmp->sname); + } + /* dup supplied name */ + ptmp->name = OPENSSL_strdup(name); + ptmp->sname = OPENSSL_strdup(sname); + if (!ptmp->name || !ptmp->sname) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + /* Keep the dynamic flag of existing entry */ + ptmp->flags &= X509_PURPOSE_DYNAMIC; + /* Set all other flags */ + ptmp->flags |= flags; + + ptmp->purpose = id; + ptmp->trust = trust; + ptmp->check_purpose = ck; + ptmp->usr_data = arg; + + /* If its a new entry manage the dynamic table */ + if (idx == -1) { + if (xptable == NULL + && (xptable = sk_X509_PURPOSE_new(xp_cmp)) == NULL) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + if (!sk_X509_PURPOSE_push(xptable, ptmp)) { + X509V3err(X509V3_F_X509_PURPOSE_ADD, ERR_R_MALLOC_FAILURE); + goto err; + } + } + return 1; + err: + if (idx == -1) { + OPENSSL_free(ptmp->name); + OPENSSL_free(ptmp->sname); + OPENSSL_free(ptmp); + } + return 0; +} + +static void xptable_free(X509_PURPOSE *p) +{ + if (!p) + return; + if (p->flags & X509_PURPOSE_DYNAMIC) { + if (p->flags & X509_PURPOSE_DYNAMIC_NAME) { + OPENSSL_free(p->name); + OPENSSL_free(p->sname); + } + OPENSSL_free(p); + } +} + +void X509_PURPOSE_cleanup(void) +{ + sk_X509_PURPOSE_pop_free(xptable, xptable_free); + xptable = NULL; +} + +int X509_PURPOSE_get_id(const X509_PURPOSE *xp) +{ + return xp->purpose; +} + +char *X509_PURPOSE_get0_name(const X509_PURPOSE *xp) +{ + return xp->name; +} + +char *X509_PURPOSE_get0_sname(const X509_PURPOSE *xp) +{ + return xp->sname; +} + +int X509_PURPOSE_get_trust(const X509_PURPOSE *xp) +{ + return xp->trust; +} + +static int nid_cmp(const int *a, const int *b) +{ + return *a - *b; +} + +DECLARE_OBJ_BSEARCH_CMP_FN(int, int, nid); +IMPLEMENT_OBJ_BSEARCH_CMP_FN(int, int, nid); + +int X509_supported_extension(X509_EXTENSION *ex) +{ + /* + * This table is a list of the NIDs of supported extensions: that is + * those which are used by the verify process. If an extension is + * critical and doesn't appear in this list then the verify process will + * normally reject the certificate. The list must be kept in numerical + * order because it will be searched using bsearch. + */ + + static const int supported_nids[] = { + NID_netscape_cert_type, /* 71 */ + NID_key_usage, /* 83 */ + NID_subject_alt_name, /* 85 */ + NID_basic_constraints, /* 87 */ + NID_certificate_policies, /* 89 */ + NID_crl_distribution_points, /* 103 */ + NID_ext_key_usage, /* 126 */ +#ifndef OPENSSL_NO_RFC3779 + NID_sbgp_ipAddrBlock, /* 290 */ + NID_sbgp_autonomousSysNum, /* 291 */ +#endif + NID_policy_constraints, /* 401 */ + NID_proxyCertInfo, /* 663 */ + NID_name_constraints, /* 666 */ + NID_policy_mappings, /* 747 */ + NID_inhibit_any_policy /* 748 */ + }; + + int ex_nid = OBJ_obj2nid(X509_EXTENSION_get_object(ex)); + + if (ex_nid == NID_undef) + return 0; + + if (OBJ_bsearch_nid(&ex_nid, supported_nids, OSSL_NELEM(supported_nids))) + return 1; + return 0; +} + +static void setup_dp(X509 *x, DIST_POINT *dp) +{ + X509_NAME *iname = NULL; + int i; + if (dp->reasons) { + if (dp->reasons->length > 0) + dp->dp_reasons = dp->reasons->data[0]; + if (dp->reasons->length > 1) + dp->dp_reasons |= (dp->reasons->data[1] << 8); + dp->dp_reasons &= CRLDP_ALL_REASONS; + } else + dp->dp_reasons = CRLDP_ALL_REASONS; + if (!dp->distpoint || (dp->distpoint->type != 1)) + return; + for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++) { + GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i); + if (gen->type == GEN_DIRNAME) { + iname = gen->d.directoryName; + break; + } + } + if (!iname) + iname = X509_get_issuer_name(x); + + DIST_POINT_set_dpname(dp->distpoint, iname); + +} + +static void setup_crldp(X509 *x) +{ + int i; + x->crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL); + for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++) + setup_dp(x, sk_DIST_POINT_value(x->crldp, i)); +} + +#define V1_ROOT (EXFLAG_V1|EXFLAG_SS) +#define ku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage))) +#define xku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_XKUSAGE) && !((x)->ex_xkusage & (usage))) +#define ns_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_NSCERT) && !((x)->ex_nscert & (usage))) + +static void x509v3_cache_extensions(X509 *x) +{ + BASIC_CONSTRAINTS *bs; + PROXY_CERT_INFO_EXTENSION *pci; + ASN1_BIT_STRING *usage; + ASN1_BIT_STRING *ns; + EXTENDED_KEY_USAGE *extusage; + X509_EXTENSION *ex; + int i; + +#ifdef tsan_ld_acq + /* fast lock-free check, see end of the function for details. */ + if (tsan_ld_acq((TSAN_QUALIFIER int *)&x->ex_cached)) + return; +#endif + + CRYPTO_THREAD_write_lock(x->lock); + if (x->ex_flags & EXFLAG_SET) { + CRYPTO_THREAD_unlock(x->lock); + return; + } + + X509_digest(x, EVP_sha1(), x->sha1_hash, NULL); + /* V1 should mean no extensions ... */ + if (!X509_get_version(x)) + x->ex_flags |= EXFLAG_V1; + /* Handle basic constraints */ + if ((bs = X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL))) { + if (bs->ca) + x->ex_flags |= EXFLAG_CA; + if (bs->pathlen) { + if ((bs->pathlen->type == V_ASN1_NEG_INTEGER) + || !bs->ca) { + x->ex_flags |= EXFLAG_INVALID; + x->ex_pathlen = 0; + } else + x->ex_pathlen = ASN1_INTEGER_get(bs->pathlen); + } else + x->ex_pathlen = -1; + BASIC_CONSTRAINTS_free(bs); + x->ex_flags |= EXFLAG_BCONS; + } + /* Handle proxy certificates */ + if ((pci = X509_get_ext_d2i(x, NID_proxyCertInfo, NULL, NULL))) { + if (x->ex_flags & EXFLAG_CA + || X509_get_ext_by_NID(x, NID_subject_alt_name, -1) >= 0 + || X509_get_ext_by_NID(x, NID_issuer_alt_name, -1) >= 0) { + x->ex_flags |= EXFLAG_INVALID; + } + if (pci->pcPathLengthConstraint) { + x->ex_pcpathlen = ASN1_INTEGER_get(pci->pcPathLengthConstraint); + } else + x->ex_pcpathlen = -1; + PROXY_CERT_INFO_EXTENSION_free(pci); + x->ex_flags |= EXFLAG_PROXY; + } + /* Handle key usage */ + if ((usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) { + if (usage->length > 0) { + x->ex_kusage = usage->data[0]; + if (usage->length > 1) + x->ex_kusage |= usage->data[1] << 8; + } else + x->ex_kusage = 0; + x->ex_flags |= EXFLAG_KUSAGE; + ASN1_BIT_STRING_free(usage); + } + x->ex_xkusage = 0; + if ((extusage = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL))) { + x->ex_flags |= EXFLAG_XKUSAGE; + for (i = 0; i < sk_ASN1_OBJECT_num(extusage); i++) { + switch (OBJ_obj2nid(sk_ASN1_OBJECT_value(extusage, i))) { + case NID_server_auth: + x->ex_xkusage |= XKU_SSL_SERVER; + break; + + case NID_client_auth: + x->ex_xkusage |= XKU_SSL_CLIENT; + break; + + case NID_email_protect: + x->ex_xkusage |= XKU_SMIME; + break; + + case NID_code_sign: + x->ex_xkusage |= XKU_CODE_SIGN; + break; + + case NID_ms_sgc: + case NID_ns_sgc: + x->ex_xkusage |= XKU_SGC; + break; + + case NID_OCSP_sign: + x->ex_xkusage |= XKU_OCSP_SIGN; + break; + + case NID_time_stamp: + x->ex_xkusage |= XKU_TIMESTAMP; + break; + + case NID_dvcs: + x->ex_xkusage |= XKU_DVCS; + break; + + case NID_anyExtendedKeyUsage: + x->ex_xkusage |= XKU_ANYEKU; + break; + } + } + sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free); + } + + if ((ns = X509_get_ext_d2i(x, NID_netscape_cert_type, NULL, NULL))) { + if (ns->length > 0) + x->ex_nscert = ns->data[0]; + else + x->ex_nscert = 0; + x->ex_flags |= EXFLAG_NSCERT; + ASN1_BIT_STRING_free(ns); + } + x->skid = X509_get_ext_d2i(x, NID_subject_key_identifier, NULL, NULL); + x->akid = X509_get_ext_d2i(x, NID_authority_key_identifier, NULL, NULL); + /* Does subject name match issuer ? */ + if (!X509_NAME_cmp(X509_get_subject_name(x), X509_get_issuer_name(x))) { + x->ex_flags |= EXFLAG_SI; + /* If SKID matches AKID also indicate self signed */ + if (X509_check_akid(x, x->akid) == X509_V_OK && + !ku_reject(x, KU_KEY_CERT_SIGN)) + x->ex_flags |= EXFLAG_SS; + } + x->altname = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + x->nc = X509_get_ext_d2i(x, NID_name_constraints, &i, NULL); + if (!x->nc && (i != -1)) + x->ex_flags |= EXFLAG_INVALID; + setup_crldp(x); + +#ifndef OPENSSL_NO_RFC3779 + x->rfc3779_addr = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, NULL, NULL); + x->rfc3779_asid = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, + NULL, NULL); +#endif + for (i = 0; i < X509_get_ext_count(x); i++) { + ex = X509_get_ext(x, i); + if (OBJ_obj2nid(X509_EXTENSION_get_object(ex)) + == NID_freshest_crl) + x->ex_flags |= EXFLAG_FRESHEST; + if (!X509_EXTENSION_get_critical(ex)) + continue; + if (!X509_supported_extension(ex)) { + x->ex_flags |= EXFLAG_CRITICAL; + break; + } + } + x509_init_sig_info(x); + x->ex_flags |= EXFLAG_SET; +#ifdef tsan_st_rel + tsan_st_rel((TSAN_QUALIFIER int *)&x->ex_cached, 1); + /* + * Above store triggers fast lock-free check in the beginning of the + * function. But one has to ensure that the structure is "stable", i.e. + * all stores are visible on all processors. Hence the release fence. + */ +#endif + CRYPTO_THREAD_unlock(x->lock); +} + +/*- + * CA checks common to all purposes + * return codes: + * 0 not a CA + * 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. + */ + +static int check_ca(const X509 *x) +{ + /* keyUsage if present should allow cert signing */ + if (ku_reject(x, KU_KEY_CERT_SIGN)) + return 0; + if (x->ex_flags & EXFLAG_BCONS) { + if (x->ex_flags & EXFLAG_CA) + return 1; + /* 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; + /* 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; + } +} + +void X509_set_proxy_flag(X509 *x) +{ + x->ex_flags |= EXFLAG_PROXY; +} + +void X509_set_proxy_pathlen(X509 *x, long l) +{ + x->ex_pcpathlen = l; +} + +int X509_check_ca(X509 *x) +{ + x509v3_cache_extensions(x); + + 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 = check_ca(x); + if (!ca_ret) + return 0; + /* check nsCertType if present */ + if (ca_ret != 5 || x->ex_nscert & NS_SSL_CA) + return ca_ret; + else + return 0; +} + +static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + if (xku_reject(x, XKU_SSL_CLIENT)) + return 0; + if (ca) + return check_ssl_ca(x); + /* We need to do digital signatures or key agreement */ + if (ku_reject(x, KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)) + return 0; + /* nsCertType if present should allow SSL client use */ + if (ns_reject(x, NS_SSL_CLIENT)) + return 0; + return 1; +} + +/* + * Key usage needed for TLS/SSL server: digital signature, encipherment or + * key agreement. The ssl code can check this more thoroughly for individual + * key types. + */ +#define KU_TLS \ + KU_DIGITAL_SIGNATURE|KU_KEY_ENCIPHERMENT|KU_KEY_AGREEMENT + +static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + if (xku_reject(x, XKU_SSL_SERVER | XKU_SGC)) + return 0; + if (ca) + return check_ssl_ca(x); + + if (ns_reject(x, NS_SSL_SERVER)) + return 0; + if (ku_reject(x, KU_TLS)) + return 0; + + return 1; + +} + +static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int ret; + ret = check_purpose_ssl_server(xp, x, ca); + if (!ret || ca) + return ret; + /* We need to encipher or Netscape complains */ + if (ku_reject(x, KU_KEY_ENCIPHERMENT)) + return 0; + return ret; +} + +/* common S/MIME checks */ +static int purpose_smime(const X509 *x, int ca) +{ + if (xku_reject(x, XKU_SMIME)) + return 0; + if (ca) { + int ca_ret; + ca_ret = check_ca(x); + if (!ca_ret) + return 0; + /* check nsCertType if present */ + if (ca_ret != 5 || x->ex_nscert & NS_SMIME_CA) + return ca_ret; + else + return 0; + } + if (x->ex_flags & EXFLAG_NSCERT) { + if (x->ex_nscert & NS_SMIME) + return 1; + /* Workaround for some buggy certificates */ + if (x->ex_nscert & NS_SSL_CLIENT) + return 2; + return 0; + } + return 1; +} + +static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int ret; + ret = purpose_smime(x, ca); + if (!ret || ca) + return ret; + if (ku_reject(x, KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION)) + return 0; + return ret; +} + +static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int ret; + ret = purpose_smime(x, ca); + if (!ret || ca) + return ret; + if (ku_reject(x, KU_KEY_ENCIPHERMENT)) + return 0; + return ret; +} + +static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + if (ca) { + int ca_ret; + if ((ca_ret = check_ca(x)) != 2) + return ca_ret; + else + return 0; + } + if (ku_reject(x, KU_CRL_SIGN)) + return 0; + return 1; +} + +/* + * OCSP helper: this is *not* a full OCSP check. It just checks that each CA + * is valid. Additional checks must be made on the chain. + */ + +static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca) +{ + /* + * 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; +} + +static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, + int ca) +{ + int i_ext; + + /* If ca is true we must return if this is a valid CA certificate. */ + if (ca) + return check_ca(x); + + /* + * Check the optional key usage field: + * if Key Usage is present, it must be one of digitalSignature + * and/or nonRepudiation (other values are not consistent and shall + * be rejected). + */ + if ((x->ex_flags & EXFLAG_KUSAGE) + && ((x->ex_kusage & ~(KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)) || + !(x->ex_kusage & (KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)))) + return 0; + + /* Only time stamp key usage is permitted and it's required. */ + if (!(x->ex_flags & EXFLAG_XKUSAGE) || x->ex_xkusage != XKU_TIMESTAMP) + return 0; + + /* Extended Key Usage MUST be critical */ + i_ext = X509_get_ext_by_NID(x, NID_ext_key_usage, -1); + if (i_ext >= 0) { + X509_EXTENSION *ext = X509_get_ext((X509 *)x, i_ext); + if (!X509_EXTENSION_get_critical(ext)) + return 0; + } + + return 1; +} + +static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca) +{ + return 1; +} + +/*- + * Various checks to see if one certificate issued the second. + * This can be used to prune a set of possible issuer certificates + * which have been looked up using some simple method such as by + * subject name. + * These are: + * 1. Check issuer_name(subject) == subject_name(issuer) + * 2. If akid(subject) exists, check that it matches issuer + * 3. Check that issuer public key algorithm matches subject signature algorithm + * 4. If key_usage(issuer) exists, check that it supports certificate signing + * returns 0 for OK, positive for reason for mismatch, reasons match + * codes for X509_verify_cert() + */ + +int X509_check_issued(X509 *issuer, X509 *subject) +{ + if (X509_NAME_cmp(X509_get_subject_name(issuer), + X509_get_issuer_name(subject))) + return X509_V_ERR_SUBJECT_ISSUER_MISMATCH; + + x509v3_cache_extensions(issuer); + x509v3_cache_extensions(subject); + + if (subject->akid) { + int ret = X509_check_akid(issuer, subject->akid); + if (ret != X509_V_OK) + return ret; + } + + { + /* + * Check if the subject signature algorithm matches the issuer's PUBKEY + * algorithm + */ + EVP_PKEY *i_pkey = X509_get0_pubkey(issuer); + X509_ALGOR *s_algor = &subject->cert_info.signature; + int s_pknid = NID_undef, s_mdnid = NID_undef; + + if (i_pkey == NULL) + return X509_V_ERR_NO_ISSUER_PUBLIC_KEY; + + if (!OBJ_find_sigid_algs(OBJ_obj2nid(s_algor->algorithm), + &s_mdnid, &s_pknid) + || EVP_PKEY_type(s_pknid) != EVP_PKEY_base_id(i_pkey)) + return X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH; + } + + if (subject->ex_flags & EXFLAG_PROXY) { + if (ku_reject(issuer, KU_DIGITAL_SIGNATURE)) + return X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE; + } else if (ku_reject(issuer, KU_KEY_CERT_SIGN)) + return X509_V_ERR_KEYUSAGE_NO_CERTSIGN; + return X509_V_OK; +} + +int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid) +{ + + if (!akid) + return X509_V_OK; + + /* Check key ids (if present) */ + if (akid->keyid && issuer->skid && + ASN1_OCTET_STRING_cmp(akid->keyid, issuer->skid)) + return X509_V_ERR_AKID_SKID_MISMATCH; + /* Check serial number */ + if (akid->serial && + ASN1_INTEGER_cmp(X509_get_serialNumber(issuer), akid->serial)) + return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; + /* Check issuer name */ + if (akid->issuer) { + /* + * Ugh, for some peculiar reason AKID includes SEQUENCE OF + * GeneralName. So look for a DirName. There may be more than one but + * we only take any notice of the first. + */ + GENERAL_NAMES *gens; + GENERAL_NAME *gen; + X509_NAME *nm = NULL; + int i; + gens = akid->issuer; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type == GEN_DIRNAME) { + nm = gen->d.dirn; + break; + } + } + if (nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer))) + return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; + } + return X509_V_OK; +} + +uint32_t X509_get_extension_flags(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + return x->ex_flags; +} + +uint32_t X509_get_key_usage(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + if (x->ex_flags & EXFLAG_KUSAGE) + return x->ex_kusage; + return UINT32_MAX; +} + +uint32_t X509_get_extended_key_usage(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + if (x->ex_flags & EXFLAG_XKUSAGE) + return x->ex_xkusage; + return UINT32_MAX; +} + +const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + return x->skid; +} + +const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + X509_check_purpose(x, -1, -1); + return (x->akid != NULL ? x->akid->keyid : NULL); +} + +long X509_get_pathlen(X509 *x) +{ + /* Called for side effect of caching extensions */ + if (X509_check_purpose(x, -1, -1) != 1 + || (x->ex_flags & EXFLAG_BCONS) == 0) + return -1; + return x->ex_pathlen; +} + +long X509_get_proxy_pathlen(X509 *x) +{ + /* Called for side effect of caching extensions */ + if (X509_check_purpose(x, -1, -1) != 1 + || (x->ex_flags & EXFLAG_PROXY) == 0) + return -1; + return x->ex_pcpathlen; +} diff --git a/crypto/x509/v3_skey.c b/crypto/x509/v3_skey.c new file mode 100644 index 0000000000..f8a780d916 --- /dev/null +++ b/crypto/x509/v3_skey.c @@ -0,0 +1,106 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/x509v3.h> +#include "internal/x509_int.h" +#include "ext_dat.h" + +static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *str); +const X509V3_EXT_METHOD v3_skey_id = { + NID_subject_key_identifier, 0, ASN1_ITEM_ref(ASN1_OCTET_STRING), + 0, 0, 0, 0, + (X509V3_EXT_I2S)i2s_ASN1_OCTET_STRING, + (X509V3_EXT_S2I)s2i_skey_id, + 0, 0, 0, 0, + NULL +}; + +char *i2s_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, + const ASN1_OCTET_STRING *oct) +{ + return OPENSSL_buf2hexstr(oct->data, oct->length); +} + +ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *str) +{ + ASN1_OCTET_STRING *oct; + long length; + + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + X509V3err(X509V3_F_S2I_ASN1_OCTET_STRING, ERR_R_MALLOC_FAILURE); + return NULL; + } + + if ((oct->data = OPENSSL_hexstr2buf(str, &length)) == NULL) { + ASN1_OCTET_STRING_free(oct); + return NULL; + } + + oct->length = length; + + return oct; + +} + +static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *str) +{ + ASN1_OCTET_STRING *oct; + X509_PUBKEY *pubkey; + const unsigned char *pk; + int pklen; + unsigned char pkey_dig[EVP_MAX_MD_SIZE]; + unsigned int diglen; + + if (strcmp(str, "hash")) + return s2i_ASN1_OCTET_STRING(method, ctx, str); + + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + X509V3err(X509V3_F_S2I_SKEY_ID, ERR_R_MALLOC_FAILURE); + return NULL; + } + + if (ctx && (ctx->flags == CTX_TEST)) + return oct; + + if (!ctx || (!ctx->subject_req && !ctx->subject_cert)) { + X509V3err(X509V3_F_S2I_SKEY_ID, X509V3_R_NO_PUBLIC_KEY); + goto err; + } + + if (ctx->subject_req) + pubkey = ctx->subject_req->req_info.pubkey; + else + pubkey = ctx->subject_cert->cert_info.key; + + if (pubkey == NULL) { + X509V3err(X509V3_F_S2I_SKEY_ID, X509V3_R_NO_PUBLIC_KEY); + goto err; + } + + X509_PUBKEY_get0_param(NULL, &pk, &pklen, NULL, pubkey); + + if (!EVP_Digest(pk, pklen, pkey_dig, &diglen, EVP_sha1(), NULL)) + goto err; + + if (!ASN1_OCTET_STRING_set(oct, pkey_dig, diglen)) { + X509V3err(X509V3_F_S2I_SKEY_ID, ERR_R_MALLOC_FAILURE); + goto err; + } + + return oct; + + err: + ASN1_OCTET_STRING_free(oct); + return NULL; +} diff --git a/crypto/x509/v3_sxnet.c b/crypto/x509/v3_sxnet.c new file mode 100644 index 0000000000..ddb2a0312e --- /dev/null +++ b/crypto/x509/v3_sxnet.c @@ -0,0 +1,226 @@ +/* + * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +/* Support for Thawte strong extranet extension */ + +#define SXNET_TEST + +static int sxnet_i2r(X509V3_EXT_METHOD *method, SXNET *sx, BIO *out, + int indent); +#ifdef SXNET_TEST +static SXNET *sxnet_v2i(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); +#endif +const X509V3_EXT_METHOD v3_sxnet = { + NID_sxnet, X509V3_EXT_MULTILINE, ASN1_ITEM_ref(SXNET), + 0, 0, 0, 0, + 0, 0, + 0, +#ifdef SXNET_TEST + (X509V3_EXT_V2I)sxnet_v2i, +#else + 0, +#endif + (X509V3_EXT_I2R)sxnet_i2r, + 0, + NULL +}; + +ASN1_SEQUENCE(SXNETID) = { + ASN1_SIMPLE(SXNETID, zone, ASN1_INTEGER), + ASN1_SIMPLE(SXNETID, user, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(SXNETID) + +IMPLEMENT_ASN1_FUNCTIONS(SXNETID) + +ASN1_SEQUENCE(SXNET) = { + ASN1_SIMPLE(SXNET, version, ASN1_INTEGER), + ASN1_SEQUENCE_OF(SXNET, ids, SXNETID) +} ASN1_SEQUENCE_END(SXNET) + +IMPLEMENT_ASN1_FUNCTIONS(SXNET) + +static int sxnet_i2r(X509V3_EXT_METHOD *method, SXNET *sx, BIO *out, + int indent) +{ + long v; + char *tmp; + SXNETID *id; + int i; + v = ASN1_INTEGER_get(sx->version); + BIO_printf(out, "%*sVersion: %ld (0x%lX)", indent, "", v + 1, v); + for (i = 0; i < sk_SXNETID_num(sx->ids); i++) { + id = sk_SXNETID_value(sx->ids, i); + tmp = i2s_ASN1_INTEGER(NULL, id->zone); + BIO_printf(out, "\n%*sZone: %s, User: ", indent, "", tmp); + OPENSSL_free(tmp); + ASN1_STRING_print(out, id->user); + } + return 1; +} + +#ifdef SXNET_TEST + +/* + * NBB: this is used for testing only. It should *not* be used for anything + * else because it will just take static IDs from the configuration file and + * they should really be separate values for each user. + */ + +static SXNET *sxnet_v2i(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + CONF_VALUE *cnf; + SXNET *sx = NULL; + int i; + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + cnf = sk_CONF_VALUE_value(nval, i); + if (!SXNET_add_id_asc(&sx, cnf->name, cnf->value, -1)) + return NULL; + } + return sx; +} + +#endif + +/* Strong Extranet utility functions */ + +/* Add an id given the zone as an ASCII number */ + +int SXNET_add_id_asc(SXNET **psx, const char *zone, const char *user, int userlen) +{ + ASN1_INTEGER *izone; + + if ((izone = s2i_ASN1_INTEGER(NULL, zone)) == NULL) { + X509V3err(X509V3_F_SXNET_ADD_ID_ASC, X509V3_R_ERROR_CONVERTING_ZONE); + return 0; + } + return SXNET_add_id_INTEGER(psx, izone, user, userlen); +} + +/* Add an id given the zone as an unsigned long */ + +int SXNET_add_id_ulong(SXNET **psx, unsigned long lzone, const char *user, + int userlen) +{ + ASN1_INTEGER *izone; + + if ((izone = ASN1_INTEGER_new()) == NULL + || !ASN1_INTEGER_set(izone, lzone)) { + X509V3err(X509V3_F_SXNET_ADD_ID_ULONG, ERR_R_MALLOC_FAILURE); + ASN1_INTEGER_free(izone); + return 0; + } + return SXNET_add_id_INTEGER(psx, izone, user, userlen); + +} + +/* + * Add an id given the zone as an ASN1_INTEGER. Note this version uses the + * passed integer and doesn't make a copy so don't free it up afterwards. + */ + +int SXNET_add_id_INTEGER(SXNET **psx, ASN1_INTEGER *zone, const char *user, + int userlen) +{ + SXNET *sx = NULL; + SXNETID *id = NULL; + if (!psx || !zone || !user) { + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, + X509V3_R_INVALID_NULL_ARGUMENT); + return 0; + } + if (userlen == -1) + userlen = strlen(user); + if (userlen > 64) { + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, X509V3_R_USER_TOO_LONG); + return 0; + } + if (*psx == NULL) { + if ((sx = SXNET_new()) == NULL) + goto err; + if (!ASN1_INTEGER_set(sx->version, 0)) + goto err; + *psx = sx; + } else + sx = *psx; + if (SXNET_get_id_INTEGER(sx, zone)) { + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, X509V3_R_DUPLICATE_ZONE_ID); + return 0; + } + + if ((id = SXNETID_new()) == NULL) + goto err; + if (userlen == -1) + userlen = strlen(user); + + if (!ASN1_OCTET_STRING_set(id->user, (const unsigned char *)user, userlen)) + goto err; + if (!sk_SXNETID_push(sx->ids, id)) + goto err; + id->zone = zone; + return 1; + + err: + X509V3err(X509V3_F_SXNET_ADD_ID_INTEGER, ERR_R_MALLOC_FAILURE); + SXNETID_free(id); + SXNET_free(sx); + *psx = NULL; + return 0; +} + +ASN1_OCTET_STRING *SXNET_get_id_asc(SXNET *sx, const char *zone) +{ + ASN1_INTEGER *izone; + ASN1_OCTET_STRING *oct; + + if ((izone = s2i_ASN1_INTEGER(NULL, zone)) == NULL) { + X509V3err(X509V3_F_SXNET_GET_ID_ASC, X509V3_R_ERROR_CONVERTING_ZONE); + return NULL; + } + oct = SXNET_get_id_INTEGER(sx, izone); + ASN1_INTEGER_free(izone); + return oct; +} + +ASN1_OCTET_STRING *SXNET_get_id_ulong(SXNET *sx, unsigned long lzone) +{ + ASN1_INTEGER *izone; + ASN1_OCTET_STRING *oct; + + if ((izone = ASN1_INTEGER_new()) == NULL + || !ASN1_INTEGER_set(izone, lzone)) { + X509V3err(X509V3_F_SXNET_GET_ID_ULONG, ERR_R_MALLOC_FAILURE); + ASN1_INTEGER_free(izone); + return NULL; + } + oct = SXNET_get_id_INTEGER(sx, izone); + ASN1_INTEGER_free(izone); + return oct; +} + +ASN1_OCTET_STRING *SXNET_get_id_INTEGER(SXNET *sx, ASN1_INTEGER *zone) +{ + SXNETID *id; + int i; + for (i = 0; i < sk_SXNETID_num(sx->ids); i++) { + id = sk_SXNETID_value(sx->ids, i); + if (!ASN1_INTEGER_cmp(id->zone, zone)) + return id->user; + } + return NULL; +} diff --git a/crypto/x509/v3_tlsf.c b/crypto/x509/v3_tlsf.c new file mode 100644 index 0000000000..d3ab3f81ad --- /dev/null +++ b/crypto/x509/v3_tlsf.c @@ -0,0 +1,137 @@ +/* + * Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "e_os.h" +#include "internal/cryptlib.h" +#include <stdio.h> +#include "internal/o_str.h" +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" + +static STACK_OF(CONF_VALUE) *i2v_TLS_FEATURE(const X509V3_EXT_METHOD *method, + TLS_FEATURE *tls_feature, + STACK_OF(CONF_VALUE) *ext_list); +static TLS_FEATURE *v2i_TLS_FEATURE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval); + +ASN1_ITEM_TEMPLATE(TLS_FEATURE) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, TLS_FEATURE, ASN1_INTEGER) +static_ASN1_ITEM_TEMPLATE_END(TLS_FEATURE) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(TLS_FEATURE) + +const X509V3_EXT_METHOD v3_tls_feature = { + NID_tlsfeature, 0, + ASN1_ITEM_ref(TLS_FEATURE), + 0, 0, 0, 0, + 0, 0, + (X509V3_EXT_I2V)i2v_TLS_FEATURE, + (X509V3_EXT_V2I)v2i_TLS_FEATURE, + 0, 0, + NULL +}; + + +typedef struct { + long num; + const char *name; +} TLS_FEATURE_NAME; + +static TLS_FEATURE_NAME tls_feature_tbl[] = { + { 5, "status_request" }, + { 17, "status_request_v2" } +}; + +/* + * i2v_TLS_FEATURE converts the TLS_FEATURE structure tls_feature into the + * STACK_OF(CONF_VALUE) structure ext_list. STACK_OF(CONF_VALUE) is the format + * used by the CONF library to represent a multi-valued extension. ext_list is + * returned. + */ +static STACK_OF(CONF_VALUE) *i2v_TLS_FEATURE(const X509V3_EXT_METHOD *method, + TLS_FEATURE *tls_feature, + STACK_OF(CONF_VALUE) *ext_list) +{ + int i; + size_t j; + ASN1_INTEGER *ai; + long tlsextid; + for (i = 0; i < sk_ASN1_INTEGER_num(tls_feature); i++) { + ai = sk_ASN1_INTEGER_value(tls_feature, i); + tlsextid = ASN1_INTEGER_get(ai); + for (j = 0; j < OSSL_NELEM(tls_feature_tbl); j++) + if (tlsextid == tls_feature_tbl[j].num) + break; + if (j < OSSL_NELEM(tls_feature_tbl)) + X509V3_add_value(NULL, tls_feature_tbl[j].name, &ext_list); + else + X509V3_add_value_int(NULL, ai, &ext_list); + } + return ext_list; +} + +/* + * v2i_TLS_FEATURE converts the multi-valued extension nval into a TLS_FEATURE + * structure, which is returned if the conversion is successful. In case of + * error, NULL is returned. + */ +static TLS_FEATURE *v2i_TLS_FEATURE(const X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) +{ + TLS_FEATURE *tlsf; + char *extval, *endptr; + ASN1_INTEGER *ai; + CONF_VALUE *val; + int i; + size_t j; + long tlsextid; + + if ((tlsf = sk_ASN1_INTEGER_new_null()) == NULL) { + X509V3err(X509V3_F_V2I_TLS_FEATURE, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { + val = sk_CONF_VALUE_value(nval, i); + if (val->value) + extval = val->value; + else + extval = val->name; + + for (j = 0; j < OSSL_NELEM(tls_feature_tbl); j++) + if (strcasecmp(extval, tls_feature_tbl[j].name) == 0) + break; + if (j < OSSL_NELEM(tls_feature_tbl)) + tlsextid = tls_feature_tbl[j].num; + else { + tlsextid = strtol(extval, &endptr, 10); + if (((*endptr) != '\0') || (extval == endptr) || (tlsextid < 0) || + (tlsextid > 65535)) { + X509V3err(X509V3_F_V2I_TLS_FEATURE, X509V3_R_INVALID_SYNTAX); + X509V3_conf_err(val); + goto err; + } + } + + if ((ai = ASN1_INTEGER_new()) == NULL + || !ASN1_INTEGER_set(ai, tlsextid) + || sk_ASN1_INTEGER_push(tlsf, ai) <= 0) { + X509V3err(X509V3_F_V2I_TLS_FEATURE, ERR_R_MALLOC_FAILURE); + goto err; + } + } + return tlsf; + + err: + sk_ASN1_INTEGER_pop_free(tlsf, ASN1_INTEGER_free); + return NULL; +} diff --git a/crypto/x509/v3_utl.c b/crypto/x509/v3_utl.c new file mode 100644 index 0000000000..24f50a13b7 --- /dev/null +++ b/crypto/x509/v3_utl.c @@ -0,0 +1,1239 @@ +/* + * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* X509 v3 extension utilities */ + +#include "e_os.h" +#include "internal/cryptlib.h" +#include <stdio.h> +#include "internal/ctype.h" +#include <openssl/conf.h> +#include <openssl/crypto.h> +#include <openssl/x509v3.h> +#include "internal/x509_int.h" +#include <openssl/bn.h> +#include "ext_dat.h" + +static char *strip_spaces(char *name); +static int sk_strcmp(const char *const *a, const char *const *b); +static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, + GENERAL_NAMES *gens); +static void str_free(OPENSSL_STRING str); +static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, const ASN1_IA5STRING *email); + +static int ipv4_from_asc(unsigned char *v4, const char *in); +static int ipv6_from_asc(unsigned char *v6, const char *in); +static int ipv6_cb(const char *elem, int len, void *usr); +static int ipv6_hex(unsigned char *out, const char *in, int inlen); + +/* Add a CONF_VALUE name value pair to stack */ + +int X509V3_add_value(const char *name, const char *value, + STACK_OF(CONF_VALUE) **extlist) +{ + CONF_VALUE *vtmp = NULL; + char *tname = NULL, *tvalue = NULL; + int sk_allocated = (*extlist == NULL); + + if (name && (tname = OPENSSL_strdup(name)) == NULL) + goto err; + if (value && (tvalue = OPENSSL_strdup(value)) == NULL) + goto err; + if ((vtmp = OPENSSL_malloc(sizeof(*vtmp))) == NULL) + goto err; + if (sk_allocated && (*extlist = sk_CONF_VALUE_new_null()) == NULL) + goto err; + vtmp->section = NULL; + vtmp->name = tname; + vtmp->value = tvalue; + if (!sk_CONF_VALUE_push(*extlist, vtmp)) + goto err; + return 1; + err: + X509V3err(X509V3_F_X509V3_ADD_VALUE, ERR_R_MALLOC_FAILURE); + if (sk_allocated) { + sk_CONF_VALUE_free(*extlist); + *extlist = NULL; + } + OPENSSL_free(vtmp); + OPENSSL_free(tname); + OPENSSL_free(tvalue); + return 0; +} + +int X509V3_add_value_uchar(const char *name, const unsigned char *value, + STACK_OF(CONF_VALUE) **extlist) +{ + return X509V3_add_value(name, (const char *)value, extlist); +} + +/* Free function for STACK_OF(CONF_VALUE) */ + +void X509V3_conf_free(CONF_VALUE *conf) +{ + if (!conf) + return; + OPENSSL_free(conf->name); + OPENSSL_free(conf->value); + OPENSSL_free(conf->section); + OPENSSL_free(conf); +} + +int X509V3_add_value_bool(const char *name, int asn1_bool, + STACK_OF(CONF_VALUE) **extlist) +{ + if (asn1_bool) + return X509V3_add_value(name, "TRUE", extlist); + return X509V3_add_value(name, "FALSE", extlist); +} + +int X509V3_add_value_bool_nf(const char *name, int asn1_bool, + STACK_OF(CONF_VALUE) **extlist) +{ + if (asn1_bool) + return X509V3_add_value(name, "TRUE", extlist); + return 1; +} + +static char *bignum_to_string(const BIGNUM *bn) +{ + char *tmp, *ret; + size_t len; + + /* + * Display large numbers in hex and small numbers in decimal. Converting to + * decimal takes quadratic time and is no more useful than hex for large + * numbers. + */ + if (BN_num_bits(bn) < 128) + return BN_bn2dec(bn); + + tmp = BN_bn2hex(bn); + if (tmp == NULL) + return NULL; + + len = strlen(tmp) + 3; + ret = OPENSSL_malloc(len); + if (ret == NULL) { + X509V3err(X509V3_F_BIGNUM_TO_STRING, ERR_R_MALLOC_FAILURE); + OPENSSL_free(tmp); + return NULL; + } + + /* Prepend "0x", but place it after the "-" if negative. */ + if (tmp[0] == '-') { + OPENSSL_strlcpy(ret, "-0x", len); + OPENSSL_strlcat(ret, tmp + 1, len); + } else { + OPENSSL_strlcpy(ret, "0x", len); + OPENSSL_strlcat(ret, tmp, len); + } + OPENSSL_free(tmp); + return ret; +} + +char *i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *method, const ASN1_ENUMERATED *a) +{ + BIGNUM *bntmp = NULL; + char *strtmp = NULL; + + if (!a) + return NULL; + if ((bntmp = ASN1_ENUMERATED_to_BN(a, NULL)) == NULL + || (strtmp = bignum_to_string(bntmp)) == NULL) + X509V3err(X509V3_F_I2S_ASN1_ENUMERATED, ERR_R_MALLOC_FAILURE); + BN_free(bntmp); + return strtmp; +} + +char *i2s_ASN1_INTEGER(X509V3_EXT_METHOD *method, const ASN1_INTEGER *a) +{ + BIGNUM *bntmp = NULL; + char *strtmp = NULL; + + if (!a) + return NULL; + if ((bntmp = ASN1_INTEGER_to_BN(a, NULL)) == NULL + || (strtmp = bignum_to_string(bntmp)) == NULL) + X509V3err(X509V3_F_I2S_ASN1_INTEGER, ERR_R_MALLOC_FAILURE); + BN_free(bntmp); + return strtmp; +} + +ASN1_INTEGER *s2i_ASN1_INTEGER(X509V3_EXT_METHOD *method, const char *value) +{ + BIGNUM *bn = NULL; + ASN1_INTEGER *aint; + int isneg, ishex; + int ret; + if (value == NULL) { + X509V3err(X509V3_F_S2I_ASN1_INTEGER, X509V3_R_INVALID_NULL_VALUE); + return NULL; + } + bn = BN_new(); + if (bn == NULL) { + X509V3err(X509V3_F_S2I_ASN1_INTEGER, ERR_R_MALLOC_FAILURE); + return NULL; + } + if (value[0] == '-') { + value++; + isneg = 1; + } else + isneg = 0; + + if (value[0] == '0' && ((value[1] == 'x') || (value[1] == 'X'))) { + value += 2; + ishex = 1; + } else + ishex = 0; + + if (ishex) + ret = BN_hex2bn(&bn, value); + else + ret = BN_dec2bn(&bn, value); + + if (!ret || value[ret]) { + BN_free(bn); + X509V3err(X509V3_F_S2I_ASN1_INTEGER, X509V3_R_BN_DEC2BN_ERROR); + return NULL; + } + + if (isneg && BN_is_zero(bn)) + isneg = 0; + + aint = BN_to_ASN1_INTEGER(bn, NULL); + BN_free(bn); + if (!aint) { + X509V3err(X509V3_F_S2I_ASN1_INTEGER, + X509V3_R_BN_TO_ASN1_INTEGER_ERROR); + return NULL; + } + if (isneg) + aint->type |= V_ASN1_NEG; + return aint; +} + +int X509V3_add_value_int(const char *name, const ASN1_INTEGER *aint, + STACK_OF(CONF_VALUE) **extlist) +{ + char *strtmp; + int ret; + + if (!aint) + return 1; + if ((strtmp = i2s_ASN1_INTEGER(NULL, aint)) == NULL) + return 0; + ret = X509V3_add_value(name, strtmp, extlist); + OPENSSL_free(strtmp); + return ret; +} + +int X509V3_get_value_bool(const CONF_VALUE *value, int *asn1_bool) +{ + const char *btmp; + + if ((btmp = value->value) == NULL) + goto err; + if (strcmp(btmp, "TRUE") == 0 + || strcmp(btmp, "true") == 0 + || strcmp(btmp, "Y") == 0 + || strcmp(btmp, "y") == 0 + || strcmp(btmp, "YES") == 0 + || strcmp(btmp, "yes") == 0) { + *asn1_bool = 0xff; + return 1; + } + if (strcmp(btmp, "FALSE") == 0 + || strcmp(btmp, "false") == 0 + || strcmp(btmp, "N") == 0 + || strcmp(btmp, "n") == 0 + || strcmp(btmp, "NO") == 0 + || strcmp(btmp, "no") == 0) { + *asn1_bool = 0; + return 1; + } + err: + X509V3err(X509V3_F_X509V3_GET_VALUE_BOOL, + X509V3_R_INVALID_BOOLEAN_STRING); + X509V3_conf_err(value); + return 0; +} + +int X509V3_get_value_int(const CONF_VALUE *value, ASN1_INTEGER **aint) +{ + ASN1_INTEGER *itmp; + + if ((itmp = s2i_ASN1_INTEGER(NULL, value->value)) == NULL) { + X509V3_conf_err(value); + return 0; + } + *aint = itmp; + return 1; +} + +#define HDR_NAME 1 +#define HDR_VALUE 2 + +/* + * #define DEBUG + */ + +STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line) +{ + char *p, *q, c; + char *ntmp, *vtmp; + STACK_OF(CONF_VALUE) *values = NULL; + char *linebuf; + int state; + /* We are going to modify the line so copy it first */ + linebuf = OPENSSL_strdup(line); + if (linebuf == NULL) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, ERR_R_MALLOC_FAILURE); + goto err; + } + state = HDR_NAME; + ntmp = NULL; + /* Go through all characters */ + for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); + p++) { + + switch (state) { + case HDR_NAME: + if (c == ':') { + state = HDR_VALUE; + *p = 0; + ntmp = strip_spaces(q); + if (!ntmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_NAME); + goto err; + } + q = p + 1; + } else if (c == ',') { + *p = 0; + ntmp = strip_spaces(q); + q = p + 1; + if (!ntmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_NAME); + goto err; + } + X509V3_add_value(ntmp, NULL, &values); + } + break; + + case HDR_VALUE: + if (c == ',') { + state = HDR_NAME; + *p = 0; + vtmp = strip_spaces(q); + if (!vtmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_VALUE); + goto err; + } + X509V3_add_value(ntmp, vtmp, &values); + ntmp = NULL; + q = p + 1; + } + + } + } + + if (state == HDR_VALUE) { + vtmp = strip_spaces(q); + if (!vtmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, + X509V3_R_INVALID_NULL_VALUE); + goto err; + } + X509V3_add_value(ntmp, vtmp, &values); + } else { + ntmp = strip_spaces(q); + if (!ntmp) { + X509V3err(X509V3_F_X509V3_PARSE_LIST, X509V3_R_INVALID_NULL_NAME); + goto err; + } + X509V3_add_value(ntmp, NULL, &values); + } + OPENSSL_free(linebuf); + return values; + + err: + OPENSSL_free(linebuf); + sk_CONF_VALUE_pop_free(values, X509V3_conf_free); + return NULL; + +} + +/* Delete leading and trailing spaces from a string */ +static char *strip_spaces(char *name) +{ + char *p, *q; + /* Skip over leading spaces */ + p = name; + while (*p && ossl_isspace(*p)) + p++; + if (!*p) + return NULL; + q = p + strlen(p) - 1; + while ((q != p) && ossl_isspace(*q)) + q--; + if (p != q) + q[1] = 0; + if (!*p) + return NULL; + return p; +} + + +/* + * V2I name comparison function: returns zero if 'name' matches cmp or cmp.* + */ + +int name_cmp(const char *name, const char *cmp) +{ + int len, ret; + char c; + len = strlen(cmp); + if ((ret = strncmp(name, cmp, len))) + return ret; + c = name[len]; + if (!c || (c == '.')) + return 0; + return 1; +} + +static int sk_strcmp(const char *const *a, const char *const *b) +{ + return strcmp(*a, *b); +} + +STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x) +{ + GENERAL_NAMES *gens; + STACK_OF(OPENSSL_STRING) *ret; + + gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + ret = get_email(X509_get_subject_name(x), gens); + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + return ret; +} + +STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x) +{ + AUTHORITY_INFO_ACCESS *info; + STACK_OF(OPENSSL_STRING) *ret = NULL; + int i; + + info = X509_get_ext_d2i(x, NID_info_access, NULL, NULL); + if (!info) + return NULL; + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) { + ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i); + if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) { + if (ad->location->type == GEN_URI) { + if (!append_ia5 + (&ret, ad->location->d.uniformResourceIdentifier)) + break; + } + } + } + AUTHORITY_INFO_ACCESS_free(info); + return ret; +} + +STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x) +{ + GENERAL_NAMES *gens; + STACK_OF(X509_EXTENSION) *exts; + STACK_OF(OPENSSL_STRING) *ret; + + exts = X509_REQ_get_extensions(x); + gens = X509V3_get_d2i(exts, NID_subject_alt_name, NULL, NULL); + ret = get_email(X509_REQ_get_subject_name(x), gens); + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + return ret; +} + +static STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, + GENERAL_NAMES *gens) +{ + STACK_OF(OPENSSL_STRING) *ret = NULL; + X509_NAME_ENTRY *ne; + const ASN1_IA5STRING *email; + GENERAL_NAME *gen; + int i = -1; + + /* Now add any email address(es) to STACK */ + /* First supplied X509_NAME */ + while ((i = X509_NAME_get_index_by_NID(name, + NID_pkcs9_emailAddress, i)) >= 0) { + ne = X509_NAME_get_entry(name, i); + email = X509_NAME_ENTRY_get_data(ne); + if (!append_ia5(&ret, email)) + return NULL; + } + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type != GEN_EMAIL) + continue; + if (!append_ia5(&ret, gen->d.ia5)) + return NULL; + } + return ret; +} + +static void str_free(OPENSSL_STRING str) +{ + OPENSSL_free(str); +} + +static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, const ASN1_IA5STRING *email) +{ + char *emtmp; + /* First some sanity checks */ + if (email->type != V_ASN1_IA5STRING) + return 1; + if (!email->data || !email->length) + return 1; + if (*sk == NULL) + *sk = sk_OPENSSL_STRING_new(sk_strcmp); + if (*sk == NULL) + return 0; + /* Don't add duplicates */ + if (sk_OPENSSL_STRING_find(*sk, (char *)email->data) != -1) + return 1; + emtmp = OPENSSL_strdup((char *)email->data); + if (emtmp == NULL || !sk_OPENSSL_STRING_push(*sk, emtmp)) { + OPENSSL_free(emtmp); /* free on push failure */ + X509_email_free(*sk); + *sk = NULL; + return 0; + } + return 1; +} + +void X509_email_free(STACK_OF(OPENSSL_STRING) *sk) +{ + sk_OPENSSL_STRING_pop_free(sk, str_free); +} + +typedef int (*equal_fn) (const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags); + +/* Skip pattern prefix to match "wildcard" subject */ +static void skip_prefix(const unsigned char **p, size_t *plen, + size_t subject_len, + unsigned int flags) +{ + const unsigned char *pattern = *p; + size_t pattern_len = *plen; + + /* + * If subject starts with a leading '.' followed by more octets, and + * pattern is longer, compare just an equal-length suffix with the + * full subject (starting at the '.'), provided the prefix contains + * no NULs. + */ + if ((flags & _X509_CHECK_FLAG_DOT_SUBDOMAINS) == 0) + return; + + while (pattern_len > subject_len && *pattern) { + if ((flags & X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS) && + *pattern == '.') + break; + ++pattern; + --pattern_len; + } + + /* Skip if entire prefix acceptable */ + if (pattern_len == subject_len) { + *p = pattern; + *plen = pattern_len; + } +} + +/* Compare while ASCII ignoring case. */ +static int equal_nocase(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + skip_prefix(&pattern, &pattern_len, subject_len, flags); + if (pattern_len != subject_len) + return 0; + while (pattern_len) { + unsigned char l = *pattern; + unsigned char r = *subject; + /* The pattern must not contain NUL characters. */ + if (l == 0) + return 0; + if (l != r) { + if ('A' <= l && l <= 'Z') + l = (l - 'A') + 'a'; + if ('A' <= r && r <= 'Z') + r = (r - 'A') + 'a'; + if (l != r) + return 0; + } + ++pattern; + ++subject; + --pattern_len; + } + return 1; +} + +/* Compare using memcmp. */ +static int equal_case(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + skip_prefix(&pattern, &pattern_len, subject_len, flags); + if (pattern_len != subject_len) + return 0; + return !memcmp(pattern, subject, pattern_len); +} + +/* + * RFC 5280, section 7.5, requires that only the domain is compared in a + * case-insensitive manner. + */ +static int equal_email(const unsigned char *a, size_t a_len, + const unsigned char *b, size_t b_len, + unsigned int unused_flags) +{ + size_t i = a_len; + if (a_len != b_len) + return 0; + /* + * We search backwards for the '@' character, so that we do not have to + * deal with quoted local-parts. The domain part is compared in a + * case-insensitive manner. + */ + while (i > 0) { + --i; + if (a[i] == '@' || b[i] == '@') { + if (!equal_nocase(a + i, a_len - i, b + i, a_len - i, 0)) + return 0; + break; + } + } + if (i == 0) + i = a_len; + return equal_case(a, i, b, i, 0); +} + +/* + * Compare the prefix and suffix with the subject, and check that the + * characters in-between are valid. + */ +static int wildcard_match(const unsigned char *prefix, size_t prefix_len, + const unsigned char *suffix, size_t suffix_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + const unsigned char *wildcard_start; + const unsigned char *wildcard_end; + const unsigned char *p; + int allow_multi = 0; + int allow_idna = 0; + + if (subject_len < prefix_len + suffix_len) + return 0; + if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags)) + return 0; + wildcard_start = subject + prefix_len; + wildcard_end = subject + (subject_len - suffix_len); + if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len, flags)) + return 0; + /* + * If the wildcard makes up the entire first label, it must match at + * least one character. + */ + if (prefix_len == 0 && *suffix == '.') { + if (wildcard_start == wildcard_end) + return 0; + allow_idna = 1; + if (flags & X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS) + allow_multi = 1; + } + /* IDNA labels cannot match partial wildcards */ + if (!allow_idna && + subject_len >= 4 && strncasecmp((char *)subject, "xn--", 4) == 0) + return 0; + /* The wildcard may match a literal '*' */ + if (wildcard_end == wildcard_start + 1 && *wildcard_start == '*') + return 1; + /* + * Check that the part matched by the wildcard contains only + * permitted characters and only matches a single label unless + * allow_multi is set. + */ + for (p = wildcard_start; p != wildcard_end; ++p) + if (!(('0' <= *p && *p <= '9') || + ('A' <= *p && *p <= 'Z') || + ('a' <= *p && *p <= 'z') || + *p == '-' || (allow_multi && *p == '.'))) + return 0; + return 1; +} + +#define LABEL_START (1 << 0) +#define LABEL_END (1 << 1) +#define LABEL_HYPHEN (1 << 2) +#define LABEL_IDNA (1 << 3) + +static const unsigned char *valid_star(const unsigned char *p, size_t len, + unsigned int flags) +{ + const unsigned char *star = 0; + size_t i; + int state = LABEL_START; + int dots = 0; + for (i = 0; i < len; ++i) { + /* + * Locate first and only legal wildcard, either at the start + * or end of a non-IDNA first and not final label. + */ + if (p[i] == '*') { + int atstart = (state & LABEL_START); + int atend = (i == len - 1 || p[i + 1] == '.'); + /*- + * At most one wildcard per pattern. + * No wildcards in IDNA labels. + * No wildcards after the first label. + */ + if (star != NULL || (state & LABEL_IDNA) != 0 || dots) + return NULL; + /* Only full-label '*.example.com' wildcards? */ + if ((flags & X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS) + && (!atstart || !atend)) + return NULL; + /* No 'foo*bar' wildcards */ + if (!atstart && !atend) + return NULL; + star = &p[i]; + state &= ~LABEL_START; + } else if (('a' <= p[i] && p[i] <= 'z') + || ('A' <= p[i] && p[i] <= 'Z') + || ('0' <= p[i] && p[i] <= '9')) { + if ((state & LABEL_START) != 0 + && len - i >= 4 && strncasecmp((char *)&p[i], "xn--", 4) == 0) + state |= LABEL_IDNA; + state &= ~(LABEL_HYPHEN | LABEL_START); + } else if (p[i] == '.') { + if ((state & (LABEL_HYPHEN | LABEL_START)) != 0) + return NULL; + state = LABEL_START; + ++dots; + } else if (p[i] == '-') { + /* no domain/subdomain starts with '-' */ + if ((state & LABEL_START) != 0) + return NULL; + state |= LABEL_HYPHEN; + } else + return NULL; + } + + /* + * The final label must not end in a hyphen or ".", and + * there must be at least two dots after the star. + */ + if ((state & (LABEL_START | LABEL_HYPHEN)) != 0 || dots < 2) + return NULL; + return star; +} + +/* Compare using wildcards. */ +static int equal_wildcard(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len, + unsigned int flags) +{ + const unsigned char *star = NULL; + + /* + * Subject names starting with '.' can only match a wildcard pattern + * via a subject sub-domain pattern suffix match. + */ + if (!(subject_len > 1 && subject[0] == '.')) + star = valid_star(pattern, pattern_len, flags); + if (star == NULL) + return equal_nocase(pattern, pattern_len, + subject, subject_len, flags); + return wildcard_match(pattern, star - pattern, + star + 1, (pattern + pattern_len) - star - 1, + subject, subject_len, flags); +} + +/* + * Compare an ASN1_STRING to a supplied string. If they match return 1. If + * cmp_type > 0 only compare if string matches the type, otherwise convert it + * to UTF8. + */ + +static int do_check_string(const ASN1_STRING *a, int cmp_type, equal_fn equal, + unsigned int flags, const char *b, size_t blen, + char **peername) +{ + int rv = 0; + + if (!a->data || !a->length) + return 0; + if (cmp_type > 0) { + if (cmp_type != a->type) + return 0; + if (cmp_type == V_ASN1_IA5STRING) + rv = equal(a->data, a->length, (unsigned char *)b, blen, flags); + else if (a->length == (int)blen && !memcmp(a->data, b, blen)) + rv = 1; + if (rv > 0 && peername) + *peername = OPENSSL_strndup((char *)a->data, a->length); + } else { + int astrlen; + unsigned char *astr; + astrlen = ASN1_STRING_to_UTF8(&astr, a); + if (astrlen < 0) { + /* + * -1 could be an internal malloc failure or a decoding error from + * malformed input; we can't distinguish. + */ + return -1; + } + rv = equal(astr, astrlen, (unsigned char *)b, blen, flags); + if (rv > 0 && peername) + *peername = OPENSSL_strndup((char *)astr, astrlen); + OPENSSL_free(astr); + } + return rv; +} + +static int do_x509_check(X509 *x, const char *chk, size_t chklen, + unsigned int flags, int check_type, char **peername) +{ + GENERAL_NAMES *gens = NULL; + X509_NAME *name = NULL; + int i; + int cnid = NID_undef; + int alt_type; + int san_present = 0; + int rv = 0; + equal_fn equal; + + /* See below, this flag is internal-only */ + flags &= ~_X509_CHECK_FLAG_DOT_SUBDOMAINS; + if (check_type == GEN_EMAIL) { + cnid = NID_pkcs9_emailAddress; + alt_type = V_ASN1_IA5STRING; + equal = equal_email; + } else if (check_type == GEN_DNS) { + cnid = NID_commonName; + /* Implicit client-side DNS sub-domain pattern */ + if (chklen > 1 && chk[0] == '.') + flags |= _X509_CHECK_FLAG_DOT_SUBDOMAINS; + alt_type = V_ASN1_IA5STRING; + if (flags & X509_CHECK_FLAG_NO_WILDCARDS) + equal = equal_nocase; + else + equal = equal_wildcard; + } else { + alt_type = V_ASN1_OCTET_STRING; + equal = equal_case; + } + + if (chklen == 0) + chklen = strlen(chk); + + gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + if (gens) { + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + GENERAL_NAME *gen; + ASN1_STRING *cstr; + gen = sk_GENERAL_NAME_value(gens, i); + if (gen->type != check_type) + continue; + san_present = 1; + if (check_type == GEN_EMAIL) + cstr = gen->d.rfc822Name; + else if (check_type == GEN_DNS) + cstr = gen->d.dNSName; + else + cstr = gen->d.iPAddress; + /* Positive on success, negative on error! */ + if ((rv = do_check_string(cstr, alt_type, equal, flags, + chk, chklen, peername)) != 0) + break; + } + GENERAL_NAMES_free(gens); + if (rv != 0) + return rv; + if (san_present && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)) + return 0; + } + + /* We're done if CN-ID is not pertinent */ + if (cnid == NID_undef || (flags & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT)) + return 0; + + i = -1; + name = X509_get_subject_name(x); + while ((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) { + const X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i); + const ASN1_STRING *str = X509_NAME_ENTRY_get_data(ne); + + /* Positive on success, negative on error! */ + if ((rv = do_check_string(str, -1, equal, flags, + chk, chklen, peername)) != 0) + return rv; + } + return 0; +} + +int X509_check_host(X509 *x, const char *chk, size_t chklen, + unsigned int flags, char **peername) +{ + if (chk == NULL) + return -2; + /* + * Embedded NULs are disallowed, except as the last character of a + * string of length 2 or more (tolerate caller including terminating + * NUL in string length). + */ + if (chklen == 0) + chklen = strlen(chk); + else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen)) + return -2; + if (chklen > 1 && chk[chklen - 1] == '\0') + --chklen; + return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername); +} + +int X509_check_email(X509 *x, const char *chk, size_t chklen, + unsigned int flags) +{ + if (chk == NULL) + return -2; + /* + * Embedded NULs are disallowed, except as the last character of a + * string of length 2 or more (tolerate caller including terminating + * NUL in string length). + */ + if (chklen == 0) + chklen = strlen((char *)chk); + else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen)) + return -2; + if (chklen > 1 && chk[chklen - 1] == '\0') + --chklen; + return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL); +} + +int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags) +{ + if (chk == NULL) + return -2; + return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, NULL); +} + +int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) +{ + unsigned char ipout[16]; + size_t iplen; + + if (ipasc == NULL) + return -2; + iplen = (size_t)a2i_ipadd(ipout, ipasc); + if (iplen == 0) + return -2; + return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL); +} + +/* + * Convert IP addresses both IPv4 and IPv6 into an OCTET STRING compatible + * with RFC3280. + */ + +ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc) +{ + unsigned char ipout[16]; + ASN1_OCTET_STRING *ret; + int iplen; + + /* If string contains a ':' assume IPv6 */ + + iplen = a2i_ipadd(ipout, ipasc); + + if (!iplen) + return NULL; + + ret = ASN1_OCTET_STRING_new(); + if (ret == NULL) + return NULL; + if (!ASN1_OCTET_STRING_set(ret, ipout, iplen)) { + ASN1_OCTET_STRING_free(ret); + return NULL; + } + return ret; +} + +ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc) +{ + ASN1_OCTET_STRING *ret = NULL; + unsigned char ipout[32]; + char *iptmp = NULL, *p; + int iplen1, iplen2; + p = strchr(ipasc, '/'); + if (!p) + return NULL; + iptmp = OPENSSL_strdup(ipasc); + if (!iptmp) + return NULL; + p = iptmp + (p - ipasc); + *p++ = 0; + + iplen1 = a2i_ipadd(ipout, iptmp); + + if (!iplen1) + goto err; + + iplen2 = a2i_ipadd(ipout + iplen1, p); + + OPENSSL_free(iptmp); + iptmp = NULL; + + if (!iplen2 || (iplen1 != iplen2)) + goto err; + + ret = ASN1_OCTET_STRING_new(); + if (ret == NULL) + goto err; + if (!ASN1_OCTET_STRING_set(ret, ipout, iplen1 + iplen2)) + goto err; + + return ret; + + err: + OPENSSL_free(iptmp); + ASN1_OCTET_STRING_free(ret); + return NULL; +} + +int a2i_ipadd(unsigned char *ipout, const char *ipasc) +{ + /* If string contains a ':' assume IPv6 */ + + if (strchr(ipasc, ':')) { + if (!ipv6_from_asc(ipout, ipasc)) + return 0; + return 16; + } else { + if (!ipv4_from_asc(ipout, ipasc)) + return 0; + return 4; + } +} + +static int ipv4_from_asc(unsigned char *v4, const char *in) +{ + int a0, a1, a2, a3; + if (sscanf(in, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) != 4) + return 0; + if ((a0 < 0) || (a0 > 255) || (a1 < 0) || (a1 > 255) + || (a2 < 0) || (a2 > 255) || (a3 < 0) || (a3 > 255)) + return 0; + v4[0] = a0; + v4[1] = a1; + v4[2] = a2; + v4[3] = a3; + return 1; +} + +typedef struct { + /* Temporary store for IPV6 output */ + unsigned char tmp[16]; + /* Total number of bytes in tmp */ + int total; + /* The position of a zero (corresponding to '::') */ + int zero_pos; + /* Number of zeroes */ + int zero_cnt; +} IPV6_STAT; + +static int ipv6_from_asc(unsigned char *v6, const char *in) +{ + IPV6_STAT v6stat; + v6stat.total = 0; + v6stat.zero_pos = -1; + v6stat.zero_cnt = 0; + /* + * Treat the IPv6 representation as a list of values separated by ':'. + * The presence of a '::' will parse as one, two or three zero length + * elements. + */ + if (!CONF_parse_list(in, ':', 0, ipv6_cb, &v6stat)) + return 0; + + /* Now for some sanity checks */ + + if (v6stat.zero_pos == -1) { + /* If no '::' must have exactly 16 bytes */ + if (v6stat.total != 16) + return 0; + } else { + /* If '::' must have less than 16 bytes */ + if (v6stat.total == 16) + return 0; + /* More than three zeroes is an error */ + if (v6stat.zero_cnt > 3) + return 0; + /* Can only have three zeroes if nothing else present */ + else if (v6stat.zero_cnt == 3) { + if (v6stat.total > 0) + return 0; + } + /* Can only have two zeroes if at start or end */ + else if (v6stat.zero_cnt == 2) { + if ((v6stat.zero_pos != 0) + && (v6stat.zero_pos != v6stat.total)) + return 0; + } else + /* Can only have one zero if *not* start or end */ + { + if ((v6stat.zero_pos == 0) + || (v6stat.zero_pos == v6stat.total)) + return 0; + } + } + + /* Format result */ + + if (v6stat.zero_pos >= 0) { + /* Copy initial part */ + memcpy(v6, v6stat.tmp, v6stat.zero_pos); + /* Zero middle */ + memset(v6 + v6stat.zero_pos, 0, 16 - v6stat.total); + /* Copy final part */ + if (v6stat.total != v6stat.zero_pos) + memcpy(v6 + v6stat.zero_pos + 16 - v6stat.total, + v6stat.tmp + v6stat.zero_pos, + v6stat.total - v6stat.zero_pos); + } else + memcpy(v6, v6stat.tmp, 16); + + return 1; +} + +static int ipv6_cb(const char *elem, int len, void *usr) +{ + IPV6_STAT *s = usr; + /* Error if 16 bytes written */ + if (s->total == 16) + return 0; + if (len == 0) { + /* Zero length element, corresponds to '::' */ + if (s->zero_pos == -1) + s->zero_pos = s->total; + /* If we've already got a :: its an error */ + else if (s->zero_pos != s->total) + return 0; + s->zero_cnt++; + } else { + /* If more than 4 characters could be final a.b.c.d form */ + if (len > 4) { + /* Need at least 4 bytes left */ + if (s->total > 12) + return 0; + /* Must be end of string */ + if (elem[len]) + return 0; + if (!ipv4_from_asc(s->tmp + s->total, elem)) + return 0; + s->total += 4; + } else { + if (!ipv6_hex(s->tmp + s->total, elem, len)) + return 0; + s->total += 2; + } + } + return 1; +} + +/* + * Convert a string of up to 4 hex digits into the corresponding IPv6 form. + */ + +static int ipv6_hex(unsigned char *out, const char *in, int inlen) +{ + unsigned char c; + unsigned int num = 0; + int x; + + if (inlen > 4) + return 0; + while (inlen--) { + c = *in++; + num <<= 4; + x = OPENSSL_hexchar2int(c); + if (x < 0) + return 0; + num |= (char)x; + } + out[0] = num >> 8; + out[1] = num & 0xff; + return 1; +} + +int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE) *dn_sk, + unsigned long chtype) +{ + CONF_VALUE *v; + int i, mval, spec_char, plus_char; + char *p, *type; + if (!nm) + return 0; + + for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) { + v = sk_CONF_VALUE_value(dn_sk, i); + type = v->name; + /* + * Skip past any leading X. X: X, etc to allow for multiple instances + */ + for (p = type; *p; p++) { +#ifndef CHARSET_EBCDIC + spec_char = ((*p == ':') || (*p == ',') || (*p == '.')); +#else + spec_char = ((*p == os_toascii[':']) || (*p == os_toascii[',']) + || (*p == os_toascii['.'])); +#endif + if (spec_char) { + p++; + if (*p) + type = p; + break; + } + } +#ifndef CHARSET_EBCDIC + plus_char = (*type == '+'); +#else + plus_char = (*type == os_toascii['+']); +#endif + if (plus_char) { + mval = -1; + type++; + } else + mval = 0; + if (!X509_NAME_add_entry_by_txt(nm, type, chtype, + (unsigned char *)v->value, -1, -1, + mval)) + return 0; + + } + return 1; +} diff --git a/crypto/x509/v3err.c b/crypto/x509/v3err.c new file mode 100644 index 0000000000..421d9366e8 --- /dev/null +++ b/crypto/x509/v3err.c @@ -0,0 +1,257 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/err.h> +#include <openssl/x509v3err.h> + +#ifndef OPENSSL_NO_ERR + +static const ERR_STRING_DATA X509V3_str_functs[] = { + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_A2I_GENERAL_NAME, 0), + "a2i_GENERAL_NAME"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_ADDR_VALIDATE_PATH_INTERNAL, 0), + "addr_validate_path_internal"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_ASIDENTIFIERCHOICE_CANONIZE, 0), + "ASIdentifierChoice_canonize"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_ASIDENTIFIERCHOICE_IS_CANONICAL, 0), + "ASIdentifierChoice_is_canonical"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_BIGNUM_TO_STRING, 0), + "bignum_to_string"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_COPY_EMAIL, 0), "copy_email"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_COPY_ISSUER, 0), "copy_issuer"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_DO_DIRNAME, 0), "do_dirname"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_DO_EXT_I2D, 0), "do_ext_i2d"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_DO_EXT_NCONF, 0), "do_ext_nconf"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_GNAMES_FROM_SECTNAME, 0), + "gnames_from_sectname"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_I2S_ASN1_ENUMERATED, 0), + "i2s_ASN1_ENUMERATED"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_I2S_ASN1_IA5STRING, 0), + "i2s_ASN1_IA5STRING"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_I2S_ASN1_INTEGER, 0), + "i2s_ASN1_INTEGER"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_I2V_AUTHORITY_INFO_ACCESS, 0), + "i2v_AUTHORITY_INFO_ACCESS"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_LEVEL_ADD_NODE, 0), "level_add_node"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_NOTICE_SECTION, 0), "notice_section"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_NREF_NOS, 0), "nref_nos"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_POLICY_CACHE_CREATE, 0), + "policy_cache_create"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_POLICY_CACHE_NEW, 0), + "policy_cache_new"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_POLICY_DATA_NEW, 0), "policy_data_new"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_POLICY_SECTION, 0), "policy_section"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_PROCESS_PCI_VALUE, 0), + "process_pci_value"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_R2I_CERTPOL, 0), "r2i_certpol"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_R2I_PCI, 0), "r2i_pci"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_S2I_ASN1_IA5STRING, 0), + "s2i_ASN1_IA5STRING"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_S2I_ASN1_INTEGER, 0), + "s2i_ASN1_INTEGER"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_S2I_ASN1_OCTET_STRING, 0), + "s2i_ASN1_OCTET_STRING"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_S2I_SKEY_ID, 0), "s2i_skey_id"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SET_DIST_POINT_NAME, 0), + "set_dist_point_name"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_ADD_ID_ASC, 0), + "SXNET_add_id_asc"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_ADD_ID_INTEGER, 0), + "SXNET_add_id_INTEGER"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_ADD_ID_ULONG, 0), + "SXNET_add_id_ulong"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_GET_ID_ASC, 0), + "SXNET_get_id_asc"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_SXNET_GET_ID_ULONG, 0), + "SXNET_get_id_ulong"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_TREE_INIT, 0), "tree_init"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_ASIDENTIFIERS, 0), + "v2i_ASIdentifiers"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_ASN1_BIT_STRING, 0), + "v2i_ASN1_BIT_STRING"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_AUTHORITY_INFO_ACCESS, 0), + "v2i_AUTHORITY_INFO_ACCESS"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_AUTHORITY_KEYID, 0), + "v2i_AUTHORITY_KEYID"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_BASIC_CONSTRAINTS, 0), + "v2i_BASIC_CONSTRAINTS"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_CRLD, 0), "v2i_crld"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_EXTENDED_KEY_USAGE, 0), + "v2i_EXTENDED_KEY_USAGE"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_GENERAL_NAMES, 0), + "v2i_GENERAL_NAMES"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_GENERAL_NAME_EX, 0), + "v2i_GENERAL_NAME_ex"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_IDP, 0), "v2i_idp"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_IPADDRBLOCKS, 0), + "v2i_IPAddrBlocks"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_ISSUER_ALT, 0), "v2i_issuer_alt"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_NAME_CONSTRAINTS, 0), + "v2i_NAME_CONSTRAINTS"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_POLICY_CONSTRAINTS, 0), + "v2i_POLICY_CONSTRAINTS"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_POLICY_MAPPINGS, 0), + "v2i_POLICY_MAPPINGS"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_SUBJECT_ALT, 0), "v2i_subject_alt"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V2I_TLS_FEATURE, 0), "v2i_TLS_FEATURE"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_V3_GENERIC_EXTENSION, 0), + "v3_generic_extension"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_ADD1_I2D, 0), "X509V3_add1_i2d"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_ADD_VALUE, 0), + "X509V3_add_value"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_ADD, 0), "X509V3_EXT_add"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_ADD_ALIAS, 0), + "X509V3_EXT_add_alias"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_I2D, 0), "X509V3_EXT_i2d"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_EXT_NCONF, 0), + "X509V3_EXT_nconf"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_GET_SECTION, 0), + "X509V3_get_section"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_GET_STRING, 0), + "X509V3_get_string"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_GET_VALUE_BOOL, 0), + "X509V3_get_value_bool"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509V3_PARSE_LIST, 0), + "X509V3_parse_list"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509_PURPOSE_ADD, 0), + "X509_PURPOSE_add"}, + {ERR_PACK(ERR_LIB_X509V3, X509V3_F_X509_PURPOSE_SET, 0), + "X509_PURPOSE_set"}, + {0, NULL} +}; + +static const ERR_STRING_DATA X509V3_str_reasons[] = { + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BAD_IP_ADDRESS), "bad ip address"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BAD_OBJECT), "bad object"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BN_DEC2BN_ERROR), "bn dec2bn error"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BN_TO_ASN1_INTEGER_ERROR), + "bn to asn1 integer error"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DIRNAME_ERROR), "dirname error"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DISTPOINT_ALREADY_SET), + "distpoint already set"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DUPLICATE_ZONE_ID), + "duplicate zone id"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_CONVERTING_ZONE), + "error converting zone"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_CREATING_EXTENSION), + "error creating extension"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_IN_EXTENSION), + "error in extension"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXPECTED_A_SECTION_NAME), + "expected a section name"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_EXISTS), + "extension exists"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_NAME_ERROR), + "extension name error"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_NOT_FOUND), + "extension not found"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED), + "extension setting not supported"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_VALUE_ERROR), + "extension value error"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ILLEGAL_EMPTY_EXTENSION), + "illegal empty extension"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG), + "incorrect policy syntax tag"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_ASNUMBER), + "invalid asnumber"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_ASRANGE), "invalid asrange"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_BOOLEAN_STRING), + "invalid boolean string"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_EXTENSION_STRING), + "invalid extension string"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_INHERITANCE), + "invalid inheritance"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_IPADDRESS), + "invalid ipaddress"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_MULTIPLE_RDNS), + "invalid multiple rdns"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NAME), "invalid name"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_ARGUMENT), + "invalid null argument"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_NAME), + "invalid null name"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_VALUE), + "invalid null value"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NUMBER), "invalid number"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NUMBERS), "invalid numbers"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_OBJECT_IDENTIFIER), + "invalid object identifier"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_OPTION), "invalid option"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_POLICY_IDENTIFIER), + "invalid policy identifier"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_PROXY_POLICY_SETTING), + "invalid proxy policy setting"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_PURPOSE), "invalid purpose"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SAFI), "invalid safi"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SECTION), "invalid section"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SYNTAX), "invalid syntax"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ISSUER_DECODE_ERROR), + "issuer decode error"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_MISSING_VALUE), "missing value"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS), + "need organization and numbers"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_CONFIG_DATABASE), + "no config database"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_ISSUER_CERTIFICATE), + "no issuer certificate"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_ISSUER_DETAILS), + "no issuer details"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_POLICY_IDENTIFIER), + "no policy identifier"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED), + "no proxy cert policy language defined"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_PUBLIC_KEY), "no public key"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_SUBJECT_DETAILS), + "no subject details"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_OPERATION_NOT_DEFINED), + "operation not defined"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_OTHERNAME_ERROR), "othername error"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED), + "policy language already defined"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_PATH_LENGTH), + "policy path length"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED), + "policy path length already defined"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY), + "policy when proxy language requires no policy"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_SECTION_NOT_FOUND), + "section not found"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS), + "unable to get issuer details"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID), + "unable to get issuer keyid"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT), + "unknown bit string argument"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_EXTENSION), + "unknown extension"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_EXTENSION_NAME), + "unknown extension name"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_OPTION), "unknown option"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNSUPPORTED_OPTION), + "unsupported option"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNSUPPORTED_TYPE), + "unsupported type"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_USER_TOO_LONG), "user too long"}, + {0, NULL} +}; + +#endif + +int ERR_load_X509V3_strings(void) +{ +#ifndef OPENSSL_NO_ERR + if (ERR_func_error_string(X509V3_str_functs[0].error) == NULL) { + ERR_load_strings_const(X509V3_str_functs); + ERR_load_strings_const(X509V3_str_reasons); + } +#endif + return 1; +} |