diff options
Diffstat (limited to 'crypto/x509')
82 files changed, 16883 insertions, 1760 deletions
diff --git a/crypto/x509/build.info b/crypto/x509/build.info index afd0b6134e52..8820f983bb3d 100644 --- a/crypto/x509/build.info +++ b/crypto/x509/build.info @@ -4,7 +4,18 @@ SOURCE[../../libcrypto]=\ x509_obj.c x509_req.c x509spki.c x509_vfy.c \ x509_set.c x509cset.c x509rset.c x509_err.c \ x509name.c x509_v3.c x509_ext.c x509_att.c \ - 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 \ + x509_meth.c x509_lu.c x_all.c x509_txt.c \ + x509_trust.c by_file.c by_dir.c by_store.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_utf8.c v3_lib.c \ + v3_prn.c v3_utl.c v3err.c v3_genn.c v3_san.c v3_skid.c v3_akid.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 v3_ist.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 + +IF[{- !$disabled{'deprecated-3.0'} -}] + SOURCE[../../libcrypto]=x509type.c +ENDIF diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c index 46a861e90de0..ad871966aa6e 100644 --- a/crypto/x509/by_dir.c +++ b/crypto/x509/by_dir.c @@ -1,12 +1,21 @@ /* * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 */ +#if defined (__TANDEM) && defined (_SPT_MODEL_) + /* + * These definitions have to come first in SPT due to scoping of the + * declarations in c99 associated with SPT use of stat. + */ +# include <sys/types.h> +# include <sys/stat.h> +#endif + #include "e_os.h" #include "internal/cryptlib.h" #include <stdio.h> @@ -40,23 +49,29 @@ typedef struct lookup_dir_st { } BY_DIR; static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, - char **ret); + char **retp); + static int new_dir(X509_LOOKUP *lu); static void free_dir(X509_LOOKUP *lu); static int add_cert_dir(BY_DIR *ctx, const char *dir, int type); static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, - X509_NAME *name, X509_OBJECT *ret); + const X509_NAME *name, X509_OBJECT *ret); +static int get_cert_by_subject_ex(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, + const X509_NAME *name, X509_OBJECT *ret, + OSSL_LIB_CTX *libctx, const char *propq); static X509_LOOKUP_METHOD x509_dir_lookup = { "Load certs from files in a directory", - new_dir, /* new_item */ - free_dir, /* free */ - NULL, /* init */ - NULL, /* shutdown */ - dir_ctrl, /* ctrl */ - get_cert_by_subject, /* get_by_subject */ - NULL, /* get_by_issuer_serial */ - NULL, /* get_by_fingerprint */ - NULL, /* get_by_alias */ + new_dir, /* new_item */ + free_dir, /* free */ + NULL, /* init */ + NULL, /* shutdown */ + dir_ctrl, /* ctrl */ + get_cert_by_subject, /* get_by_subject */ + NULL, /* get_by_issuer_serial */ + NULL, /* get_by_fingerprint */ + NULL, /* get_by_alias */ + get_cert_by_subject_ex, /* get_by_subject_ex */ + NULL, /* ctrl_ex */ }; X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) @@ -81,7 +96,7 @@ static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl, ret = add_cert_dir(ld, X509_get_default_cert_dir(), X509_FILETYPE_PEM); if (!ret) { - X509err(X509_F_DIR_CTRL, X509_R_LOADING_CERT_DIR); + ERR_raise(ERR_LIB_X509, X509_R_LOADING_CERT_DIR); } } else ret = add_cert_dir(ld, argp, (int)argl); @@ -95,19 +110,19 @@ static int new_dir(X509_LOOKUP *lu) BY_DIR *a = OPENSSL_malloc(sizeof(*a)); if (a == NULL) { - X509err(X509_F_NEW_DIR, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return 0; } if ((a->buffer = BUF_MEM_new()) == NULL) { - X509err(X509_F_NEW_DIR, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } a->dirs = NULL; a->lock = CRYPTO_THREAD_lock_new(); if (a->lock == NULL) { BUF_MEM_free(a->buffer); - X509err(X509_F_NEW_DIR, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } lu->method_data = a; @@ -156,8 +171,8 @@ static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) size_t len; const char *s, *ss, *p; - if (dir == NULL || !*dir) { - X509err(X509_F_ADD_CERT_DIR, X509_R_INVALID_DIRECTORY); + if (dir == NULL || *dir == '\0') { + ERR_raise(ERR_LIB_X509, X509_R_INVALID_DIRECTORY); return 0; } @@ -182,13 +197,13 @@ static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) if (ctx->dirs == NULL) { ctx->dirs = sk_BY_DIR_ENTRY_new_null(); if (!ctx->dirs) { - X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return 0; } } ent = OPENSSL_malloc(sizeof(*ent)); if (ent == NULL) { - X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return 0; } ent->dir_type = type; @@ -200,7 +215,7 @@ static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) } if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) { by_dir_entry_free(ent); - X509err(X509_F_ADD_CERT_DIR, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return 0; } } @@ -208,8 +223,9 @@ static int add_cert_dir(BY_DIR *ctx, const char *dir, int type) return 1; } -static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, - X509_NAME *name, X509_OBJECT *ret) +static int get_cert_by_subject_ex(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, + const X509_NAME *name, X509_OBJECT *ret, + OSSL_LIB_CTX *libctx, const char *propq) { BY_DIR *ctx; union { @@ -228,26 +244,26 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, stmp.type = type; if (type == X509_LU_X509) { - data.st_x509.cert_info.subject = name; + data.st_x509.cert_info.subject = (X509_NAME *)name; /* won't modify it */ stmp.data.x509 = &data.st_x509; - postfix = ""; } else if (type == X509_LU_CRL) { - data.crl.crl.issuer = name; + data.crl.crl.issuer = (X509_NAME *)name; /* won't modify it */ stmp.data.crl = &data.crl; postfix = "r"; } else { - X509err(X509_F_GET_CERT_BY_SUBJECT, X509_R_WRONG_LOOKUP_TYPE); + ERR_raise(ERR_LIB_X509, X509_R_WRONG_LOOKUP_TYPE); goto finish; } if ((b = BUF_MEM_new()) == NULL) { - X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); goto finish; } ctx = (BY_DIR *)xl->method_data; - - h = X509_NAME_hash(name); + h = X509_NAME_hash_ex(name, libctx, propq, &i); + if (i == 0) + goto finish; for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) { BY_DIR_ENTRY *ent; int idx; @@ -256,12 +272,13 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i); j = strlen(ent->dir) + 1 + 8 + 6 + 1 + 1; if (!BUF_MEM_grow(b, j)) { - X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto finish; } if (type == X509_LU_CRL && ent->hashes) { htmp.hash = h; - CRYPTO_THREAD_read_lock(ctx->lock); + if (!CRYPTO_THREAD_read_lock(ctx->lock)) + goto finish; idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp); if (idx >= 0) { hent = sk_BY_DIR_HASH_value(ent->hashes, idx); @@ -277,6 +294,7 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, } for (;;) { char c = '/'; + #ifdef OPENSSL_SYS_VMS c = ent->dir[strlen(ent->dir) - 1]; if (c != ':' && c != '>' && c != ']') { @@ -290,7 +308,7 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, } else { c = '\0'; } -#endif + if (c == '\0') { /* * This is special. When c == '\0', no directory separator @@ -298,7 +316,9 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, */ BIO_snprintf(b->data, b->max, "%s%08lx.%s%d", ent->dir, h, postfix, k); - } else { + } else +#endif + { BIO_snprintf(b->data, b->max, "%s%c%08lx.%s%d", ent->dir, c, h, postfix, k); } @@ -314,7 +334,8 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, #endif /* found one. */ if (type == X509_LU_X509) { - if ((X509_load_cert_file(xl, b->data, ent->dir_type)) == 0) + if ((X509_load_cert_file_ex(xl, b->data, ent->dir_type, libctx, + propq)) == 0) break; } else if (type == X509_LU_CRL) { if ((X509_load_crl_file(xl, b->data, ent->dir_type)) == 0) @@ -327,7 +348,8 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, /* * we have added it to the cache so now pull it out again */ - X509_STORE_lock(xl->store_ctx); + if (!X509_STORE_lock(xl->store_ctx)) + goto finish; j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp); tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j); X509_STORE_unlock(xl->store_ctx); @@ -339,7 +361,8 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, * simple case where no CRL is present for a hash. */ if (type == X509_LU_CRL && k > 0) { - CRYPTO_THREAD_write_lock(ctx->lock); + if (!CRYPTO_THREAD_write_lock(ctx->lock)) + goto finish; /* * Look for entry again in case another thread added an entry * first. @@ -353,7 +376,7 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, hent = OPENSSL_malloc(sizeof(*hent)); if (hent == NULL) { CRYPTO_THREAD_unlock(ctx->lock); - X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); ok = 0; goto finish; } @@ -362,7 +385,7 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) { CRYPTO_THREAD_unlock(ctx->lock); OPENSSL_free(hent); - X509err(X509_F_GET_CERT_BY_SUBJECT, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); ok = 0; goto finish; } @@ -398,3 +421,9 @@ static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, BUF_MEM_free(b); return ok; } + +static int get_cert_by_subject(X509_LOOKUP *xl, X509_LOOKUP_TYPE type, + const X509_NAME *name, X509_OBJECT *ret) +{ + return get_cert_by_subject_ex(xl, type, name, ret, NULL, NULL); +} diff --git a/crypto/x509/by_file.c b/crypto/x509/by_file.c index 237b362e2746..37d73ca84c54 100644 --- a/crypto/x509/by_file.c +++ b/crypto/x509/by_file.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -19,6 +19,11 @@ static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret); +static int by_file_ctrl_ex(X509_LOOKUP *ctx, int cmd, const char *argc, + long argl, char **ret, OSSL_LIB_CTX *libctx, + const char *propq); + + static X509_LOOKUP_METHOD x509_file_lookup = { "Load file into cache", NULL, /* new_item */ @@ -30,6 +35,8 @@ static X509_LOOKUP_METHOD x509_file_lookup = { NULL, /* get_by_issuer_serial */ NULL, /* get_by_fingerprint */ NULL, /* get_by_alias */ + NULL, /* get_by_subject_ex */ + by_file_ctrl_ex, /* ctrl_ex */ }; X509_LOOKUP_METHOD *X509_LOOKUP_file(void) @@ -37,8 +44,9 @@ X509_LOOKUP_METHOD *X509_LOOKUP_file(void) return &x509_file_lookup; } -static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, - long argl, char **ret) +static int by_file_ctrl_ex(X509_LOOKUP *ctx, int cmd, const char *argp, + long argl, char **ret, OSSL_LIB_CTX *libctx, + const char *propq) { int ok = 0; const char *file; @@ -48,30 +56,38 @@ static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, if (argl == X509_FILETYPE_DEFAULT) { file = ossl_safe_getenv(X509_get_default_cert_file_env()); if (file) - ok = (X509_load_cert_crl_file(ctx, file, - X509_FILETYPE_PEM) != 0); + ok = (X509_load_cert_crl_file_ex(ctx, file, X509_FILETYPE_PEM, + libctx, propq) != 0); else - ok = (X509_load_cert_crl_file - (ctx, X509_get_default_cert_file(), - X509_FILETYPE_PEM) != 0); + ok = (X509_load_cert_crl_file_ex( + ctx, X509_get_default_cert_file(), + X509_FILETYPE_PEM, libctx, propq) != 0); if (!ok) { - X509err(X509_F_BY_FILE_CTRL, X509_R_LOADING_DEFAULTS); + ERR_raise(ERR_LIB_X509, X509_R_LOADING_DEFAULTS); } } else { if (argl == X509_FILETYPE_PEM) - ok = (X509_load_cert_crl_file(ctx, argp, - X509_FILETYPE_PEM) != 0); + ok = (X509_load_cert_crl_file_ex(ctx, argp, X509_FILETYPE_PEM, + libctx, propq) != 0); else - ok = (X509_load_cert_file(ctx, argp, (int)argl) != 0); + ok = (X509_load_cert_file_ex(ctx, argp, (int)argl, libctx, + propq) != 0); } break; } return ok; } -int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type) +static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, + const char *argp, long argl, char **ret) +{ + return by_file_ctrl_ex(ctx, cmd, argp, argl, ret, NULL, NULL); +} + +int X509_load_cert_file_ex(X509_LOOKUP *ctx, const char *file, int type, + OSSL_LIB_CTX *libctx, const char *propq) { int ret = 0; BIO *in = NULL; @@ -81,23 +97,34 @@ int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type) in = BIO_new(BIO_s_file()); if ((in == NULL) || (BIO_read_filename(in, file) <= 0)) { - X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_SYS_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_SYS_LIB); + goto err; + } + + if (type != X509_FILETYPE_PEM && type != X509_FILETYPE_ASN1) { + ERR_raise(ERR_LIB_X509, X509_R_BAD_X509_FILETYPE); + goto err; + } + x = X509_new_ex(libctx, propq); + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } if (type == X509_FILETYPE_PEM) { for (;;) { - x = PEM_read_bio_X509_AUX(in, NULL, NULL, ""); - if (x == NULL) { + ERR_set_mark(); + if (PEM_read_bio_X509_AUX(in, &x, NULL, "") == NULL) { if ((ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) && (count > 0)) { - ERR_clear_error(); + ERR_pop_to_mark(); break; } else { - X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_PEM_LIB); + ERR_clear_last_mark(); goto err; } } + ERR_clear_last_mark(); i = X509_STORE_add_cert(ctx->store_ctx, x); if (!i) goto err; @@ -107,27 +134,28 @@ int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type) } ret = count; } else if (type == X509_FILETYPE_ASN1) { - x = d2i_X509_bio(in, NULL); - if (x == NULL) { - X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_ASN1_LIB); + if (d2i_X509_bio(in, &x) == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); goto err; } i = X509_STORE_add_cert(ctx->store_ctx, x); if (!i) goto err; ret = i; - } else { - X509err(X509_F_X509_LOAD_CERT_FILE, X509_R_BAD_X509_FILETYPE); - goto err; } if (ret == 0) - X509err(X509_F_X509_LOAD_CERT_FILE, X509_R_NO_CERTIFICATE_FOUND); + ERR_raise(ERR_LIB_X509, X509_R_NO_CERTIFICATE_FOUND); err: X509_free(x); BIO_free(in); return ret; } +int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type) +{ + return X509_load_cert_file_ex(ctx, file, type, NULL, NULL); +} + int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type) { int ret = 0; @@ -138,7 +166,7 @@ int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type) in = BIO_new(BIO_s_file()); if ((in == NULL) || (BIO_read_filename(in, file) <= 0)) { - X509err(X509_F_X509_LOAD_CRL_FILE, ERR_R_SYS_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_SYS_LIB); goto err; } @@ -151,7 +179,7 @@ int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type) ERR_clear_error(); break; } else { - X509err(X509_F_X509_LOAD_CRL_FILE, ERR_R_PEM_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_PEM_LIB); goto err; } } @@ -166,7 +194,7 @@ int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type) } else if (type == X509_FILETYPE_ASN1) { x = d2i_X509_CRL_bio(in, NULL); if (x == NULL) { - X509err(X509_F_X509_LOAD_CRL_FILE, ERR_R_ASN1_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); goto err; } i = X509_STORE_add_crl(ctx->store_ctx, x); @@ -174,18 +202,19 @@ int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type) goto err; ret = i; } else { - X509err(X509_F_X509_LOAD_CRL_FILE, X509_R_BAD_X509_FILETYPE); + ERR_raise(ERR_LIB_X509, X509_R_BAD_X509_FILETYPE); goto err; } if (ret == 0) - X509err(X509_F_X509_LOAD_CRL_FILE, X509_R_NO_CRL_FOUND); + ERR_raise(ERR_LIB_X509, X509_R_NO_CRL_FOUND); err: X509_CRL_free(x); BIO_free(in); return ret; } -int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type) +int X509_load_cert_crl_file_ex(X509_LOOKUP *ctx, const char *file, int type, + OSSL_LIB_CTX *libctx, const char *propq) { STACK_OF(X509_INFO) *inf; X509_INFO *itmp; @@ -193,16 +222,16 @@ int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type) int i, count = 0; if (type != X509_FILETYPE_PEM) - return X509_load_cert_file(ctx, file, type); + return X509_load_cert_file_ex(ctx, file, type, libctx, propq); in = BIO_new_file(file, "r"); if (!in) { - X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_SYS_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_SYS_LIB); return 0; } - inf = PEM_X509_INFO_read_bio(in, NULL, NULL, ""); + inf = PEM_X509_INFO_read_bio_ex(in, NULL, NULL, "", libctx, propq); BIO_free(in); if (!inf) { - X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_PEM_LIB); return 0; } for (i = 0; i < sk_X509_INFO_num(inf); i++) { @@ -219,9 +248,14 @@ int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type) } } if (count == 0) - X509err(X509_F_X509_LOAD_CERT_CRL_FILE, - X509_R_NO_CERTIFICATE_OR_CRL_FOUND); + ERR_raise(ERR_LIB_X509, X509_R_NO_CERTIFICATE_OR_CRL_FOUND); err: sk_X509_INFO_pop_free(inf, X509_INFO_free); return count; } + +int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type) +{ + return X509_load_cert_crl_file_ex(ctx, file, type, NULL, NULL); +} + diff --git a/crypto/x509/by_store.c b/crypto/x509/by_store.c new file mode 100644 index 000000000000..e486fb0a9d94 --- /dev/null +++ b/crypto/x509/by_store.c @@ -0,0 +1,298 @@ +/* + * Copyright 2018-2025 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/safestack.h> +#include <openssl/store.h> +#include "internal/cryptlib.h" +#include "crypto/x509.h" +#include "x509_local.h" + +typedef struct cached_store_st { + char *uri; + OSSL_LIB_CTX *libctx; + char *propq; + OSSL_STORE_CTX *ctx; +} CACHED_STORE; + +DEFINE_STACK_OF(CACHED_STORE) + +/* Generic object loader, given expected type and criterion */ +static int cache_objects(X509_LOOKUP *lctx, CACHED_STORE *store, + const OSSL_STORE_SEARCH *criterion, int depth) +{ + int ok = 0; + OSSL_STORE_CTX *ctx = store->ctx; + X509_STORE *xstore = X509_LOOKUP_get_store(lctx); + + if (ctx == NULL + && (ctx = OSSL_STORE_open_ex(store->uri, store->libctx, store->propq, + NULL, NULL, NULL, NULL, NULL)) == NULL) + return 0; + store->ctx = ctx; + + /* + * We try to set the criterion, but don't care if it was valid or not. + * For a OSSL_STORE, it merely serves as an optimization, the expectation + * being that if the criterion couldn't be used, we will get *everything* + * from the container that the URI represents rather than the subset that + * the criterion indicates, so the biggest harm is that we cache more + * objects certs and CRLs than we may expect, but that's ok. + * + * Specifically for OpenSSL's own file: scheme, the only workable + * criterion is the BY_NAME one, which it can only apply on directories, + * but it's possible that the URI is a single file rather than a directory, + * and in that case, the BY_NAME criterion is pointless. + * + * We could very simply not apply any criterion at all here, and just let + * the code that selects certs and CRLs from the cached objects do its job, + * but it's a nice optimization when it can be applied (such as on an + * actual directory with a thousand CA certs). + */ + if (criterion != NULL) + OSSL_STORE_find(ctx, criterion); + + for (;;) { + OSSL_STORE_INFO *info = OSSL_STORE_load(ctx); + int infotype; + + /* NULL means error or "end of file". Either way, we break. */ + if (info == NULL) + break; + + infotype = OSSL_STORE_INFO_get_type(info); + ok = 0; + + if (infotype == OSSL_STORE_INFO_NAME) { + /* + * This is an entry in the "directory" represented by the current + * uri. if |depth| allows, dive into it. + */ + if (depth > 0) { + CACHED_STORE substore; + + substore.uri = (char *)OSSL_STORE_INFO_get0_NAME(info); + substore.libctx = store->libctx; + substore.propq = store->propq; + substore.ctx = NULL; + ok = cache_objects(lctx, &substore, criterion, depth - 1); + } + } else { + /* + * We know that X509_STORE_add_{cert|crl} increments the object's + * refcount, so we can safely use OSSL_STORE_INFO_get0_{cert,crl} + * to get them. + */ + switch (infotype) { + case OSSL_STORE_INFO_CERT: + ok = X509_STORE_add_cert(xstore, + OSSL_STORE_INFO_get0_CERT(info)); + break; + case OSSL_STORE_INFO_CRL: + ok = X509_STORE_add_crl(xstore, + OSSL_STORE_INFO_get0_CRL(info)); + break; + } + } + + OSSL_STORE_INFO_free(info); + if (!ok) + break; + } + OSSL_STORE_close(ctx); + store->ctx = NULL; + + return ok; +} + + +static void free_store(CACHED_STORE *store) +{ + if (store != NULL) { + OSSL_STORE_close(store->ctx); + OPENSSL_free(store->uri); + OPENSSL_free(store->propq); + OPENSSL_free(store); + } +} + +static void by_store_free(X509_LOOKUP *ctx) +{ + STACK_OF(CACHED_STORE) *stores = X509_LOOKUP_get_method_data(ctx); + sk_CACHED_STORE_pop_free(stores, free_store); +} + +static int by_store_ctrl_ex(X509_LOOKUP *ctx, int cmd, const char *argp, + long argl, char **retp, OSSL_LIB_CTX *libctx, + const char *propq) +{ + /* + * In some cases below, failing to use the defaults shouldn't result in + * an error. |use_default| is used as the return code in those cases. + */ + int use_default = argp == NULL; + + switch (cmd) { + case X509_L_ADD_STORE: + /* If no URI is given, use the default cert dir as default URI */ + if (argp == NULL) + argp = ossl_safe_getenv(X509_get_default_cert_dir_env()); + if (argp == NULL) + argp = X509_get_default_cert_dir(); + + { + STACK_OF(CACHED_STORE) *stores = X509_LOOKUP_get_method_data(ctx); + CACHED_STORE *store = OPENSSL_zalloc(sizeof(*store)); + + if (store == NULL) { + return 0; + } + + store->uri = OPENSSL_strdup(argp); + store->libctx = libctx; + if (propq != NULL) + store->propq = OPENSSL_strdup(propq); + store->ctx = OSSL_STORE_open_ex(argp, libctx, propq, NULL, NULL, + NULL, NULL, NULL); + if (store->ctx == NULL + || (propq != NULL && store->propq == NULL) + || store->uri == NULL) { + free_store(store); + return use_default; + } + + if (stores == NULL) { + stores = sk_CACHED_STORE_new_null(); + if (stores != NULL) + X509_LOOKUP_set_method_data(ctx, stores); + } + if (stores == NULL || sk_CACHED_STORE_push(stores, store) <= 0) { + free_store(store); + return 0; + } + return 1; + } + case X509_L_LOAD_STORE: { + /* This is a shortcut for quick loading of specific containers */ + CACHED_STORE store; + + store.uri = (char *)argp; + store.libctx = libctx; + store.propq = (char *)propq; + store.ctx = NULL; + return cache_objects(ctx, &store, NULL, 0); + } + default: + /* Unsupported command */ + return 0; + } + + return 0; +} + +static int by_store_ctrl(X509_LOOKUP *ctx, int cmd, + const char *argp, long argl, char **retp) +{ + return by_store_ctrl_ex(ctx, cmd, argp, argl, retp, NULL, NULL); +} + +static int by_store(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const OSSL_STORE_SEARCH *criterion, X509_OBJECT *ret) +{ + STACK_OF(CACHED_STORE) *stores = X509_LOOKUP_get_method_data(ctx); + int i; + int ok = 0; + + for (i = 0; i < sk_CACHED_STORE_num(stores); i++) { + ok = cache_objects(ctx, sk_CACHED_STORE_value(stores, i), criterion, + 1 /* depth */); + + if (ok) + break; + } + return ok; +} + +static int by_store_subject(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const X509_NAME *name, X509_OBJECT *ret) +{ + OSSL_STORE_SEARCH *criterion = + OSSL_STORE_SEARCH_by_name((X509_NAME *)name); /* won't modify it */ + int ok = by_store(ctx, type, criterion, ret); + STACK_OF(X509_OBJECT) *store_objects = + X509_STORE_get0_objects(X509_LOOKUP_get_store(ctx)); + X509_OBJECT *tmp = NULL; + + OSSL_STORE_SEARCH_free(criterion); + + if (ok) + tmp = X509_OBJECT_retrieve_by_subject(store_objects, type, name); + + ok = 0; + if (tmp != NULL) { + /* + * This could also be done like this: + * + * if (tmp != NULL) { + * *ret = *tmp; + * ok = 1; + * } + * + * However, we want to exercise the documented API to the max, so + * we do it the hard way. + * + * To be noted is that X509_OBJECT_set1_* increment the refcount, + * but so does X509_STORE_CTX_get_by_subject upon return of this + * function, so we must ensure the refcount is decremented + * before we return, or we will get a refcount leak. We cannot do + * this with X509_OBJECT_free(), though, as that will free a bit + * too much. + */ + switch (type) { + case X509_LU_X509: + ok = X509_OBJECT_set1_X509(ret, tmp->data.x509); + if (ok) + X509_free(tmp->data.x509); + break; + case X509_LU_CRL: + ok = X509_OBJECT_set1_X509_CRL(ret, tmp->data.crl); + if (ok) + X509_CRL_free(tmp->data.crl); + break; + case X509_LU_NONE: + break; + } + } + return ok; +} + +/* + * We lack the implementations for get_by_issuer_serial, get_by_fingerprint + * and get_by_alias. There's simply not enough support in the X509_LOOKUP + * or X509_STORE APIs. + */ + +static X509_LOOKUP_METHOD x509_store_lookup = { + "Load certs from STORE URIs", + NULL, /* new_item */ + by_store_free, /* free */ + NULL, /* init */ + NULL, /* shutdown */ + by_store_ctrl, /* ctrl */ + by_store_subject, /* get_by_subject */ + NULL, /* get_by_issuer_serial */ + NULL, /* get_by_fingerprint */ + NULL, /* get_by_alias */ + NULL, /* get_by_subject_ex */ + by_store_ctrl_ex +}; + +X509_LOOKUP_METHOD *X509_LOOKUP_store(void) +{ + return &x509_store_lookup; +} diff --git a/crypto/x509/ext_dat.h b/crypto/x509/ext_dat.h new file mode 100644 index 000000000000..a0a7f88ccd8a --- /dev/null +++ b/crypto/x509/ext_dat.h @@ -0,0 +1,27 @@ +/* + * Copyright 1999-2021 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 ossl_v3_name_cmp(const char *name, const char *cmp); + +extern const X509V3_EXT_METHOD ossl_v3_bcons, ossl_v3_nscert, ossl_v3_key_usage, ossl_v3_ext_ku; +extern const X509V3_EXT_METHOD ossl_v3_pkey_usage_period, ossl_v3_sxnet, ossl_v3_info, ossl_v3_sinfo; +extern const X509V3_EXT_METHOD ossl_v3_ns_ia5_list[8], ossl_v3_alt[3], ossl_v3_skey_id, ossl_v3_akey_id; +extern const X509V3_EXT_METHOD ossl_v3_crl_num, ossl_v3_crl_reason, ossl_v3_crl_invdate; +extern const X509V3_EXT_METHOD ossl_v3_delta_crl, ossl_v3_cpols, ossl_v3_crld, ossl_v3_freshest_crl; +extern const X509V3_EXT_METHOD ossl_v3_ocsp_nonce, ossl_v3_ocsp_accresp, ossl_v3_ocsp_acutoff; +extern const X509V3_EXT_METHOD ossl_v3_ocsp_crlid, ossl_v3_ocsp_nocheck, ossl_v3_ocsp_serviceloc; +extern const X509V3_EXT_METHOD ossl_v3_crl_hold, ossl_v3_pci; +extern const X509V3_EXT_METHOD ossl_v3_policy_mappings, ossl_v3_policy_constraints; +extern const X509V3_EXT_METHOD ossl_v3_name_constraints, ossl_v3_inhibit_anyp, ossl_v3_idp; +extern const X509V3_EXT_METHOD ossl_v3_addr, ossl_v3_asid; +extern const X509V3_EXT_METHOD ossl_v3_ct_scts[3]; +extern const X509V3_EXT_METHOD ossl_v3_tls_feature; +extern const X509V3_EXT_METHOD ossl_v3_ext_admission; +extern const X509V3_EXT_METHOD ossl_v3_utf8_list[1]; +extern const X509V3_EXT_METHOD ossl_v3_issuer_sign_tool; diff --git a/crypto/x509/pcy_cache.c b/crypto/x509/pcy_cache.c new file mode 100644 index 000000000000..1339f994aee1 --- /dev/null +++ b/crypto/x509/pcy_cache.c @@ -0,0 +1,225 @@ +/* + * Copyright 2004-2021 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 "crypto/x509.h" + +#include "pcy_local.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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto just_cleanup; + } + for (i = 0; i < num; i++) { + policy = sk_POLICYINFO_value(policies, i); + data = ossl_policy_data_new(policy, NULL, crit); + if (data == NULL) { + ERR_raise(ERR_LIB_X509V3, 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)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto bad_policy; + } + data = NULL; + } + ret = 1; + + bad_policy: + if (ret == -1) + x->ex_flags |= EXFLAG_INVALID_POLICY; + ossl_policy_data_free(data); + just_cleanup: + sk_POLICYINFO_pop_free(policies, POLICYINFO_free); + if (ret <= 0) { + sk_X509_POLICY_DATA_pop_free(cache->data, ossl_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) { + ERR_raise(ERR_LIB_X509V3, 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 = ossl_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 ossl_policy_cache_free(X509_POLICY_CACHE *cache) +{ + if (!cache) + return; + ossl_policy_data_free(cache->anyPolicy); + sk_X509_POLICY_DATA_pop_free(cache->data, ossl_policy_data_free); + OPENSSL_free(cache); +} + +const X509_POLICY_CACHE *ossl_policy_cache_set(X509 *x) +{ + + if (x->policy_cache == NULL) { + if (!CRYPTO_THREAD_write_lock(x->lock)) + return NULL; + policy_cache_new(x); + CRYPTO_THREAD_unlock(x->lock); + } + + return x->policy_cache; + +} + +X509_POLICY_DATA *ossl_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 000000000000..6fb8f14ba8e8 --- /dev/null +++ b/crypto/x509/pcy_data.c @@ -0,0 +1,82 @@ +/* + * Copyright 2004-2021 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_local.h" + +/* Policy Node routines */ + +void ossl_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 *ossl_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) { + ASN1_OBJECT_free(id); + ERR_raise(ERR_LIB_X509V3, 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); + ERR_raise(ERR_LIB_X509V3, 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_lib.c b/crypto/x509/pcy_lib.c new file mode 100644 index 000000000000..c4740a0a30c5 --- /dev/null +++ b/crypto/x509/pcy_lib.c @@ -0,0 +1,108 @@ +/* + * Copyright 2004-2020 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_local.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(const 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_local.h b/crypto/x509/pcy_local.h new file mode 100644 index 000000000000..523f3e35feeb --- /dev/null +++ b/crypto/x509/pcy_local.h @@ -0,0 +1,171 @@ +/* + * Copyright 2004-2023 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 { + /* The number of nodes in the tree */ + size_t node_count; + /* The maximum number of nodes in the tree */ + size_t node_maximum; + + /* 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 *ossl_policy_data_new(POLICYINFO *policy, const ASN1_OBJECT *id, + int crit); +void ossl_policy_data_free(X509_POLICY_DATA *data); + +X509_POLICY_DATA *ossl_policy_cache_find_data(const X509_POLICY_CACHE *cache, + const ASN1_OBJECT *id); +int ossl_policy_cache_set_mapping(X509 *x, POLICY_MAPPINGS *maps); + +STACK_OF(X509_POLICY_NODE) *ossl_policy_node_cmp_new(void); + +void ossl_policy_cache_free(X509_POLICY_CACHE *cache); + +X509_POLICY_NODE *ossl_policy_level_find_node(const X509_POLICY_LEVEL *level, + const X509_POLICY_NODE *parent, + const ASN1_OBJECT *id); + +X509_POLICY_NODE *ossl_policy_tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk, + const ASN1_OBJECT *id); + +X509_POLICY_NODE *ossl_policy_level_add_node(X509_POLICY_LEVEL *level, + X509_POLICY_DATA *data, + X509_POLICY_NODE *parent, + X509_POLICY_TREE *tree, + int extra_data); +void ossl_policy_node_free(X509_POLICY_NODE *node); +int ossl_policy_node_match(const X509_POLICY_LEVEL *lvl, + const X509_POLICY_NODE *node, const ASN1_OBJECT *oid); + +const X509_POLICY_CACHE *ossl_policy_cache_set(X509 *x); diff --git a/crypto/x509/pcy_map.c b/crypto/x509/pcy_map.c new file mode 100644 index 000000000000..60dfd1e3203b --- /dev/null +++ b/crypto/x509/pcy_map.c @@ -0,0 +1,79 @@ +/* + * Copyright 2004-2021 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 "crypto/x509.h" + +#include "pcy_local.h" + +/* + * Set policy mapping entries in cache. Note: this modifies the passed + * POLICY_MAPPINGS structure + */ + +int ossl_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 = ossl_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 = ossl_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)) { + ossl_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: + 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 000000000000..9b77e6e95e05 --- /dev/null +++ b/crypto/x509/pcy_node.c @@ -0,0 +1,161 @@ +/* + * Copyright 2004-2023 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_local.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) *ossl_policy_node_cmp_new(void) +{ + return sk_X509_POLICY_NODE_new(node_cmp); +} + +X509_POLICY_NODE *ossl_policy_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 *ossl_policy_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 *ossl_policy_level_add_node(X509_POLICY_LEVEL *level, + X509_POLICY_DATA *data, + X509_POLICY_NODE *parent, + X509_POLICY_TREE *tree, + int extra_data) +{ + X509_POLICY_NODE *node; + + /* Verify that the tree isn't too large. This mitigates CVE-2023-0464 */ + if (tree->node_maximum > 0 && tree->node_count >= tree->node_maximum) + return NULL; + + node = OPENSSL_zalloc(sizeof(*node)); + if (node == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return NULL; + } + node->data = data; + node->parent = parent; + if (level != NULL) { + 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 = ossl_policy_node_cmp_new(); + if (level->nodes == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto node_error; + } + if (!sk_X509_POLICY_NODE_push(level->nodes, node)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto node_error; + } + } + } + + if (extra_data) { + if (tree->extra_data == NULL) + tree->extra_data = sk_X509_POLICY_DATA_new_null(); + if (tree->extra_data == NULL){ + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto extra_data_error; + } + if (!sk_X509_POLICY_DATA_push(tree->extra_data, data)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto extra_data_error; + } + } + + tree->node_count++; + if (parent) + parent->nchild++; + + return node; + + extra_data_error: + if (level != NULL) { + if (level->anyPolicy == node) + level->anyPolicy = NULL; + else + (void) sk_X509_POLICY_NODE_pop(level->nodes); + } + + node_error: + ossl_policy_node_free(node); + return NULL; +} + +void ossl_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 ossl_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 000000000000..2012810303ed --- /dev/null +++ b/crypto/x509/pcy_tree.c @@ -0,0 +1,723 @@ +/* + * Copyright 2004-2023 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_local.h" + +/* + * If the maximum number of nodes in the policy tree isn't defined, set it to + * a generous default of 1000 nodes. + * + * Defining this to be zero means unlimited policy tree growth which opens the + * door on CVE-2023-0464. + */ +#ifndef OPENSSL_POLICY_TREE_NODES_MAX +# define OPENSSL_POLICY_TREE_NODES_MAX 1000 +#endif + +static void exnode_free(X509_POLICY_NODE *node); + +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", + (long)(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 (ossl_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 = ossl_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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return X509_PCY_TREE_INTERNAL; + } + + /* Limit the growth of the tree to mitigate CVE-2023-0464 */ + tree->node_maximum = OPENSSL_POLICY_TREE_NODES_MAX; + + /* + * 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); + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return X509_PCY_TREE_INTERNAL; + } + tree->nlevel = n+1; + level = tree->levels; + if ((data = ossl_policy_data_new(NULL, + OBJ_nid2obj(NID_any_policy), 0)) == NULL) + goto bad_tree; + if (ossl_policy_level_add_node(level, data, NULL, tree, 1) == NULL) { + ossl_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 = ossl_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_TREE *tree) +{ + 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 (ossl_policy_node_match(last, node, data->valid_policy)) { + if (ossl_policy_level_add_node(curr, data, node, tree, 0) == NULL) + return 0; + matched = 1; + } + } + if (!matched && last->anyPolicy) { + if (ossl_policy_level_add_node(curr, data, last->anyPolicy, tree, 0) == 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, + X509_POLICY_TREE *tree) +{ + 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, tree)) + 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 = ossl_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 (ossl_policy_level_add_node(curr, data, node, tree, 1) == NULL) { + ossl_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 (ossl_policy_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 && + ossl_policy_level_add_node(curr, cache->anyPolicy, + last->anyPolicy, tree, 0) == 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 = ossl_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 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 = ossl_policy_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 = ossl_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 = ossl_policy_level_add_node(NULL, extra, anyPolicy->parent, + tree, 1); + if (node == NULL) { + ossl_policy_data_free(extra); + return 0; + } + } + if (!tree->user_policies) { + tree->user_policies = sk_X509_POLICY_NODE_new_null(); + if (!tree->user_policies) { + exnode_free(node); + return 0; + } + } + if (!sk_X509_POLICY_NODE_push(tree->user_policies, node)) { + exnode_free(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 = ossl_policy_cache_set(curr->cert); + if (!tree_link_nodes(curr, cache, tree)) + 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, ossl_policy_node_free); + ossl_policy_node_free(curr->anyPolicy); + } + + sk_X509_POLICY_DATA_pop_free(tree->extra_data, ossl_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 000000000000..27a99a4b13ed --- /dev/null +++ b/crypto/x509/standard_exts.h @@ -0,0 +1,80 @@ +/* + * Copyright 1999-2021 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[] = { + &ossl_v3_nscert, + &ossl_v3_ns_ia5_list[0], + &ossl_v3_ns_ia5_list[1], + &ossl_v3_ns_ia5_list[2], + &ossl_v3_ns_ia5_list[3], + &ossl_v3_ns_ia5_list[4], + &ossl_v3_ns_ia5_list[5], + &ossl_v3_ns_ia5_list[6], + &ossl_v3_skey_id, + &ossl_v3_key_usage, + &ossl_v3_pkey_usage_period, + &ossl_v3_alt[0], + &ossl_v3_alt[1], + &ossl_v3_bcons, + &ossl_v3_crl_num, + &ossl_v3_cpols, + &ossl_v3_akey_id, + &ossl_v3_crld, + &ossl_v3_ext_ku, + &ossl_v3_delta_crl, + &ossl_v3_crl_reason, +#ifndef OPENSSL_NO_OCSP + &ossl_v3_crl_invdate, +#endif + &ossl_v3_sxnet, + &ossl_v3_info, +#ifndef OPENSSL_NO_RFC3779 + &ossl_v3_addr, + &ossl_v3_asid, +#endif +#ifndef OPENSSL_NO_OCSP + &ossl_v3_ocsp_nonce, + &ossl_v3_ocsp_crlid, + &ossl_v3_ocsp_accresp, + &ossl_v3_ocsp_nocheck, + &ossl_v3_ocsp_acutoff, + &ossl_v3_ocsp_serviceloc, +#endif + &ossl_v3_sinfo, + &ossl_v3_policy_constraints, +#ifndef OPENSSL_NO_OCSP + &ossl_v3_crl_hold, +#endif + &ossl_v3_pci, + &ossl_v3_name_constraints, + &ossl_v3_policy_mappings, + &ossl_v3_inhibit_anyp, + &ossl_v3_idp, + &ossl_v3_alt[2], + &ossl_v3_freshest_crl, +#ifndef OPENSSL_NO_CT + &ossl_v3_ct_scts[0], + &ossl_v3_ct_scts[1], + &ossl_v3_ct_scts[2], +#endif + &ossl_v3_utf8_list[0], + &ossl_v3_issuer_sign_tool, + &ossl_v3_tls_feature, + &ossl_v3_ext_admission +}; + +/* Number of standard extensions */ + +#define STANDARD_EXTENSION_COUNT OSSL_NELEM(standard_exts) + diff --git a/crypto/x509/t_crl.c b/crypto/x509/t_crl.c index 8e262912ffaa..e77a77978a83 100644 --- a/crypto/x509/t_crl.c +++ b/crypto/x509/t_crl.c @@ -1,7 +1,7 @@ /* - * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1999-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -22,7 +22,7 @@ int X509_CRL_print_fp(FILE *fp, X509_CRL *x) int ret; if ((b = BIO_new(BIO_s_file())) == NULL) { - X509err(X509_F_X509_CRL_PRINT_FP, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); return 0; } BIO_set_fp(b, fp, BIO_NOCLOSE); @@ -48,7 +48,7 @@ int X509_CRL_print_ex(BIO *out, X509_CRL *x, unsigned long nmflag) BIO_printf(out, "Certificate Revocation List (CRL):\n"); l = X509_CRL_get_version(x); - if (l >= 0 && l <= 1) + if (l >= X509_CRL_VERSION_1 && l <= X509_CRL_VERSION_2) BIO_printf(out, "%8sVersion %ld (0x%lx)\n", "", l + 1, (unsigned long)l); else BIO_printf(out, "%8sVersion unknown (%ld)\n", "", l); diff --git a/crypto/x509/t_req.c b/crypto/x509/t_req.c index dc3b4f262de9..63626c0d9810 100644 --- a/crypto/x509/t_req.c +++ b/crypto/x509/t_req.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -24,7 +24,7 @@ int X509_REQ_print_fp(FILE *fp, X509_REQ *x) int ret; if ((b = BIO_new(BIO_s_file())) == NULL) { - X509err(X509_F_X509_REQ_PRINT_FP, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); return 0; } BIO_set_fp(b, fp, BIO_NOCLOSE); @@ -42,15 +42,15 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, EVP_PKEY *pkey; STACK_OF(X509_EXTENSION) *exts; char mlch = ' '; - int nmindent = 0; + int nmindent = 0, printok = 0; if ((nmflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) { mlch = '\n'; nmindent = 12; } - if (nmflags == X509_FLAG_COMPAT) - nmindent = 16; + if (nmflags == XN_FLAG_COMPAT) + printok = 1; if (!(cflag & X509_FLAG_NO_HEADER)) { if (BIO_write(bp, "Certificate Request:\n", 21) <= 0) @@ -60,7 +60,7 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, } if (!(cflag & X509_FLAG_NO_VERSION)) { l = X509_REQ_get_version(x); - if (l >= 0 && l <= 2) { + if (l == X509_REQ_VERSION_1) { if (BIO_printf(bp, "%8sVersion: %ld (0x%lx)\n", "", l + 1, (unsigned long)l) <= 0) goto err; } else { @@ -72,7 +72,7 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, if (BIO_printf(bp, " Subject:%c", mlch) <= 0) goto err; if (X509_NAME_print_ex(bp, X509_REQ_get_subject_name(x), - nmindent, nmflags) < 0) + nmindent, nmflags) < printok) goto err; if (BIO_write(bp, "\n", 1) <= 0) goto err; @@ -108,7 +108,7 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, goto err; if (X509_REQ_get_attr_count(x) == 0) { - if (BIO_printf(bp, "%12sa0:00\n", "") <= 0) + if (BIO_printf(bp, "%12s(none)\n", "") <= 0) goto err; } else { for (i = 0; i < X509_REQ_get_attr_count(x); i++) { @@ -128,7 +128,7 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, ii = 0; count = X509_ATTRIBUTE_count(a); if (count == 0) { - X509err(X509_F_X509_REQ_PRINT_EX, X509_R_INVALID_ATTRIBUTES); + ERR_raise(ERR_LIB_X509, X509_R_INVALID_ATTRIBUTES); return 0; } get_next: @@ -166,14 +166,14 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, if (!(cflag & X509_FLAG_NO_EXTENSIONS)) { exts = X509_REQ_get_extensions(x); if (exts) { - if (BIO_printf(bp, "%8sRequested Extensions:\n", "") <= 0) + if (BIO_printf(bp, "%12sRequested Extensions:\n", "") <= 0) goto err; for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { ASN1_OBJECT *obj; X509_EXTENSION *ex; int critical; ex = sk_X509_EXTENSION_value(exts, i); - if (BIO_printf(bp, "%12s", "") <= 0) + if (BIO_printf(bp, "%16s", "") <= 0) goto err; obj = X509_EXTENSION_get_object(ex); if (i2a_ASN1_OBJECT(bp, obj) <= 0) @@ -181,8 +181,8 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, critical = X509_EXTENSION_get_critical(ex); if (BIO_printf(bp, ": %s\n", critical ? "critical" : "") <= 0) goto err; - if (!X509V3_EXT_print(bp, ex, cflag, 16)) { - if (BIO_printf(bp, "%16s", "") <= 0 + if (!X509V3_EXT_print(bp, ex, cflag, 20)) { + if (BIO_printf(bp, "%20s", "") <= 0 || ASN1_STRING_print(bp, X509_EXTENSION_get_data(ex)) <= 0) goto err; @@ -204,7 +204,7 @@ int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflags, return 1; err: - X509err(X509_F_X509_REQ_PRINT_EX, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); return 0; } diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c index ece987a6bdbe..5b0282bc132f 100644 --- a/crypto/x509/t_x509.c +++ b/crypto/x509/t_x509.c @@ -1,7 +1,7 @@ /* * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -15,6 +15,7 @@ #include <openssl/x509.h> #include <openssl/x509v3.h> #include "crypto/asn1.h" +#include "crypto/x509.h" #ifndef OPENSSL_NO_STDIO int X509_print_fp(FILE *fp, X509 *x) @@ -29,7 +30,7 @@ int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag, int ret; if ((b = BIO_new(BIO_s_file())) == NULL) { - X509err(X509_F_X509_PRINT_EX_FP, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); return 0; } BIO_set_fp(b, fp, BIO_NOCLOSE); @@ -50,8 +51,7 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, long l; int ret = 0, i; char *m = NULL, mlch = ' '; - int nmindent = 0; - ASN1_INTEGER *bs; + int nmindent = 0, printok = 0; EVP_PKEY *pkey = NULL; const char *neg; @@ -60,8 +60,8 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, nmindent = 12; } - if (nmflags == X509_FLAG_COMPAT) - nmindent = 16; + if (nmflags == XN_FLAG_COMPAT) + printok = 1; if (!(cflag & X509_FLAG_NO_HEADER)) { if (BIO_write(bp, "Certificate:\n", 13) <= 0) @@ -71,7 +71,7 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, } if (!(cflag & X509_FLAG_NO_VERSION)) { l = X509_get_version(x); - if (l >= 0 && l <= 2) { + if (l >= X509_VERSION_1 && l <= X509_VERSION_3) { if (BIO_printf(bp, "%8sVersion: %ld (0x%lx)\n", "", l + 1, (unsigned long)l) <= 0) goto err; } else { @@ -80,11 +80,11 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, } } if (!(cflag & X509_FLAG_NO_SERIAL)) { + const ASN1_INTEGER *bs = X509_get0_serialNumber(x); if (BIO_write(bp, " Serial Number:", 22) <= 0) goto err; - bs = X509_get_serialNumber(x); if (bs->length <= (int)sizeof(long)) { ERR_set_mark(); l = ASN1_INTEGER_get(bs); @@ -130,7 +130,7 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, if (BIO_printf(bp, " Issuer:%c", mlch) <= 0) goto err; if (X509_NAME_print_ex(bp, X509_get_issuer_name(x), nmindent, nmflags) - < 0) + < printok) goto err; if (BIO_write(bp, "\n", 1) <= 0) goto err; @@ -140,11 +140,11 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, goto err; if (BIO_write(bp, " Not Before: ", 24) <= 0) goto err; - if (!ASN1_TIME_print(bp, X509_get0_notBefore(x))) + if (ossl_asn1_time_print_ex(bp, X509_get0_notBefore(x), ASN1_DTFLGS_RFC822) == 0) goto err; if (BIO_write(bp, "\n Not After : ", 25) <= 0) goto err; - if (!ASN1_TIME_print(bp, X509_get0_notAfter(x))) + if (ossl_asn1_time_print_ex(bp, X509_get0_notAfter(x), ASN1_DTFLGS_RFC822) == 0) goto err; if (BIO_write(bp, "\n", 1) <= 0) goto err; @@ -153,7 +153,7 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, if (BIO_printf(bp, " Subject:%c", mlch) <= 0) goto err; if (X509_NAME_print_ex - (bp, X509_get_subject_name(x), nmindent, nmflags) < 0) + (bp, X509_get_subject_name(x), nmindent, nmflags) < printok) goto err; if (BIO_write(bp, "\n", 1) <= 0) goto err; @@ -197,9 +197,10 @@ int X509_print_ex(BIO *bp, X509 *x, unsigned long nmflags, } } - if (!(cflag & X509_FLAG_NO_EXTENSIONS)) - X509V3_extensions_print(bp, "X509v3 extensions", - X509_get0_extensions(x), cflag, 8); + if (!(cflag & X509_FLAG_NO_EXTENSIONS) + && !X509V3_extensions_print(bp, "X509v3 extensions", + X509_get0_extensions(x), cflag, 8)) + goto err; if (!(cflag & X509_FLAG_NO_SIGDUMP)) { const X509_ALGOR *sig_alg; @@ -226,8 +227,11 @@ int X509_ocspid_print(BIO *bp, X509 *x) int i; unsigned char SHA1md[SHA_DIGEST_LENGTH]; ASN1_BIT_STRING *keybstr; - X509_NAME *subj; + const X509_NAME *subj; + EVP_MD *md = NULL; + if (x == NULL || bp == NULL) + return 0; /* * display the hash of the subject as it would appear in OCSP requests */ @@ -235,11 +239,16 @@ int X509_ocspid_print(BIO *bp, X509 *x) goto err; subj = X509_get_subject_name(x); derlen = i2d_X509_NAME(subj, NULL); + if (derlen <= 0) + goto err; if ((der = dertmp = OPENSSL_malloc(derlen)) == NULL) goto err; i2d_X509_NAME(subj, &dertmp); - if (!EVP_Digest(der, derlen, SHA1md, NULL, EVP_sha1(), NULL)) + md = EVP_MD_fetch(x->libctx, SN_sha1, x->propq); + if (md == NULL) + goto err; + if (!EVP_Digest(der, derlen, SHA1md, NULL, md, NULL)) goto err; for (i = 0; i < SHA_DIGEST_LENGTH; i++) { if (BIO_printf(bp, "%02X", SHA1md[i]) <= 0) @@ -260,18 +269,19 @@ int X509_ocspid_print(BIO *bp, X509 *x) goto err; if (!EVP_Digest(ASN1_STRING_get0_data(keybstr), - ASN1_STRING_length(keybstr), SHA1md, NULL, EVP_sha1(), - NULL)) + ASN1_STRING_length(keybstr), SHA1md, NULL, md, NULL)) goto err; for (i = 0; i < SHA_DIGEST_LENGTH; i++) { if (BIO_printf(bp, "%02X", SHA1md[i]) <= 0) goto err; } BIO_printf(bp, "\n"); + EVP_MD_free(md); return 1; err: OPENSSL_free(der); + EVP_MD_free(md); return 0; } @@ -284,7 +294,7 @@ int X509_signature_dump(BIO *bp, const ASN1_STRING *sig, int indent) s = sig->data; for (i = 0; i < n; i++) { if ((i % 18) == 0) { - if (BIO_write(bp, "\n", 1) <= 0) + if (i > 0 && BIO_write(bp, "\n", 1) <= 0) return 0; if (BIO_indent(bp, indent, indent) <= 0) return 0; @@ -302,11 +312,14 @@ int X509_signature_print(BIO *bp, const X509_ALGOR *sigalg, const ASN1_STRING *sig) { int sig_nid; - if (BIO_puts(bp, " Signature Algorithm: ") <= 0) + int indent = 4; + if (BIO_printf(bp, "%*sSignature Algorithm: ", indent, "") <= 0) return 0; if (i2a_ASN1_OBJECT(bp, sigalg->algorithm) <= 0) return 0; + if (sig && BIO_printf(bp, "\n%*sSignature Value:", indent, "") <= 0) + return 0; sig_nid = OBJ_obj2nid(sigalg->algorithm); if (sig_nid != NID_undef) { int pkey_nid, dig_nid; @@ -314,13 +327,13 @@ int X509_signature_print(BIO *bp, const X509_ALGOR *sigalg, if (OBJ_find_sigid_algs(sig_nid, &dig_nid, &pkey_nid)) { ameth = EVP_PKEY_asn1_find(NULL, pkey_nid); if (ameth && ameth->sig_print) - return ameth->sig_print(bp, sigalg, sig, 9, 0); + return ameth->sig_print(bp, sigalg, sig, indent + 4, 0); } } - if (sig) - return X509_signature_dump(bp, sig, 9); - else if (BIO_puts(bp, "\n") <= 0) + if (BIO_write(bp, "\n", 1) != 1) return 0; + if (sig) + return X509_signature_dump(bp, sig, indent + 4); return 1; } @@ -377,3 +390,138 @@ int X509_aux_print(BIO *out, X509 *x, int indent) } return 1; } + +/* + * Helper functions for improving certificate verification error diagnostics + */ + +int ossl_x509_print_ex_brief(BIO *bio, X509 *cert, unsigned long neg_cflags) +{ + unsigned long flags = ASN1_STRFLGS_RFC2253 | ASN1_STRFLGS_ESC_QUOTE | + XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_FN_SN; + + if (cert == NULL) + return BIO_printf(bio, " (no certificate)\n") > 0; + if (BIO_printf(bio, " certificate\n") <= 0 + || !X509_print_ex(bio, cert, flags, ~X509_FLAG_NO_SUBJECT)) + return 0; + if (X509_check_issued((X509 *)cert, cert) == X509_V_OK) { + if (BIO_printf(bio, " self-issued\n") <= 0) + return 0; + } else { + if (BIO_printf(bio, " ") <= 0 + || !X509_print_ex(bio, cert, flags, ~X509_FLAG_NO_ISSUER)) + return 0; + } + if (!X509_print_ex(bio, cert, flags, + ~(X509_FLAG_NO_SERIAL | X509_FLAG_NO_VALIDITY))) + return 0; + if (X509_cmp_current_time(X509_get0_notBefore(cert)) > 0) + if (BIO_printf(bio, " not yet valid\n") <= 0) + return 0; + if (X509_cmp_current_time(X509_get0_notAfter(cert)) < 0) + if (BIO_printf(bio, " no more valid\n") <= 0) + return 0; + return X509_print_ex(bio, cert, flags, + ~neg_cflags & ~X509_FLAG_EXTENSIONS_ONLY_KID); +} + +static int print_certs(BIO *bio, const STACK_OF(X509) *certs) +{ + int i; + + if (certs == NULL || sk_X509_num(certs) <= 0) + return BIO_printf(bio, " (no certificates)\n") >= 0; + + for (i = 0; i < sk_X509_num(certs); i++) { + X509 *cert = sk_X509_value(certs, i); + + if (cert != NULL) { + if (!ossl_x509_print_ex_brief(bio, cert, 0)) + return 0; + if (!X509V3_extensions_print(bio, NULL, + X509_get0_extensions(cert), + X509_FLAG_EXTENSIONS_ONLY_KID, 8)) + return 0; + } + } + return 1; +} + +static int print_store_certs(BIO *bio, X509_STORE *store) +{ + if (store != NULL) { + STACK_OF(X509) *certs = X509_STORE_get1_all_certs(store); + int ret = print_certs(bio, certs); + + sk_X509_pop_free(certs, X509_free); + return ret; + } else { + return BIO_printf(bio, " (no trusted store)\n") >= 0; + } +} + +/* Extend the error queue with details on a failed cert verification */ +int X509_STORE_CTX_print_verify_cb(int ok, X509_STORE_CTX *ctx) +{ + if (ok == 0 && ctx != NULL) { + int cert_error = X509_STORE_CTX_get_error(ctx); + BIO *bio = BIO_new(BIO_s_mem()); /* may be NULL */ + + BIO_printf(bio, "%s at depth = %d error = %d (%s)\n", + X509_STORE_CTX_get0_parent_ctx(ctx) != NULL + ? "CRL path validation" + : "Certificate verification", + X509_STORE_CTX_get_error_depth(ctx), + cert_error, X509_verify_cert_error_string(cert_error)); + { + X509_STORE *ts = X509_STORE_CTX_get0_store(ctx); + X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts); + char *str; + int idx = 0; + + switch (cert_error) { + case X509_V_ERR_HOSTNAME_MISMATCH: + BIO_printf(bio, "Expected hostname(s) = "); + while ((str = X509_VERIFY_PARAM_get0_host(vpm, idx++)) != NULL) + BIO_printf(bio, "%s%s", idx == 1 ? "" : ", ", str); + BIO_printf(bio, "\n"); + break; + case X509_V_ERR_EMAIL_MISMATCH: + str = X509_VERIFY_PARAM_get0_email(vpm); + if (str != NULL) + BIO_printf(bio, "Expected email address = %s\n", str); + break; + case X509_V_ERR_IP_ADDRESS_MISMATCH: + str = X509_VERIFY_PARAM_get1_ip_asc(vpm); + if (str != NULL) + BIO_printf(bio, "Expected IP address = %s\n", str); + OPENSSL_free(str); + break; + default: + break; + } + } + + BIO_printf(bio, "Failure for:\n"); + ossl_x509_print_ex_brief(bio, X509_STORE_CTX_get_current_cert(ctx), + X509_FLAG_NO_EXTENSIONS); + if (cert_error == X509_V_ERR_CERT_UNTRUSTED + || cert_error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT + || cert_error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN + || cert_error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT + || cert_error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY + || cert_error == X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER + || cert_error == X509_V_ERR_STORE_LOOKUP) { + BIO_printf(bio, "Non-trusted certs:\n"); + print_certs(bio, X509_STORE_CTX_get0_untrusted(ctx)); + BIO_printf(bio, "Certs in trust store:\n"); + print_store_certs(bio, X509_STORE_CTX_get0_store(ctx)); + } + ERR_raise(ERR_LIB_X509, X509_R_CERTIFICATE_VERIFICATION_FAILED); + ERR_add_error_mem_bio("\n", bio); + BIO_free(bio); + } + + return ok; +} diff --git a/crypto/x509/v3_addr.c b/crypto/x509/v3_addr.c new file mode 100644 index 000000000000..20f3d2ba70de --- /dev/null +++ b/crypto/x509/v3_addr.c @@ -0,0 +1,1361 @@ +/* + * Copyright 2006-2024 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 <assert.h> +#include <string.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 "crypto/x509.h" +#include "ext_dat.h" +#include "x509_local.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; + + /* + * It is the responsibility of the caller to confirm min <= max. We don't + * use ossl_assert() here since we have no way of signalling an error from + * this function - so we just use a plain assert instead. + */ + assert(memcmp(min, max, length) <= 0); + + 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, const int afilen) +{ + int bytelen = (prefixlen + 7) / 8, bitlen = prefixlen % 8; + IPAddressOrRange *aor; + + if (prefixlen < 0 || prefixlen > (afilen * 8)) + return 0; + if ((aor = IPAddressOrRange_new()) == 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 (memcmp(min, max, length) > 0) + return 0; + + if ((prefixlen = range_should_be_prefix(min, max, length)) >= 0) + return make_addressPrefix(result, min, prefixlen, length); + + 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, length_from_afi(afi))) + 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; +} + +static int IPAddressFamily_check_len(const IPAddressFamily *f) +{ + if (f->addressFamily->length < 2 || f->addressFamily->length > 3) + return 0; + else + return 1; +} + +/* + * 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_check_len(a) || !IPAddressFamily_check_len(b)) + return 0; + + 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; + } + + if (!IPAddressFamily_check_len(f)) + 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 (!IPAddressFamily_check_len(f)) + return 0; + + 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) { + ERR_raise(ERR_LIB_X509V3, 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 (!ossl_v3_name_cmp(val->name, "IPv4")) { + afi = IANA_AFI_IPV4; + } else if (!ossl_v3_name_cmp(val->name, "IPv6")) { + afi = IANA_AFI_IPV6; + } else if (!ossl_v3_name_cmp(val->name, "IPv4-SAFI")) { + afi = IANA_AFI_IPV4; + safi = &safi_; + } else if (!ossl_v3_name_cmp(val->name, "IPv6-SAFI")) { + afi = IANA_AFI_IPV6; + safi = &safi_; + } else { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_EXTENSION_NAME_ERROR, + "%s", val->name); + 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) { + if (val->value == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_MISSING_VALUE); + goto err; + } + *safi = strtoul(val->value, &t, 0); + t += strspn(t, " \t"); + if (*safi > 0xFF || *t++ != ':') { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_SAFI); + X509V3_conf_add_error_name_value(val); + goto err; + } + t += strspn(t, " \t"); + s = OPENSSL_strdup(t); + } else { + s = OPENSSL_strdup(val->value); + } + if (s == NULL) { + ERR_raise(ERR_LIB_X509V3, 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)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_INHERITANCE); + X509V3_conf_add_error_name_value(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 (ossl_a2i_ipadd(min, s) != length) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_IPADDRESS); + X509V3_conf_add_error_name_value(val); + goto err; + } + + switch (delim) { + case '/': + prefixlen = (int)strtoul(s + i2, &t, 10); + if (t == s + i2 + || *t != '\0' + || prefixlen > (length * 8) + || prefixlen < 0) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_add_error_name_value(val); + goto err; + } + if (!X509v3_addr_add_prefix(addr, afi, safi, min, prefixlen)) { + ERR_raise(ERR_LIB_X509V3, 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') { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_add_error_name_value(val); + goto err; + } + if (ossl_a2i_ipadd(max, s + i1) != length) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_IPADDRESS); + X509V3_conf_add_error_name_value(val); + goto err; + } + if (memcmp(min, max, length_from_afi(afi)) > 0) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_add_error_name_value(val); + goto err; + } + if (!X509v3_addr_add_range(addr, afi, safi, min, max)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + case '\0': + if (!X509v3_addr_add_prefix(addr, afi, safi, min, length * 8)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + break; + default: + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); + X509V3_conf_add_error_name_value(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 ossl_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 0; + 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 = sk_IPAddressFamily_value(b, j); + + if (fb == NULL) + return 0; + if (!IPAddressFamily_check_len(fa) || !IPAddressFamily_check_len(fb)) + 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; \ + rv = ctx->verify_cb(0, ctx); \ + } else { \ + rv = 0; \ + } \ + if (rv == 0) \ + 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 = 0, rv; + 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) + return 1; /* Return success */ + } + 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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + if (ctx != NULL) + ctx->error = X509_V_ERR_OUT_OF_MEM; + 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 (!IPAddressFamily_check_len(fc)) + goto done; + + 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 (!IPAddressFamily_check_len(fc) || !IPAddressFamily_check_len(fp)) + goto done; + + 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)))) + (void)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 (!IPAddressFamily_check_len(fp)) + goto done; + + if (fp->ipAddressChoice->type == IPAddressChoice_inherit + && sk_IPAddressFamily_find(child, fp) >= 0) + validation_err(X509_V_ERR_UNNESTED_RESOURCE); + } + } + ret = 1; + 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 000000000000..53259c29f569 --- /dev/null +++ b/crypto/x509/v3_admis.c @@ -0,0 +1,353 @@ +/* + * Copyright 2017-2023 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/types.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 ossl_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:\n", 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 namingAuthorityId: ", 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 + 2) <= 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 + 4) <= 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 0; +} + +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 000000000000..1e82c0f4a33f --- /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 OSSL_CRYPTO_X509_V3_ADMIS_H +# define OSSL_CRYPTO_X509_V3_ADMIS_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_akeya.c b/crypto/x509/v3_akeya.c new file mode 100644 index 000000000000..aae8c21e73c2 --- /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_akid.c b/crypto/x509/v3_akid.c new file mode 100644 index 000000000000..43b515f50c49 --- /dev/null +++ b/crypto/x509/v3_akid.c @@ -0,0 +1,218 @@ +/* + * Copyright 1999-2021 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 "crypto/x509.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 ossl_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 = NULL; + STACK_OF(CONF_VALUE) *origextlist = extlist, *tmpextlist; + + if (akeyid->keyid) { + tmp = OPENSSL_buf2hexstr(akeyid->keyid->data, akeyid->keyid->length); + if (tmp == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return NULL; + } + if (!X509V3_add_value((akeyid->issuer || akeyid->serial) ? "keyid" : NULL, + tmp, &extlist)) { + OPENSSL_free(tmp); + ERR_raise(ERR_LIB_X509V3, ERR_R_X509_LIB); + goto err; + } + OPENSSL_free(tmp); + } + if (akeyid->issuer) { + tmpextlist = i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist); + if (tmpextlist == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_X509_LIB); + goto err; + } + extlist = tmpextlist; + } + if (akeyid->serial) { + tmp = OPENSSL_buf2hexstr(akeyid->serial->data, akeyid->serial->length); + if (tmp == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + if (!X509V3_add_value("serial", tmp, &extlist)) { + OPENSSL_free(tmp); + goto err; + } + OPENSSL_free(tmp); + } + return extlist; + err: + if (origextlist == NULL) + sk_CONF_VALUE_pop_free(extlist, X509V3_conf_free); + return NULL; +} + +/*- + * 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, n = sk_CONF_VALUE_num(values); + 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 *issuer_cert; + int same_issuer, ss; + AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new(); + + if (akeyid == NULL) + goto err; + + if (n == 1 && strcmp(sk_CONF_VALUE_value(values, 0)->name, "none") == 0) { + return akeyid; + } + + for (i = 0; i < n; 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 { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_UNKNOWN_OPTION, + "name=%s", cnf->name); + goto err; + } + } + + if (ctx != NULL && (ctx->flags & X509V3_CTX_TEST) != 0) + return akeyid; + + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER); + goto err; + } + if ((issuer_cert = ctx->issuer_cert) == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_ISSUER_CERTIFICATE); + goto err; + } + same_issuer = ctx->subject_cert == ctx->issuer_cert; + ERR_set_mark(); + if (ctx->issuer_pkey != NULL) + ss = X509_check_private_key(ctx->subject_cert, ctx->issuer_pkey); + else + ss = same_issuer; + ERR_pop_to_mark(); + + /* unless forced with "always", AKID is suppressed for self-signed certs */ + if (keyid == 2 || (keyid == 1 && !ss)) { + /* + * prefer any pre-existing subject key identifier of the issuer cert + * except issuer cert is same as subject cert and is not self-signed + */ + i = X509_get_ext_by_NID(issuer_cert, NID_subject_key_identifier, -1); + if (i >= 0 && (ext = X509_get_ext(issuer_cert, i)) != NULL + && !(same_issuer && !ss)) + ikeyid = X509V3_EXT_d2i(ext); + if (ikeyid == NULL && same_issuer && ctx->issuer_pkey != NULL) { + /* generate fallback AKID, emulating s2i_skey_id(..., "hash") */ + X509_PUBKEY *pubkey = NULL; + + if (X509_PUBKEY_set(&pubkey, ctx->issuer_pkey)) + ikeyid = ossl_x509_pubkey_hash(pubkey); + X509_PUBKEY_free(pubkey); + } + if ((keyid == 2 || issuer == 0) + && (ikeyid == NULL + || ASN1_STRING_length(ikeyid) <= 2) /* indicating "none" */) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID); + goto err; + } + } + + if (issuer == 2 || (issuer == 1 && ikeyid == NULL)) { + isname = X509_NAME_dup(X509_get_issuer_name(issuer_cert)); + serial = ASN1_INTEGER_dup(X509_get0_serialNumber(issuer_cert)); + if (isname == NULL || serial == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS); + goto err; + } + } + + if (isname != NULL) { + if ((gens = sk_GENERAL_NAME_new_null()) == NULL + || (gen = GENERAL_NAME_new()) == NULL + || !sk_GENERAL_NAME_push(gens, gen)) { + ERR_raise(ERR_LIB_X509V3, 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); + AUTHORITY_KEYID_free(akeyid); + return NULL; +} diff --git a/crypto/x509/v3_asid.c b/crypto/x509/v3_asid.c new file mode 100644 index 000000000000..c2b6f8a660df --- /dev/null +++ b/crypto/x509/v3_asid.c @@ -0,0 +1,910 @@ +/* + * Copyright 2006-2024 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 "crypto/x509.h" +#include <openssl/bn.h> +#include "ext_dat.h" +#include "x509_local.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) { + ASIdentifierChoice_free(*choice); + *choice = 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_asIdsOrRanges) + 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) { + ASIdentifierChoice_free(*choice); + *choice = NULL; + return 0; + } + (*choice)->type = ASIdentifierChoice_asIdsOrRanges; + } + if ((aor = ASIdOrRange_new()) == NULL) + return 0; + if (!sk_ASIdOrRange_reserve((*choice)->u.asIdsOrRanges, 1)) + goto err; + 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; + } + /* Cannot fail due to the reservation above */ + if (!ossl_assert(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; + ASN1_INTEGER *orig; + 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)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto done; + } + + if ((a_max_plus_one = + BN_to_ASN1_INTEGER(bn, orig = a_max_plus_one)) == NULL) { + a_max_plus_one = orig; + ERR_raise(ERR_LIB_X509V3, 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; + ASN1_INTEGER *orig; + 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) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto done; + } + + if ((a_max_plus_one = + BN_to_ASN1_INTEGER(bn, orig = a_max_plus_one)) == NULL) { + a_max_plus_one = orig; + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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 (!ossl_v3_name_cmp(val->name, "AS")) { + which = V3_ASID_ASNUM; + } else if (!ossl_v3_name_cmp(val->name, "RDI")) { + which = V3_ASID_RDI; + } else { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_NAME_ERROR); + X509V3_conf_add_error_name_value(val); + goto err; + } + + if (val->value == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); + goto err; + } + + /* + * Handle inheritance. + */ + if (strcmp(val->value, "inherit") == 0) { + if (X509v3_asid_add_inherit(asid, which)) + continue; + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_INHERITANCE); + X509V3_conf_add_error_name_value(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] != '-') { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_ASNUMBER); + X509V3_conf_add_error_name_value(val); + goto err; + } + i2++; + i2 = i2 + strspn(val->value + i2, " \t"); + i3 = i2 + strspn(val->value + i2, "0123456789"); + if (val->value[i3] != '\0') { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_ASRANGE); + X509V3_conf_add_error_name_value(val); + goto err; + } + } + + /* + * Syntax is ok, read and add it. + */ + if (!is_range) { + if (!X509V3_get_value_int(val, &min)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + } else { + char *s = OPENSSL_strdup(val->value); + if (s == NULL) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + if (ASN1_INTEGER_cmp(min, max) > 0) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR); + goto err; + } + } + if (!X509v3_asid_add_id_or_range(asid, which, min, max)) { + ERR_raise(ERR_LIB_X509V3, 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 ossl_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) +{ + int subset; + + if (a == NULL || a == b) + return 1; + + if (b == NULL) + return 0; + + if (X509v3_asid_inherits(a) || X509v3_asid_inherits(b)) + return 0; + + subset = a->asnum == NULL + || (b->asnum != NULL + && asid_contains(b->asnum->u.asIdsOrRanges, + a->asnum->u.asIdsOrRanges)); + if (!subset) + return 0; + + return a->rdi == NULL + || (b->rdi != NULL + && 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 000000000000..6e7a165f26e8 --- /dev/null +++ b/crypto/x509/v3_bcons.c @@ -0,0 +1,85 @@ +/* + * Copyright 1999-2021 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" +#include "x509_local.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 ossl_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) { + ERR_raise(ERR_LIB_X509V3, 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 { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NAME); + X509V3_conf_add_error_name_value(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 000000000000..b53c5ba3ecd3 --- /dev/null +++ b/crypto/x509/v3_bitst.c @@ -0,0 +1,91 @@ +/* + * Copyright 1999-2021 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 ossl_v3_nscert = +EXT_BITSTRING(NID_netscape_cert_type, ns_cert_type_table); +const X509V3_EXT_METHOD ossl_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) { + ERR_raise(ERR_LIB_X509V3, 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)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + ASN1_BIT_STRING_free(bs); + return NULL; + } + break; + } + } + if (!bnam->lname) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT, + "%s", val->name); + 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 000000000000..1c11d671b2ed --- /dev/null +++ b/crypto/x509/v3_conf.c @@ -0,0 +1,584 @@ +/* + * Copyright 1999-2021 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 "crypto/ctype.h" +#include "internal/cryptlib.h" +#include <openssl/conf.h> +#include <openssl/x509.h> +#include "crypto/x509.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); + +static X509_EXTENSION *X509V3_EXT_nconf_int(CONF *conf, X509V3_CTX *ctx, + const char *section, + 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) { + if (section != NULL) + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_ERROR_IN_EXTENSION, + "section=%s, name=%s, value=%s", + section, name, value); + else + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_ERROR_IN_EXTENSION, + "name=%s, value=%s", name, value); + } + return ret; +} + +X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, const char *name, + const char *value) +{ + return X509V3_EXT_nconf_int(conf, ctx, NULL, name, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_UNKNOWN_EXTENSION_NAME); + return NULL; + } + if ((method = X509V3_EXT_get_nid(ext_nid)) == NULL) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_INVALID_EXTENSION_STRING, + "name=%s,section=%s", OBJ_nid2sn(ext_nid), 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_CONFIG_DATABASE); + return NULL; + } + if ((ext_struc = method->r2i(method, ctx, value)) == NULL) + return NULL; + } else { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED, + "name=%s", 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_len <= 0) + goto merr; + 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: + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_EXTENSION_NAME_ERROR, + "name=%s", 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) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR, + "value=%s", value); + goto err; + } + + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + ERR_raise(ERR_LIB_X509V3, 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_free(X509v3_delete_ext(sk, idx)); +} + +/* + * This is the main function: add a bunch of extensions based on a config + * file section to an extension STACK. Just check in case sk == NULL. + * Note that on error new elements may have been added to *sk if sk != NULL. + */ +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_int(conf, ctx, val->section, + val->name, val->value)) == NULL) + return 0; + if (sk != NULL) { + if (ctx->flags == X509V3_CTX_REPLACE) + delete_ext(*sk, ext); + if (X509v3_add_ext(sk, ext, -1) == NULL) { + X509_EXTENSION_free(ext); + return 0; + } + } + X509_EXTENSION_free(ext); + } + return 1; +} + +/* + * Add extensions to a certificate. Just check in case cert == NULL. + * Note that on error new elements may remain added to cert if cert != NULL. + */ +int X509V3_EXT_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509 *cert) +{ + STACK_OF(X509_EXTENSION) **sk = NULL; + if (cert != NULL) + sk = &cert->cert_info.extensions; + return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); +} + +/* + * Add extensions to a CRL. Just check in case crl == NULL. + * Note that on error new elements may remain added to crl if crl != NULL. + */ +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 != NULL) + sk = &crl->crl.extensions; + return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk); +} + +/* + * Add extensions to certificate request. Just check in case req is NULL. + * Note that on error new elements may remain added to req if req != NULL. + */ +int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, const char *section, + X509_REQ *req) +{ + STACK_OF(X509_EXTENSION) *exts = NULL; + int ret = X509V3_EXT_add_nconf_sk(conf, ctx, section, &exts); + + if (ret && req != NULL && exts != NULL) + ret = X509_REQ_add_extensions(req, exts); + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); + return ret; +} + +/* 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) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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) +{ + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER); + return; + } + 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) +{ + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER); + return; + } + ctx->flags = flags; + ctx->issuer_cert = issuer; + ctx->subject_cert = subj; + ctx->subject_req = req; + ctx->crl = crl; + ctx->db_meth = NULL; + ctx->db = NULL; + ctx->issuer_pkey = NULL; +} + +/* For API backward compatibility, this is separate from X509V3_set_ctx() */ +int X509V3_set_issuer_pkey(X509V3_CTX *ctx, EVP_PKEY *pkey) +{ + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (ctx->subject_cert == NULL && pkey != NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + ctx->issuer_pkey = pkey; + return 1; +} + +/* 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; + X509_EXTENSION *ret; + + if ((ctmp = NCONF_new(NULL)) == NULL) + return NULL; + CONF_set_nconf(ctmp, conf); + ret = X509V3_EXT_nconf(ctmp, ctx, name, value); + CONF_set_nconf(ctmp, NULL); + NCONF_free(ctmp); + return ret; +} + +X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, + X509V3_CTX *ctx, int ext_nid, const char *value) +{ + CONF *ctmp; + X509_EXTENSION *ret; + + if ((ctmp = NCONF_new(NULL)) == NULL) + return NULL; + CONF_set_nconf(ctmp, conf); + ret = X509V3_EXT_nconf_nid(ctmp, ctx, ext_nid, value); + CONF_set_nconf(ctmp, NULL); + NCONF_free(ctmp); + return ret; +} + +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) +{ + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER); + return; + } + 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; + int ret; + + if ((ctmp = NCONF_new(NULL)) == NULL) + return 0; + CONF_set_nconf(ctmp, conf); + ret = X509V3_EXT_add_nconf(ctmp, ctx, section, cert); + CONF_set_nconf(ctmp, NULL); + NCONF_free(ctmp); + return ret; +} + +/* 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; + int ret; + + if ((ctmp = NCONF_new(NULL)) == NULL) + return 0; + CONF_set_nconf(ctmp, conf); + ret = X509V3_EXT_CRL_add_nconf(ctmp, ctx, section, crl); + CONF_set_nconf(ctmp, NULL); + NCONF_free(ctmp); + return ret; +} + +/* 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; + int ret; + + if ((ctmp = NCONF_new(NULL)) == NULL) + return 0; + CONF_set_nconf(ctmp, conf); + ret = X509V3_EXT_REQ_add_nconf(ctmp, ctx, section, req); + CONF_set_nconf(ctmp, NULL); + NCONF_free(ctmp); + return ret; +} diff --git a/crypto/x509/v3_cpols.c b/crypto/x509/v3_cpols.c new file mode 100644 index 000000000000..5353a6916761 --- /dev/null +++ b/crypto/x509/v3_cpols.c @@ -0,0 +1,500 @@ +/* + * Copyright 1999-2021 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 "x509_local.h" +#include "pcy_local.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 ossl_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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_X509V3_LIB); + return NULL; + } + + pols = sk_POLICYINFO_new_reserve(NULL, num); + if (pols == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + + ia5org = 0; + for (i = 0; i < num; i++) { + cnf = sk_CONF_VALUE_value(vals, i); + if (cnf->value != NULL || cnf->name == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_POLICY_IDENTIFIER); + X509V3_conf_add_error_name_value(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 == NULL) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_INVALID_SECTION, + "%s", cnf->name); + 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) { + ERR_raise_data(ERR_LIB_X509V3, + X509V3_R_INVALID_OBJECT_IDENTIFIER, + "%s", cnf->name); + goto err; + } + pol = POLICYINFO_new(); + if (pol == NULL) { + ASN1_OBJECT_free(pobj); + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + pol->policyid = pobj; + } + if (!sk_POLICYINFO_push(pols, pol)) { + POLICYINFO_free(pol); + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(cnf); + goto err; + } + pol->policyid = pobj; + + } else if (!ossl_v3_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) { + ERR_raise(ERR_LIB_X509V3, 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 (!ossl_v3_name_cmp(cnf->name, "userNotice")) { + STACK_OF(CONF_VALUE) *unot; + if (*cnf->value != '@') { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXPECTED_A_SECTION_NAME); + X509V3_conf_err(cnf); + goto err; + } + unot = X509V3_get_section(ctx, cnf->value + 1); + if (!unot) { + ERR_raise(ERR_LIB_X509V3, 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 == NULL) + pol->qualifiers = sk_POLICYQUALINFO_new_null(); + if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual)) + goto merr; + } else { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_OPTION); + X509V3_conf_err(cnf); + goto err; + } + } + if (pol->policyid == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_POLICY_IDENTIFIER); + goto err; + } + + return pol; + + merr: + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NUMBERS); + X509V3_conf_add_error_name_value(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 { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_OPTION); + X509V3_conf_add_error_name_value(cnf); + goto err; + } + } + + if (not->noticeref && + (!not->noticeref->noticenos || !not->noticeref->organization)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS); + goto err; + } + + return qual; + + merr: + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NUMBER); + goto err; + } + if (!sk_ASN1_INTEGER_push(nnums, aint)) + goto merr; + } + return 1; + + merr: + ASN1_INTEGER_free(aint); + ERR_raise(ERR_LIB_X509V3, 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->length, + 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->length, + 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->length, + 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 000000000000..07c8379d3521 --- /dev/null +++ b/crypto/x509/v3_crld.c @@ -0,0 +1,513 @@ +/* + * Copyright 1999-2024 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 "crypto/x509.h" +#include "ext_dat.h" +#include "x509_local.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 ossl_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 ossl_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) { + ERR_raise(ERR_LIB_X509V3, 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 (cnf->value == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_MISSING_VALUE); + goto err; + } + + 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) { + X509_NAME_free(nm); + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_MULTIPLE_RDNS); + goto err; + } + } else + return 0; + + if (*pdp) { + ERR_raise(ERR_LIB_X509V3, 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 == NULL) + 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 == NULL) + 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 == NULL) { + 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 == NULL) + 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: + ERR_raise(ERR_LIB_X509V3, 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 ossl_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 { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NAME); + X509V3_conf_add_error_name_value(cnf); + goto err; + } + } + return idp; + + merr: + ERR_raise(ERR_LIB_X509V3, 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; +} + +/* Append any nameRelativeToCRLIssuer in dpn to iname, set in dpn->dpname */ +int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, const X509_NAME *iname) +{ + int i; + STACK_OF(X509_NAME_ENTRY) *frag; + X509_NAME_ENTRY *ne; + + if (dpn == NULL || dpn->type != 1) + return 1; + frag = dpn->name.relativename; + X509_NAME_free(dpn->dpname); /* just in case it was already set */ + dpn->dpname = X509_NAME_dup(iname); + if (dpn->dpname == NULL) + 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)) + goto err; + } + /* generate cached encoding of name */ + if (i2d_X509_NAME(dpn->dpname, NULL) >= 0) + return 1; + + err: + X509_NAME_free(dpn->dpname); + dpn->dpname = NULL; + return 0; +} diff --git a/crypto/x509/v3_enum.c b/crypto/x509/v3_enum.c new file mode 100644 index 000000000000..b73a6d55162b --- /dev/null +++ b/crypto/x509/v3_enum.c @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2021 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 ossl_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 000000000000..4f2a86bdcb2b --- /dev/null +++ b/crypto/x509/v3_extku.c @@ -0,0 +1,102 @@ +/* + * Copyright 1999-2021 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 ossl_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 ossl_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) { + ERR_raise(ERR_LIB_X509V3, 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); + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER, + "%s", extval); + 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 000000000000..1f67bf2f63ab --- /dev/null +++ b/crypto/x509/v3_genn.c @@ -0,0 +1,241 @@ +/* + * Copyright 1999-2023 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) = { + /* DirectoryString is a CHOICE type so use explicit tagging */ + ASN1_EXP_OPT(EDIPARTYNAME, nameAssigner, DIRECTORYSTRING, 0), + ASN1_EXP(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); +} + +static int edipartyname_cmp(const EDIPARTYNAME *a, const EDIPARTYNAME *b) +{ + int res; + + if (a == NULL || b == NULL) { + /* + * Shouldn't be possible in a valid GENERAL_NAME, but we handle it + * anyway. OTHERNAME_cmp treats NULL != NULL so we do the same here + */ + return -1; + } + if (a->nameAssigner == NULL && b->nameAssigner != NULL) + return -1; + if (a->nameAssigner != NULL && b->nameAssigner == NULL) + return 1; + /* If we get here then both have nameAssigner set, or both unset */ + if (a->nameAssigner != NULL) { + res = ASN1_STRING_cmp(a->nameAssigner, b->nameAssigner); + if (res != 0) + return res; + } + /* + * partyName is required, so these should never be NULL. We treat it in + * the same way as the a == NULL || b == NULL case above + */ + if (a->partyName == NULL || b->partyName == NULL) + return -1; + + return ASN1_STRING_cmp(a->partyName, b->partyName); +} + +/* 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: + result = ASN1_STRING_cmp(a->d.x400Address, b->d.x400Address); + break; + + case GEN_EDIPARTY: + result = edipartyname_cmp(a->d.ediPartyName, b->d.ediPartyName); + 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: + a->d.x400Address = value; + break; + + case GEN_EDIPARTY: + a->d.ediPartyName = 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: + return a->d.x400Address; + + case GEN_EDIPARTY: + return a->d.ediPartyName; + + 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 000000000000..6722b6c01f05 --- /dev/null +++ b/crypto/x509/v3_ia5.c @@ -0,0 +1,64 @@ +/* + * Copyright 1999-2021 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 ossl_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 == NULL || ia5->length <= 0) + return NULL; + if ((tmp = OPENSSL_malloc(ia5->length + 1)) == NULL) { + ERR_raise(ERR_LIB_X509V3, 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 == NULL) { + ERR_raise(ERR_LIB_X509V3, 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: + ERR_raise(ERR_LIB_X509V3, 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 000000000000..5f21ce11e7d5 --- /dev/null +++ b/crypto/x509/v3_info.c @@ -0,0 +1,156 @@ +/* + * Copyright 1999-2021 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 ossl_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 ossl_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: + ERR_raise(ERR_LIB_X509V3, 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; + const int num = sk_CONF_VALUE_num(nval); + char *objtmp, *ptmp; + + if ((ainfo = sk_ACCESS_DESCRIPTION_new_reserve(NULL, num)) == NULL) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + sk_ACCESS_DESCRIPTION_push(ainfo, acc); /* Cannot fail due to reserve */ + ptmp = strchr(cnf->name, ';'); + if (ptmp == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_SYNTAX); + goto err; + } + 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, ptmp - cnf->name)) == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + acc->method = OBJ_txt2obj(objtmp, 0); + if (!acc->method) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_BAD_OBJECT, + "value=%s", 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 000000000000..dae26a5ba9d7 --- /dev/null +++ b/crypto/x509/v3_int.c @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2021 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 ossl_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 ossl_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 ossl_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_ist.c b/crypto/x509/v3_ist.c new file mode 100644 index 000000000000..96c40a3961a2 --- /dev/null +++ b/crypto/x509/v3_ist.c @@ -0,0 +1,152 @@ +/* + * Copyright 2020-2024 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" + +/* + * Issuer Sign Tool (1.2.643.100.112) The name of the tool used to signs the subject (ASN1_SEQUENCE) + * This extention is required to obtain the status of a qualified certificate at Russian Federation. + * RFC-style description is available here: https://tools.ietf.org/html/draft-deremin-rfc4491-bis-04#section-5 + * Russian Federal Law 63 "Digital Sign" is available here: http://www.consultant.ru/document/cons_doc_LAW_112701/ + */ + +ASN1_SEQUENCE(ISSUER_SIGN_TOOL) = { + ASN1_SIMPLE(ISSUER_SIGN_TOOL, signTool, ASN1_UTF8STRING), + ASN1_SIMPLE(ISSUER_SIGN_TOOL, cATool, ASN1_UTF8STRING), + ASN1_SIMPLE(ISSUER_SIGN_TOOL, signToolCert, ASN1_UTF8STRING), + ASN1_SIMPLE(ISSUER_SIGN_TOOL, cAToolCert, ASN1_UTF8STRING) +} ASN1_SEQUENCE_END(ISSUER_SIGN_TOOL) + +IMPLEMENT_ASN1_FUNCTIONS(ISSUER_SIGN_TOOL) + + +static ISSUER_SIGN_TOOL *v2i_issuer_sign_tool(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + STACK_OF(CONF_VALUE) *nval) +{ + ISSUER_SIGN_TOOL *ist = ISSUER_SIGN_TOOL_new(); + int i; + + if (ist == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return NULL; + } + for (i = 0; i < sk_CONF_VALUE_num(nval); ++i) { + CONF_VALUE *cnf = sk_CONF_VALUE_value(nval, i); + + if (cnf == NULL) { + continue; + } + if (strcmp(cnf->name, "signTool") == 0) { + ist->signTool = ASN1_UTF8STRING_new(); + if (ist->signTool == NULL + || cnf->value == NULL + || !ASN1_STRING_set(ist->signTool, cnf->value, strlen(cnf->value))) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + } else if (strcmp(cnf->name, "cATool") == 0) { + ist->cATool = ASN1_UTF8STRING_new(); + if (ist->cATool == NULL + || cnf->value == NULL + || !ASN1_STRING_set(ist->cATool, cnf->value, strlen(cnf->value))) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + } else if (strcmp(cnf->name, "signToolCert") == 0) { + ist->signToolCert = ASN1_UTF8STRING_new(); + if (ist->signToolCert == NULL + || cnf->value == NULL + || !ASN1_STRING_set(ist->signToolCert, cnf->value, strlen(cnf->value))) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + } else if (strcmp(cnf->name, "cAToolCert") == 0) { + ist->cAToolCert = ASN1_UTF8STRING_new(); + if (ist->cAToolCert == NULL + || cnf->value == NULL + || !ASN1_STRING_set(ist->cAToolCert, cnf->value, strlen(cnf->value))) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + } else { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_INVALID_ARGUMENT); + goto err; + } + } + return ist; + +err: + ISSUER_SIGN_TOOL_free(ist); + return NULL; +} + +static int i2r_issuer_sign_tool(X509V3_EXT_METHOD *method, + ISSUER_SIGN_TOOL *ist, BIO *out, + int indent) +{ + int new_line = 0; + + if (ist == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + if (ist->signTool != NULL) { + if (new_line == 1) { + BIO_write(out, "\n", 1); + } + BIO_printf(out, "%*ssignTool : ", indent, ""); + BIO_write(out, ist->signTool->data, ist->signTool->length); + new_line = 1; + } + if (ist->cATool != NULL) { + if (new_line == 1) { + BIO_write(out, "\n", 1); + } + BIO_printf(out, "%*scATool : ", indent, ""); + BIO_write(out, ist->cATool->data, ist->cATool->length); + new_line = 1; + } + if (ist->signToolCert != NULL) { + if (new_line == 1) { + BIO_write(out, "\n", 1); + } + BIO_printf(out, "%*ssignToolCert: ", indent, ""); + BIO_write(out, ist->signToolCert->data, ist->signToolCert->length); + new_line = 1; + } + if (ist->cAToolCert != NULL) { + if (new_line == 1) { + BIO_write(out, "\n", 1); + } + BIO_printf(out, "%*scAToolCert : ", indent, ""); + BIO_write(out, ist->cAToolCert->data, ist->cAToolCert->length); + new_line = 1; + } + return 1; +} + +const X509V3_EXT_METHOD ossl_v3_issuer_sign_tool = { + NID_issuerSignTool, /* nid */ + X509V3_EXT_MULTILINE, /* flags */ + ASN1_ITEM_ref(ISSUER_SIGN_TOOL), /* template */ + 0, 0, 0, 0, /* old functions, ignored */ + 0, /* i2s */ + 0, /* s2i */ + 0, /* i2v */ + (X509V3_EXT_V2I)v2i_issuer_sign_tool, /* v2i */ + (X509V3_EXT_I2R)i2r_issuer_sign_tool, /* i2r */ + 0, /* r2i */ + NULL /* extension-specific data */ +}; diff --git a/crypto/x509/v3_lib.c b/crypto/x509/v3_lib.c new file mode 100644 index 000000000000..6d91df99550f --- /dev/null +++ b/crypto/x509/v3_lib.c @@ -0,0 +1,308 @@ +/* + * Copyright 1999-2025 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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return 0; + } + if (!sk_X509V3_EXT_METHOD_push(ext_list, ext)) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_NOT_FOUND); + return 0; + } + if ((tmpext = OPENSSL_malloc(sizeof(*tmpext))) == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return 0; + } + *tmpext = *ext; + tmpext->ext_nid = nid_to; + tmpext->ext_flags |= X509V3_EXT_DYNAMIC; + if (!X509V3_EXT_add(tmpext)) { + OPENSSL_free(tmpext); + return 0; + } + return 1; +} + +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) { + extmp = sk_X509_EXTENSION_delete(*x, extidx); + if (extmp == NULL) + return -1; + X509_EXTENSION_free(extmp); + 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) { + ERR_raise(ERR_LIB_X509V3, 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: + /* ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); */ + if (ret != *x) + sk_X509_EXTENSION_free(ret); + X509_EXTENSION_free(ext); + return -1; + + err: + if (!(flags & X509V3_ADD_SILENT)) + ERR_raise(ERR_LIB_X509V3, errcode); + return 0; +} diff --git a/crypto/x509/v3_ncons.c b/crypto/x509/v3_ncons.c new file mode 100644 index 000000000000..a51354e7fc4c --- /dev/null +++ b/crypto/x509/v3_ncons.c @@ -0,0 +1,834 @@ +/* + * Copyright 2003-2023 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 "crypto/asn1.h" +#include <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include <openssl/bn.h> + +#include "crypto/x509.h" +#include "crypto/punycode.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(int effective_type, GENERAL_NAME *sub, + GENERAL_NAME *gen); +static int nc_dn(const X509_NAME *sub, const 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_email_eai(ASN1_TYPE *emltype, ASN1_IA5STRING *base); +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 ossl_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) + + +#define IA5_OFFSET_LEN(ia5base, offset) \ + ((ia5base)->length - ((unsigned char *)(offset) - (ia5base)->data)) + +/* Like memchr but for ASN1_IA5STRING. Additionally you can specify the + * starting point to search from + */ +# define ia5memchr(str, start, c) memchr(start, c, IA5_OFFSET_LEN(str, start)) + +/* Like memrrchr but for ASN1_IA5STRING */ +static char *ia5memrchr(ASN1_IA5STRING *str, int c) +{ + int i; + + for (i = str->length; i > 0 && str->data[i - 1] != c; i--); + + if (i == 0) + return NULL; + + return (char *)&str->data[i - 1]; +} + +/* + * We cannot use strncasecmp here because that applies locale specific rules. It + * also doesn't work with ASN1_STRINGs that may have embedded NUL characters. + * 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; + } + } + + return 0; +} + +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 { + ERR_raise(ERR_LIB_X509V3, 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: + ERR_raise(ERR_LIB_X509V3, 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) +{ + /* ip->length should be 8 or 32 and len1 == len2 == 4 or len1 == len2 == 16 */ + int len1 = ip->length >= 16 ? 16 : ip->length >= 4 ? 4 : ip->length; + int len2 = ip->length - len1; + char *ip1 = ossl_ipaddr_to_asc(ip->data, len1); + char *ip2 = ossl_ipaddr_to_asc(ip->data + len1, len2); + int ret = ip1 != NULL && ip2 != NULL + && BIO_printf(bp, "IP:%s/%s", ip1, ip2) > 0; + + OPENSSL_free(ip1); + OPENSSL_free(ip2); + return ret; +} + +#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 (memchr(utf8_value, 0, utf8_length) != NULL) { + 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; + const 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; + int effective_type = gen->type; + + /* + * We need to compare not gen->type field but an "effective" type because + * the otherName field may contain EAI email address treated specially + * according to RFC 8398, section 6 + */ + if (effective_type == GEN_OTHERNAME && + (OBJ_obj2nid(gen->d.otherName->type_id) == NID_id_on_SmtpUTF8Mailbox)) { + effective_type = GEN_EMAIL; + } + + /* + * 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 (effective_type != sub->base->type + || (effective_type == GEN_OTHERNAME && + OBJ_cmp(gen->d.otherName->type_id, + sub->base->d.otherName->type_id) != 0)) + 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(effective_type, 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 (effective_type != sub->base->type + || (effective_type == GEN_OTHERNAME && + OBJ_cmp(gen->d.otherName->type_id, + sub->base->d.otherName->type_id) != 0)) + continue; + if (!nc_minmax_valid(sub)) + return X509_V_ERR_SUBTREE_MINMAX; + + r = nc_match_single(effective_type, 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(int effective_type, GENERAL_NAME *gen, + GENERAL_NAME *base) +{ + switch (gen->type) { + case GEN_OTHERNAME: + switch (effective_type) { + case GEN_EMAIL: + /* + * We are here only when we have SmtpUTF8 name, + * so we match the value of othername with base->d.rfc822Name + */ + return nc_email_eai(gen->d.otherName->value, base->d.rfc822Name); + + default: + return X509_V_ERR_UNSUPPORTED_CONSTRAINT_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(const X509_NAME *nm, const 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 (base->length == 0) + return X509_V_OK; + + if (dns->length < base->length) + return X509_V_ERR_PERMITTED_VIOLATION; + + /* + * 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 (ia5ncasecmp(baseptr, dnsptr, base->length)) + return X509_V_ERR_PERMITTED_VIOLATION; + + return X509_V_OK; + +} + +/* + * This function implements comparison between ASCII/U-label in emltype + * and A-label in base according to RFC 8398, section 6. + * Convert base to U-label and ASCII-parts of domain names, for base + * Octet-to-octet comparison of `emltype` and `base` hostname parts + * (ASCII-parts should be compared in case-insensitive manner) + */ +static int nc_email_eai(ASN1_TYPE *emltype, ASN1_IA5STRING *base) +{ + ASN1_UTF8STRING *eml; + char *baseptr = NULL; + const char *emlptr; + const char *emlat; + char ulabel[256]; + size_t size = sizeof(ulabel) - 1; + int ret = X509_V_OK; + size_t emlhostlen; + + /* We do not accept embedded NUL characters */ + if (base->length > 0 && memchr(base->data, 0, base->length) != NULL) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + /* 'base' may not be NUL terminated. Create a copy that is */ + baseptr = OPENSSL_strndup((char *)base->data, base->length); + if (baseptr == NULL) + return X509_V_ERR_OUT_OF_MEM; + + if (emltype->type != V_ASN1_UTF8STRING) { + ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + goto end; + } + + eml = emltype->value.utf8string; + emlptr = (char *)eml->data; + emlat = ia5memrchr(eml, '@'); + + if (emlat == NULL) { + ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + goto end; + } + + memset(ulabel, 0, sizeof(ulabel)); + /* Special case: initial '.' is RHS match */ + if (*baseptr == '.') { + ulabel[0] = '.'; + size -= 1; + if (ossl_a2ulabel(baseptr, ulabel + 1, &size) <= 0) { + ret = X509_V_ERR_UNSPECIFIED; + goto end; + } + + if ((size_t)eml->length > strlen(ulabel)) { + emlptr += eml->length - (strlen(ulabel)); + /* X509_V_OK */ + if (ia5ncasecmp(ulabel, emlptr, strlen(ulabel)) == 0) + goto end; + } + ret = X509_V_ERR_PERMITTED_VIOLATION; + goto end; + } + + if (ossl_a2ulabel(baseptr, ulabel, &size) <= 0) { + ret = X509_V_ERR_UNSPECIFIED; + goto end; + } + /* Just have hostname left to match: case insensitive */ + emlptr = emlat + 1; + emlhostlen = IA5_OFFSET_LEN(eml, emlptr); + if (emlhostlen != strlen(ulabel) + || ia5ncasecmp(ulabel, emlptr, emlhostlen) != 0) { + ret = X509_V_ERR_PERMITTED_VIOLATION; + goto end; + } + + end: + OPENSSL_free(baseptr); + return ret; +} + +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 = ia5memrchr(base, '@'); + const char *emlat = ia5memrchr(eml, '@'); + size_t basehostlen, emlhostlen; + + if (!emlat) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + /* Special case: initial '.' is RHS match */ + if (!baseat && base->length > 0 && (*baseptr == '.')) { + if (eml->length > base->length) { + emlptr += eml->length - base->length; + if (ia5ncasecmp(baseptr, emlptr, base->length) == 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; + if (memchr(baseptr, 0, baseat - baseptr) || + memchr(emlptr, 0, emlat - emlptr)) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + /* 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; + basehostlen = IA5_OFFSET_LEN(base, baseptr); + emlhostlen = IA5_OFFSET_LEN(eml, emlptr); + /* Just have hostname left to match: case insensitive */ + if (basehostlen != emlhostlen || ia5ncasecmp(baseptr, emlptr, emlhostlen)) + 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 = ia5memchr(uri, (char *)uri->data, ':'); + int hostlen; + + /* Check for foo:// and skip past it */ + if (p == NULL + || IA5_OFFSET_LEN(uri, p) < 3 + || 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 = ia5memchr(uri, hostptr, ':'); + /* Otherwise look for trailing slash */ + if (p == NULL) + p = ia5memchr(uri, hostptr, '/'); + + if (p == NULL) + hostlen = IA5_OFFSET_LEN(uri, hostptr); + else + hostlen = p - hostptr; + + if (hostlen == 0) + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + + /* Special case: initial '.' is RHS match */ + if (base->length > 0 && *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 000000000000..a931e01a9c92 --- /dev/null +++ b/crypto/x509/v3_pci.c @@ -0,0 +1,321 @@ +/* + * Copyright 2004-2021 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 ossl_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->length, + 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED); + X509V3_conf_err(val); + return 0; + } + if ((*language = OBJ_txt2obj(val->value, 0)) == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER); + X509V3_conf_err(val); + return 0; + } + } else if (strcmp(val->name, "pathlen") == 0) { + if (*pathlen) { + ERR_raise(ERR_LIB_X509V3, + X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED); + X509V3_conf_err(val); + return 0; + } + if (!X509V3_get_value_int(val, pathlen)) { + ERR_raise(ERR_LIB_X509V3, 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 == NULL) { + *policy = ASN1_OCTET_STRING_new(); + if (*policy == NULL) { + ERR_raise(ERR_LIB_X509V3, 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; + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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; + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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; + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + X509V3_conf_err(val); + goto err; + } + } else { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG); + X509V3_conf_err(val); + goto err; + } + if (!tmp_data) { + ERR_raise(ERR_LIB_X509V3, 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)) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, + X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED); + goto err; + } + i = OBJ_obj2nid(language); + if ((i == NID_Independent || i == NID_id_ppl_inheritAll) && policy) { + ERR_raise(ERR_LIB_X509V3, + X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY); + goto err; + } + + pci = PROXY_CERT_INFO_EXTENSION_new(); + if (pci == NULL) { + ERR_raise(ERR_LIB_X509V3, 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 000000000000..7f5985f5e859 --- /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 000000000000..128365f572e2 --- /dev/null +++ b/crypto/x509/v3_pcons.c @@ -0,0 +1,91 @@ +/* + * Copyright 2003-2021 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 ossl_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) { + ERR_raise(ERR_LIB_X509V3, 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 { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_INVALID_NAME, + "%s", val->name); + goto err; + } + } + if (pcons->inhibitPolicyMapping == NULL + && pcons->requireExplicitPolicy == NULL) { + ERR_raise(ERR_LIB_X509V3, 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 000000000000..8f7e7d681300 --- /dev/null +++ b/crypto/x509/v3_pku.c @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2021 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 ossl_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 000000000000..2094e9671141 --- /dev/null +++ b/crypto/x509/v3_pmaps.c @@ -0,0 +1,110 @@ +/* + * Copyright 2003-2021 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 ossl_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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (i = 0; i < num; i++) { + val = sk_CONF_VALUE_value(nval, i); + if (!val->value || !val->name) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER, + "%s", val->name); + goto err; + } + obj1 = OBJ_txt2obj(val->name, 0); + obj2 = OBJ_txt2obj(val->value, 0); + if (!obj1 || !obj2) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER, + "%s", val->name); + goto err; + } + pmap = POLICY_MAPPING_new(); + if (pmap == NULL) { + ERR_raise(ERR_LIB_X509V3, 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 000000000000..1e4516a713c2 --- /dev/null +++ b/crypto/x509/v3_prn.c @@ -0,0 +1,216 @@ +/* + * Copyright 1999-2020 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); + obj = X509_EXTENSION_get_object(ex); + if ((flag & X509_FLAG_EXTENSIONS_ONLY_KID) != 0 + && OBJ_obj2nid(obj) != NID_subject_key_identifier + && OBJ_obj2nid(obj) != NID_authority_key_identifier) + continue; + if (indent && BIO_printf(bp, "%*s", indent, "") <= 0) + return 0; + 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 000000000000..6461189179f4 --- /dev/null +++ b/crypto/x509/v3_purp.c @@ -0,0 +1,1066 @@ +/* + * Copyright 1999-2023 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 "crypto/x509.h" +#include "internal/tsan_assist.h" +#include "x509_local.h" + +static int check_ssl_ca(const X509 *x); +static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int purpose_smime(const X509 *x, int require_ca); +static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int no_check_purpose(const X509_PURPOSE *xp, const X509 *x, + int require_ca); +static int check_purpose_ocsp_helper(const X509_PURPOSE *xp, const X509 *x, + int require_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_purpose, + "Any Purpose", "any", + NULL}, + {X509_PURPOSE_OCSP_HELPER, X509_TRUST_COMPAT, 0, check_purpose_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. + * If id == -1 it just calls x509v3_cache_extensions() for its side-effect. + * Returns 1 on success, 0 if x does not allow purpose, -1 on (internal) error. + */ +int X509_check_purpose(X509 *x, int id, int require_ca) +{ + int idx; + const X509_PURPOSE *pt; + + if (!ossl_x509v3_cache_extensions(x)) + return -1; + 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, require_ca); +} + +int X509_PURPOSE_set(int *p, int purpose) +{ + if (X509_PURPOSE_get_by_id(purpose) == -1) { + ERR_raise(ERR_LIB_X509V3, 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; +} + +/* Returns -1 on error, else an index => 0 in standard/extended purpose table */ +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) { + ERR_raise(ERR_LIB_X509V3, 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 == NULL|| ptmp->sname == NULL) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + if (!sk_X509_PURPOSE_push(xptable, ptmp)) { + ERR_raise(ERR_LIB_X509V3, 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 == NULL) + 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_id_pkix_OCSP_noCheck, /* 369 */ + 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; +} + +/* Returns 1 on success, 0 if x is invalid, -1 on (internal) error. */ +static int setup_dp(const X509 *x, DIST_POINT *dp) +{ + const X509_NAME *iname = NULL; + int i; + + if (dp->distpoint == NULL && sk_GENERAL_NAME_num(dp->CRLissuer) <= 0) { + ERR_raise(ERR_LIB_X509, X509_R_INVALID_DISTPOINT); + return 0; + } + if (dp->reasons != NULL) { + 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 == NULL || dp->distpoint->type != 1) + return 1; + + /* Handle name fragment given by nameRelativeToCRLIssuer */ + /* + * Note that the below way of determining iname is not really compliant + * with https://tools.ietf.org/html/rfc5280#section-4.2.1.13 + * According to it, sk_GENERAL_NAME_num(dp->CRLissuer) MUST be <= 1 + * and any CRLissuer could be of type different to GEN_DIRNAME. + */ + 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 == NULL) + iname = X509_get_issuer_name(x); + return DIST_POINT_set_dpname(dp->distpoint, iname) ? 1 : -1; +} + +/* Return 1 on success, 0 if x is invalid, -1 on (internal) error. */ +static int setup_crldp(X509 *x) +{ + int i; + + x->crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &i, NULL); + if (x->crldp == NULL && i != -1) + return 0; + + for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++) { + int res = setup_dp(x, sk_DIST_POINT_value(x->crldp, i)); + + if (res < 1) + return res; + } + return 1; +} + +/* Check that issuer public key algorithm matches subject signature algorithm */ +static int check_sig_alg_match(const EVP_PKEY *issuer_key, const X509 *subject) +{ + int subj_sig_nid; + + if (issuer_key == NULL) + return X509_V_ERR_NO_ISSUER_PUBLIC_KEY; + if (OBJ_find_sigid_algs(OBJ_obj2nid(subject->cert_info.signature.algorithm), + NULL, &subj_sig_nid) == 0) + return X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM; + if (EVP_PKEY_is_a(issuer_key, OBJ_nid2sn(subj_sig_nid)) + || (EVP_PKEY_is_a(issuer_key, "RSA") && subj_sig_nid == NID_rsassaPss)) + return X509_V_OK; + return X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH; +} + +#define V1_ROOT (EXFLAG_V1|EXFLAG_SS) +#define ku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_KUSAGE) != 0 && ((x)->ex_kusage & (usage)) == 0) +#define xku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_XKUSAGE) != 0 && ((x)->ex_xkusage & (usage)) == 0) +#define ns_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_NSCERT) != 0 && ((x)->ex_nscert & (usage)) == 0) + +/* + * Cache info on various X.509v3 extensions and further derived information, + * e.g., if cert 'x' is self-issued, in x->ex_flags and other internal fields. + * x->sha1_hash is filled in, or else EXFLAG_NO_FINGERPRINT is set in x->flags. + * X509_SIG_INFO_VALID is set in x->flags if x->siginf was filled successfully. + * Set EXFLAG_INVALID and return 0 in case the certificate is invalid. + */ +int ossl_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; + int i; + int res; + +#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 (x->ex_flags & EXFLAG_INVALID) == 0; +#endif + + if (!CRYPTO_THREAD_write_lock(x->lock)) + return 0; + if (x->ex_flags & EXFLAG_SET) { /* Cert has already been processed */ + CRYPTO_THREAD_unlock(x->lock); + return (x->ex_flags & EXFLAG_INVALID) == 0; + } + + /* Cache the SHA1 digest of the cert */ + if (!X509_digest(x, EVP_sha1(), x->sha1_hash, NULL)) + x->ex_flags |= EXFLAG_NO_FINGERPRINT; + + ERR_set_mark(); + + /* V1 should mean no extensions ... */ + if (X509_get_version(x) == X509_VERSION_1) + x->ex_flags |= EXFLAG_V1; + + /* Handle basic constraints */ + x->ex_pathlen = -1; + if ((bs = X509_get_ext_d2i(x, NID_basic_constraints, &i, NULL)) != NULL) { + if (bs->ca) + x->ex_flags |= EXFLAG_CA; + if (bs->pathlen != NULL) { + /* + * The error case !bs->ca is checked by check_chain() + * in case ctx->param->flags & X509_V_FLAG_X509_STRICT + */ + if (bs->pathlen->type == V_ASN1_NEG_INTEGER) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NEGATIVE_PATHLEN); + x->ex_flags |= EXFLAG_INVALID; + } else { + x->ex_pathlen = ASN1_INTEGER_get(bs->pathlen); + } + } + BASIC_CONSTRAINTS_free(bs); + x->ex_flags |= EXFLAG_BCONS; + } else if (i != -1) { + x->ex_flags |= EXFLAG_INVALID; + } + + /* Handle proxy certificates */ + if ((pci = X509_get_ext_d2i(x, NID_proxyCertInfo, &i, 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 != NULL) + x->ex_pcpathlen = ASN1_INTEGER_get(pci->pcPathLengthConstraint); + else + x->ex_pcpathlen = -1; + PROXY_CERT_INFO_EXTENSION_free(pci); + x->ex_flags |= EXFLAG_PROXY; + } else if (i != -1) { + x->ex_flags |= EXFLAG_INVALID; + } + + /* Handle (basic) key usage */ + if ((usage = X509_get_ext_d2i(x, NID_key_usage, &i, NULL)) != NULL) { + x->ex_kusage = 0; + if (usage->length > 0) { + x->ex_kusage = usage->data[0]; + if (usage->length > 1) + x->ex_kusage |= usage->data[1] << 8; + } + x->ex_flags |= EXFLAG_KUSAGE; + ASN1_BIT_STRING_free(usage); + /* Check for empty key usage according to RFC 5280 section 4.2.1.3 */ + if (x->ex_kusage == 0) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_EMPTY_KEY_USAGE); + x->ex_flags |= EXFLAG_INVALID; + } + } else if (i != -1) { + x->ex_flags |= EXFLAG_INVALID; + } + + /* Handle extended key usage */ + x->ex_xkusage = 0; + if ((extusage = X509_get_ext_d2i(x, NID_ext_key_usage, &i, 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; + default: + /* Ignore unknown extended key usage */ + break; + } + } + sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free); + } else if (i != -1) { + x->ex_flags |= EXFLAG_INVALID; + } + + /* Handle legacy Netscape extension */ + if ((ns = X509_get_ext_d2i(x, NID_netscape_cert_type, &i, 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); + } else if (i != -1) { + x->ex_flags |= EXFLAG_INVALID; + } + + /* Handle subject key identifier and issuer/authority key identifier */ + x->skid = X509_get_ext_d2i(x, NID_subject_key_identifier, &i, NULL); + if (x->skid == NULL && i != -1) + x->ex_flags |= EXFLAG_INVALID; + + x->akid = X509_get_ext_d2i(x, NID_authority_key_identifier, &i, NULL); + if (x->akid == NULL && i != -1) + x->ex_flags |= EXFLAG_INVALID; + + /* Check if subject name matches issuer */ + if (X509_NAME_cmp(X509_get_subject_name(x), X509_get_issuer_name(x)) == 0) { + x->ex_flags |= EXFLAG_SI; /* Cert is self-issued */ + if (X509_check_akid(x, x->akid) == X509_V_OK /* SKID matches AKID */ + /* .. and the signature alg matches the PUBKEY alg: */ + && check_sig_alg_match(X509_get0_pubkey(x), x) == X509_V_OK) + x->ex_flags |= EXFLAG_SS; /* indicate self-signed */ + /* This is very related to ossl_x509_likely_issued(x, x) == X509_V_OK */ + } + + /* Handle subject alternative names and various other extensions */ + x->altname = X509_get_ext_d2i(x, NID_subject_alt_name, &i, NULL); + if (x->altname == NULL && i != -1) + x->ex_flags |= EXFLAG_INVALID; + x->nc = X509_get_ext_d2i(x, NID_name_constraints, &i, NULL); + if (x->nc == NULL && i != -1) + x->ex_flags |= EXFLAG_INVALID; + + /* Handle CRL distribution point entries */ + res = setup_crldp(x); + if (res == 0) + x->ex_flags |= EXFLAG_INVALID; + else if (res < 0) + goto err; + +#ifndef OPENSSL_NO_RFC3779 + x->rfc3779_addr = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &i, NULL); + if (x->rfc3779_addr == NULL && i != -1) + x->ex_flags |= EXFLAG_INVALID; + x->rfc3779_asid = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, &i, NULL); + if (x->rfc3779_asid == NULL && i != -1) + x->ex_flags |= EXFLAG_INVALID; +#endif + for (i = 0; i < X509_get_ext_count(x); i++) { + X509_EXTENSION *ex = X509_get_ext(x, i); + int nid = OBJ_obj2nid(X509_EXTENSION_get_object(ex)); + + if (nid == 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; + } + switch (nid) { + case NID_basic_constraints: + x->ex_flags |= EXFLAG_BCONS_CRITICAL; + break; + case NID_authority_key_identifier: + x->ex_flags |= EXFLAG_AKID_CRITICAL; + break; + case NID_subject_key_identifier: + x->ex_flags |= EXFLAG_SKID_CRITICAL; + break; + case NID_subject_alt_name: + x->ex_flags |= EXFLAG_SAN_CRITICAL; + break; + default: + break; + } + } + + /* Set x->siginf, ignoring errors due to unsupported algos */ + (void)ossl_x509_init_sig_info(x); + + x->ex_flags |= EXFLAG_SET; /* Indicate that cert has been processed */ +#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 + ERR_pop_to_mark(); + if ((x->ex_flags & (EXFLAG_INVALID | EXFLAG_NO_FINGERPRINT)) == 0) { + CRYPTO_THREAD_unlock(x->lock); + return 1; + } + if ((x->ex_flags & EXFLAG_INVALID) != 0) + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_CERTIFICATE); + /* If computing sha1_hash failed the error queue already reflects this. */ + + err: + x->ex_flags |= EXFLAG_SET; /* indicate that cert has been processed */ + CRYPTO_THREAD_unlock(x->lock); + return 0; +} + +/*- + * CA checks common to all purposes + * return codes: + * 0 not a CA + * 1 is a CA + * 2 Only possible in older versions of openSSL when basicConstraints are absent + * new versions will not return this value. May be a CA + * 3 basicConstraints absent but self-signed V1. + * 4 basicConstraints absent but keyUsage present and keyCertSign asserted. + * 5 Netscape specific CA Flags present + */ + +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) != 0) { + /* If basicConstraints says not a CA then say so */ + return (x->ex_flags & EXFLAG_CA) != 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) +{ + if (CRYPTO_THREAD_write_lock(x->lock)) { + x->ex_flags |= EXFLAG_PROXY; + CRYPTO_THREAD_unlock(x->lock); + } +} + +void X509_set_proxy_pathlen(X509 *x, long l) +{ + x->ex_pcpathlen = l; +} + +int X509_check_ca(X509 *x) +{ + /* Note 0 normally means "not a CA" - but in this case means error. */ + if (!ossl_x509v3_cache_extensions(x)) + return 0; + + 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 = check_ca(x); + + if (ca_ret == 0) + return 0; + /* Check nsCertType if present */ + return ca_ret != 5 || (x->ex_nscert & NS_SSL_CA) != 0; +} + +static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, + int require_ca) +{ + if (xku_reject(x, XKU_SSL_CLIENT)) + return 0; + if (require_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 require_ca) +{ + if (xku_reject(x, XKU_SSL_SERVER | XKU_SGC)) + return 0; + if (require_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 require_ca) +{ + int ret; + ret = check_purpose_ssl_server(xp, x, require_ca); + if (!ret || require_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 require_ca) +{ + if (xku_reject(x, XKU_SMIME)) + return 0; + if (require_ca) { + int ca_ret; + ca_ret = check_ca(x); + if (ca_ret == 0) + 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 require_ca) +{ + int ret; + ret = purpose_smime(x, require_ca); + if (!ret || require_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 require_ca) +{ + int ret; + ret = purpose_smime(x, require_ca); + if (!ret || require_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 require_ca) +{ + if (require_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 check_purpose_ocsp_helper(const X509_PURPOSE *xp, const X509 *x, + int require_ca) +{ + /* + * Must be a valid CA. Should we really support the "I don't know" value + * (2)? + */ + if (require_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 require_ca) +{ + int i_ext; + + /* If ca is true we must return if this is a valid CA certificate. */ + if (require_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_purpose(const X509_PURPOSE *xp, const X509 *x, + int require_ca) +{ + return 1; +} + +/*- + * Various checks to see if one certificate potentially 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. issuer_name(subject) == subject_name(issuer) + * 2. If akid(subject) exists, it matches the respective issuer fields. + * 3. subject signature algorithm == issuer public key algorithm + * 4. If key_usage(issuer) exists, it allows for signing subject. + * Note that this does not include actually checking the signature. + * Returns 0 for OK, or positive for reason for mismatch + * where reason codes match those for X509_verify_cert(). + */ +int X509_check_issued(X509 *issuer, X509 *subject) +{ + int ret; + + if ((ret = ossl_x509_likely_issued(issuer, subject)) != X509_V_OK) + return ret; + return ossl_x509_signing_allowed(issuer, subject); +} + +/* do the checks 1., 2., and 3. as described above for X509_check_issued() */ +int ossl_x509_likely_issued(X509 *issuer, X509 *subject) +{ + int ret; + + if (X509_NAME_cmp(X509_get_subject_name(issuer), + X509_get_issuer_name(subject)) != 0) + return X509_V_ERR_SUBJECT_ISSUER_MISMATCH; + + /* set issuer->skid and subject->akid */ + if (!ossl_x509v3_cache_extensions(issuer) + || !ossl_x509v3_cache_extensions(subject)) + return X509_V_ERR_UNSPECIFIED; + + ret = X509_check_akid(issuer, subject->akid); + if (ret != X509_V_OK) + return ret; + + /* Check if the subject signature alg matches the issuer's PUBKEY alg */ + return check_sig_alg_match(X509_get0_pubkey(issuer), subject); +} + +/*- + * Check if certificate I<issuer> is allowed to issue certificate I<subject> + * according to the B<keyUsage> field of I<issuer> if present + * depending on any proxyCertInfo extension of I<subject>. + * Returns 0 for OK, or positive for reason for rejection + * where reason codes match those for X509_verify_cert(). + */ +int ossl_x509_signing_allowed(const X509 *issuer, const X509 *subject) +{ + 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(const X509 *issuer, const AUTHORITY_KEYID *akid) +{ + if (akid == NULL) + 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_get0_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 != NULL && X509_NAME_cmp(nm, X509_get_issuer_name(issuer)) != 0) + 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, 0); + return x->ex_flags; +} + +uint32_t X509_get_key_usage(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + if (X509_check_purpose(x, -1, 0) != 1) + return 0; + 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 */ + if (X509_check_purpose(x, -1, 0) != 1) + return 0; + 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 */ + if (X509_check_purpose(x, -1, 0) != 1) + return NULL; + return x->skid; +} + +const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + if (X509_check_purpose(x, -1, 0) != 1) + return NULL; + return (x->akid != NULL ? x->akid->keyid : NULL); +} + +const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + if (X509_check_purpose(x, -1, 0) != 1) + return NULL; + return (x->akid != NULL ? x->akid->issuer : NULL); +} + +const ASN1_INTEGER *X509_get0_authority_serial(X509 *x) +{ + /* Call for side-effect of computing hash and caching extensions */ + if (X509_check_purpose(x, -1, 0) != 1) + return NULL; + return (x->akid != NULL ? x->akid->serial : NULL); +} + +long X509_get_pathlen(X509 *x) +{ + /* Called for side effect of caching extensions */ + if (X509_check_purpose(x, -1, 0) != 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, 0) != 1 + || (x->ex_flags & EXFLAG_PROXY) == 0) + return -1; + return x->ex_pcpathlen; +} diff --git a/crypto/x509/v3_san.c b/crypto/x509/v3_san.c new file mode 100644 index 000000000000..d4999f1fc6c7 --- /dev/null +++ b/crypto/x509/v3_san.c @@ -0,0 +1,699 @@ +/* + * Copyright 1999-2021 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 "crypto/x509.h" +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include <openssl/bio.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 ossl_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; + STACK_OF(CONF_VALUE) *tmpret = NULL, *origret = ret; + + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gen = sk_GENERAL_NAME_value(gens, i); + /* + * i2v_GENERAL_NAME allocates ret if it is NULL. If something goes + * wrong we need to free the stack - but only if it was empty when we + * originally entered this function. + */ + tmpret = i2v_GENERAL_NAME(method, gen, ret); + if (tmpret == NULL) { + if (origret == NULL) + sk_CONF_VALUE_pop_free(ret, X509V3_conf_free); + return NULL; + } + ret = tmpret; + } + if (ret == NULL) + 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) +{ + char othername[300]; + char oline[256], *tmp; + + switch (gen->type) { + case GEN_OTHERNAME: + switch (OBJ_obj2nid(gen->d.otherName->type_id)) { + case NID_id_on_SmtpUTF8Mailbox: + if (gen->d.otherName->value->type != V_ASN1_UTF8STRING + || !x509v3_add_len_value_uchar("othername: SmtpUTF8Mailbox:", + gen->d.otherName->value->value.utf8string->data, + gen->d.otherName->value->value.utf8string->length, + &ret)) + return NULL; + break; + case NID_XmppAddr: + if (gen->d.otherName->value->type != V_ASN1_UTF8STRING + || !x509v3_add_len_value_uchar("othername: XmppAddr:", + gen->d.otherName->value->value.utf8string->data, + gen->d.otherName->value->value.utf8string->length, + &ret)) + return NULL; + break; + case NID_SRVName: + if (gen->d.otherName->value->type != V_ASN1_IA5STRING + || !x509v3_add_len_value_uchar("othername: SRVName:", + gen->d.otherName->value->value.ia5string->data, + gen->d.otherName->value->value.ia5string->length, + &ret)) + return NULL; + break; + case NID_ms_upn: + if (gen->d.otherName->value->type != V_ASN1_UTF8STRING + || !x509v3_add_len_value_uchar("othername: UPN:", + gen->d.otherName->value->value.utf8string->data, + gen->d.otherName->value->value.utf8string->length, + &ret)) + return NULL; + break; + case NID_NAIRealm: + if (gen->d.otherName->value->type != V_ASN1_UTF8STRING + || !x509v3_add_len_value_uchar("othername: NAIRealm:", + gen->d.otherName->value->value.utf8string->data, + gen->d.otherName->value->value.utf8string->length, + &ret)) + return NULL; + break; + default: + if (OBJ_obj2txt(oline, sizeof(oline), gen->d.otherName->type_id, 0) > 0) + BIO_snprintf(othername, sizeof(othername), "othername: %s:", + oline); + else + OPENSSL_strlcpy(othername, "othername:", sizeof(othername)); + + /* check if the value is something printable */ + if (gen->d.otherName->value->type == V_ASN1_IA5STRING) { + if (x509v3_add_len_value_uchar(othername, + gen->d.otherName->value->value.ia5string->data, + gen->d.otherName->value->value.ia5string->length, + &ret)) + return ret; + } + if (gen->d.otherName->value->type == V_ASN1_UTF8STRING) { + if (x509v3_add_len_value_uchar(othername, + gen->d.otherName->value->value.utf8string->data, + gen->d.otherName->value->value.utf8string->length, + &ret)) + return ret; + } + if (!X509V3_add_value(othername, "<unsupported>", &ret)) + return NULL; + break; + } + 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_len_value_uchar("email", gen->d.ia5->data, + gen->d.ia5->length, &ret)) + return NULL; + break; + + case GEN_DNS: + if (!x509v3_add_len_value_uchar("DNS", gen->d.ia5->data, + gen->d.ia5->length, &ret)) + return NULL; + break; + + case GEN_URI: + if (!x509v3_add_len_value_uchar("URI", gen->d.ia5->data, + gen->d.ia5->length, &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: + tmp = ossl_ipaddr_to_asc(gen->d.ip->data, gen->d.ip->length); + if (tmp == NULL || !X509V3_add_value("IP Address", tmp, &ret)) + ret = NULL; + OPENSSL_free(tmp); + 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) +{ + char *tmp; + int nid; + + switch (gen->type) { + case GEN_OTHERNAME: + nid = OBJ_obj2nid(gen->d.otherName->type_id); + /* Validate the types are as we expect before we use them */ + if ((nid == NID_SRVName + && gen->d.otherName->value->type != V_ASN1_IA5STRING) + || (nid != NID_SRVName + && gen->d.otherName->value->type != V_ASN1_UTF8STRING)) { + BIO_printf(out, "othername:<unsupported>"); + break; + } + + switch (nid) { + case NID_id_on_SmtpUTF8Mailbox: + BIO_printf(out, "othername:SmtpUTF8Mailbox:%.*s", + gen->d.otherName->value->value.utf8string->length, + gen->d.otherName->value->value.utf8string->data); + break; + case NID_XmppAddr: + BIO_printf(out, "othername:XmppAddr:%.*s", + gen->d.otherName->value->value.utf8string->length, + gen->d.otherName->value->value.utf8string->data); + break; + case NID_SRVName: + BIO_printf(out, "othername:SRVName:%.*s", + gen->d.otherName->value->value.ia5string->length, + gen->d.otherName->value->value.ia5string->data); + break; + case NID_ms_upn: + BIO_printf(out, "othername:UPN:%.*s", + gen->d.otherName->value->value.utf8string->length, + gen->d.otherName->value->value.utf8string->data); + break; + case NID_NAIRealm: + BIO_printf(out, "othername:NAIRealm:%.*s", + gen->d.otherName->value->value.utf8string->length, + gen->d.otherName->value->value.utf8string->data); + break; + default: + BIO_printf(out, "othername:<unsupported>"); + break; + } + 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:"); + ASN1_STRING_print(out, gen->d.ia5); + break; + + case GEN_DNS: + BIO_printf(out, "DNS:"); + ASN1_STRING_print(out, gen->d.ia5); + break; + + case GEN_URI: + BIO_printf(out, "URI:"); + ASN1_STRING_print(out, gen->d.ia5); + break; + + case GEN_DIRNAME: + BIO_printf(out, "DirName:"); + X509_NAME_print_ex(out, gen->d.dirn, 0, XN_FLAG_ONELINE); + break; + + case GEN_IPADD: + tmp = ossl_ipaddr_to_asc(gen->d.ip->data, gen->d.ip->length); + if (tmp == NULL) + return 0; + BIO_printf(out, "IP Address:%s", tmp); + OPENSSL_free(tmp); + 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) { + ERR_raise(ERR_LIB_X509V3, 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 (!ossl_v3_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 = NULL; + GENERAL_NAME *gen; + X509_EXTENSION *ext; + int i, num; + + if (ctx != NULL && (ctx->flags & X509V3_CTX_TEST) != 0) + return 1; + if (!ctx || !ctx->issuer_cert) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_ISSUER_DECODE_ERROR); + goto err; + } + + num = sk_GENERAL_NAME_num(ialt); + if (!sk_GENERAL_NAME_reserve(gens, num)) { + ERR_raise(ERR_LIB_X509V3, 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: + sk_GENERAL_NAME_free(ialt); + 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) { + ERR_raise(ERR_LIB_X509V3, 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 (ossl_v3_name_cmp(cnf->name, "email") == 0 + && cnf->value && strcmp(cnf->value, "copy") == 0) { + if (!copy_email(ctx, gens, 0)) + goto err; + } else if (ossl_v3_name_cmp(cnf->name, "email") == 0 + && 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 & X509V3_CTX_TEST) != 0) + return 1; + if (ctx == NULL + || (ctx->subject_cert == NULL && ctx->subject_req == NULL)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_SUBJECT_DETAILS); + return 0; + } + /* Find the subject name */ + nm = ctx->subject_cert != NULL ? + X509_get_subject_name(ctx->subject_cert) : + 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) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + gen->d.ia5 = email; + email = NULL; + gen->type = GEN_EMAIL; + if (!sk_GENERAL_NAME_push(gens, gen)) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_MISSING_VALUE); + return NULL; + } + + if (out) + gen = out; + else { + gen = GENERAL_NAME_new(); + if (gen == NULL) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_BAD_OBJECT, + "value=%s", 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) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_BAD_IP_ADDRESS, + "value=%s", value); + goto err; + } + break; + + case GEN_DIRNAME: + if (!do_dirname(gen, value, ctx)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_DIRNAME_ERROR); + goto err; + } + break; + + case GEN_OTHERNAME: + if (!do_othername(gen, value, ctx)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_OTHERNAME_ERROR); + goto err; + } + break; + default: + ERR_raise(ERR_LIB_X509V3, 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))) { + ASN1_IA5STRING_free(gen->d.ia5); + gen->d.ia5 = NULL; + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_MISSING_VALUE); + return NULL; + } + + if (!ossl_v3_name_cmp(name, "email")) + type = GEN_EMAIL; + else if (!ossl_v3_name_cmp(name, "URI")) + type = GEN_URI; + else if (!ossl_v3_name_cmp(name, "DNS")) + type = GEN_DNS; + else if (!ossl_v3_name_cmp(name, "RID")) + type = GEN_RID; + else if (!ossl_v3_name_cmp(name, "IP")) + type = GEN_IPADD; + else if (!ossl_v3_name_cmp(name, "dirName")) + type = GEN_DIRNAME; + else if (!ossl_v3_name_cmp(name, "otherName")) + type = GEN_OTHERNAME; + else { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_UNSUPPORTED_OPTION, + "name=%s", 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) + goto err; + objlen = p - value; + objtmp = OPENSSL_strndup(value, objlen); + if (objtmp == NULL) + goto err; + gen->d.otherName->type_id = OBJ_txt2obj(objtmp, 0); + OPENSSL_free(objtmp); + if (!gen->d.otherName->type_id) + goto err; + return 1; + + err: + OTHERNAME_free(gen->d.otherName); + gen->d.otherName = NULL; + return 0; +} + +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) { + ERR_raise_data(ERR_LIB_X509V3, X509V3_R_SECTION_NOT_FOUND, + "section=%s", 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_skid.c b/crypto/x509/v3_skid.c new file mode 100644 index 000000000000..18223f2ef496 --- /dev/null +++ b/crypto/x509/v3_skid.c @@ -0,0 +1,111 @@ +/* + * Copyright 1999-2021 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 "crypto/x509.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 ossl_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) { + ERR_raise(ERR_LIB_X509V3, 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; + +} + +ASN1_OCTET_STRING *ossl_x509_pubkey_hash(X509_PUBKEY *pubkey) +{ + ASN1_OCTET_STRING *oct; + const unsigned char *pk; + int pklen; + unsigned char pkey_dig[EVP_MAX_MD_SIZE]; + unsigned int diglen; + const char *propq; + OSSL_LIB_CTX *libctx; + EVP_MD *md; + + if (pubkey == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_PUBLIC_KEY); + return NULL; + } + if (!ossl_x509_PUBKEY_get0_libctx(&libctx, &propq, pubkey)) + return NULL; + if ((md = EVP_MD_fetch(libctx, SN_sha1, propq)) == NULL) + return NULL; + if ((oct = ASN1_OCTET_STRING_new()) == NULL) { + EVP_MD_free(md); + return NULL; + } + + X509_PUBKEY_get0_param(NULL, &pk, &pklen, NULL, pubkey); + if (EVP_Digest(pk, pklen, pkey_dig, &diglen, md, NULL) + && ASN1_OCTET_STRING_set(oct, pkey_dig, diglen)) { + EVP_MD_free(md); + return oct; + } + + EVP_MD_free(md); + ASN1_OCTET_STRING_free(oct); + return NULL; +} + +static ASN1_OCTET_STRING *s2i_skey_id(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, char *str) +{ + if (strcmp(str, "none") == 0) + return ASN1_OCTET_STRING_new(); /* dummy */ + + if (strcmp(str, "hash") != 0) + return s2i_ASN1_OCTET_STRING(method, ctx /* not used */, str); + + if (ctx != NULL && (ctx->flags & X509V3_CTX_TEST) != 0) + return ASN1_OCTET_STRING_new(); + if (ctx == NULL + || (ctx->subject_cert == NULL && ctx->subject_req == NULL)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_NO_SUBJECT_DETAILS); + return NULL; + } + + return ossl_x509_pubkey_hash(ctx->subject_cert != NULL ? + ctx->subject_cert->cert_info.key : + ctx->subject_req->req_info.pubkey); +} diff --git a/crypto/x509/v3_sxnet.c b/crypto/x509/v3_sxnet.c new file mode 100644 index 000000000000..70f5db636c4b --- /dev/null +++ b/crypto/x509/v3_sxnet.c @@ -0,0 +1,252 @@ +/* + * Copyright 1999-2024 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 ossl_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) +{ + int64_t v; + char *tmp; + SXNETID *id; + int i; + + /* + * Since we add 1 to the version number to display it, we don't support + * LONG_MAX since that would cause on overflow. + */ + if (!ASN1_INTEGER_get_int64(&v, sx->version) + || v >= LONG_MAX + || v < LONG_MIN) { + BIO_printf(out, "%*sVersion: <unsupported>", indent, ""); + } else { + long vl = (long)v; + + BIO_printf(out, "%*sVersion: %ld (0x%lX)", indent, "", vl + 1, vl); + } + for (i = 0; i < sk_SXNETID_num(sx->ids); i++) { + id = sk_SXNETID_value(sx->ids, i); + tmp = i2s_ASN1_INTEGER(NULL, id->zone); + if (tmp == NULL) + return 0; + 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)) { + SXNET_free(sx); + 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_ERROR_CONVERTING_ZONE); + return 0; + } + if (!SXNET_add_id_INTEGER(psx, izone, user, userlen)) { + ASN1_INTEGER_free(izone); + return 0; + } + return 1; +} + +/* 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)) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + ASN1_INTEGER_free(izone); + return 0; + } + if (!SXNET_add_id_INTEGER(psx, izone, user, userlen)) { + ASN1_INTEGER_free(izone); + return 0; + } + return 1; +} + +/* + * 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 == NULL || zone == NULL || user == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NULL_ARGUMENT); + return 0; + } + if (userlen == -1) + userlen = strlen(user); + if (userlen > 64) { + ERR_raise(ERR_LIB_X509V3, 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; + } else + sx = *psx; + if (SXNET_get_id_INTEGER(sx, zone)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_DUPLICATE_ZONE_ID); + if (*psx == NULL) + SXNET_free(sx); + 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; + ASN1_INTEGER_free(id->zone); + id->zone = zone; + *psx = sx; + return 1; + + err: + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + SXNETID_free(id); + if (*psx == NULL) + SXNET_free(sx); + 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) { + ERR_raise(ERR_LIB_X509V3, 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)) { + ERR_raise(ERR_LIB_X509V3, 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 000000000000..3a457fa57bee --- /dev/null +++ b/crypto/x509/v3_tlsf.c @@ -0,0 +1,140 @@ +/* + * Copyright 2015-2022 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 <openssl/asn1t.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include "ext_dat.h" +#include "x509_local.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 ossl_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 = NULL; + CONF_VALUE *val; + int i; + size_t j; + long tlsextid; + + if ((tlsf = sk_ASN1_INTEGER_new_null()) == NULL) { + ERR_raise(ERR_LIB_X509V3, 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 (OPENSSL_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)) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_SYNTAX); + X509V3_conf_add_error_name_value(val); + goto err; + } + } + + if ((ai = ASN1_INTEGER_new()) == NULL + || !ASN1_INTEGER_set(ai, tlsextid) + || sk_ASN1_INTEGER_push(tlsf, ai) <= 0) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + goto err; + } + /* So it doesn't get purged if an error occurs next time around */ + ai = NULL; + } + return tlsf; + + err: + sk_ASN1_INTEGER_pop_free(tlsf, ASN1_INTEGER_free); + ASN1_INTEGER_free(ai); + return NULL; +} diff --git a/crypto/x509/v3_utf8.c b/crypto/x509/v3_utf8.c new file mode 100644 index 000000000000..1c4f79c4cd9b --- /dev/null +++ b/crypto/x509/v3_utf8.c @@ -0,0 +1,68 @@ +/* + * Copyright 2020-2021 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" + +/* + * Subject Sign Tool (1.2.643.100.111) The name of the tool used to signs the subject (UTF8String) + * This extention is required to obtain the status of a qualified certificate at Russian Federation. + * RFC-style description is available here: https://tools.ietf.org/html/draft-deremin-rfc4491-bis-04#section-5 + * Russian Federal Law 63 "Digital Sign" is available here: http://www.consultant.ru/document/cons_doc_LAW_112701/ + */ + + +const X509V3_EXT_METHOD ossl_v3_utf8_list[1] = { + EXT_UTF8STRING(NID_subjectSignTool), +}; + +char *i2s_ASN1_UTF8STRING(X509V3_EXT_METHOD *method, + ASN1_UTF8STRING *utf8) +{ + char *tmp; + + if (utf8 == NULL || utf8->length == 0) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if ((tmp = OPENSSL_malloc(utf8->length + 1)) == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return NULL; + } + memcpy(tmp, utf8->data, utf8->length); + tmp[utf8->length] = 0; + return tmp; +} + +ASN1_UTF8STRING *s2i_ASN1_UTF8STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, const char *str) +{ + ASN1_UTF8STRING *utf8; + if (str == NULL) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NULL_ARGUMENT); + return NULL; + } + if ((utf8 = ASN1_UTF8STRING_new()) == NULL) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + return NULL; + } + if (!ASN1_STRING_set((ASN1_STRING *)utf8, str, strlen(str))) { + ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); + ASN1_UTF8STRING_free(utf8); + return NULL; + } +#ifdef CHARSET_EBCDIC + ebcdic2ascii(utf8->data, utf8->data, utf8->length); +#endif /* CHARSET_EBCDIC */ + return utf8; +} diff --git a/crypto/x509/v3_utl.c b/crypto/x509/v3_utl.c new file mode 100644 index 000000000000..56ee36d4521e --- /dev/null +++ b/crypto/x509/v3_utl.c @@ -0,0 +1,1386 @@ +/* + * Copyright 1999-2024 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 <string.h> +#include "crypto/ctype.h" +#include <openssl/conf.h> +#include <openssl/crypto.h> +#include <openssl/x509v3.h> +#include "crypto/x509.h" +#include <openssl/bn.h> +#include "ext_dat.h" +#include "x509_local.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(const 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 */ + +static int x509v3_add_len_value(const char *name, const char *value, + size_t vallen, STACK_OF(CONF_VALUE) **extlist) +{ + CONF_VALUE *vtmp = NULL; + char *tname = NULL, *tvalue = NULL; + int sk_allocated = (*extlist == NULL); + + if (name != NULL && (tname = OPENSSL_strdup(name)) == NULL) + goto err; + if (value != NULL) { + /* We don't allow embeded NUL characters */ + if (memchr(value, 0, vallen) != NULL) + goto err; + tvalue = OPENSSL_strndup(value, vallen); + if (tvalue == 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: + ERR_raise(ERR_LIB_X509V3, 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(const char *name, const char *value, + STACK_OF(CONF_VALUE) **extlist) +{ + return x509v3_add_len_value(name, value, + value != NULL ? strlen((const char *)value) : 0, + extlist); +} + +int X509V3_add_value_uchar(const char *name, const unsigned char *value, + STACK_OF(CONF_VALUE) **extlist) +{ + return x509v3_add_len_value(name, (const char *)value, + value != NULL ? strlen((const char *)value) : 0, + extlist); +} + +int x509v3_add_len_value_uchar(const char *name, const unsigned char *value, + size_t vallen, STACK_OF(CONF_VALUE) **extlist) +{ + return x509v3_add_len_value(name, (const char *)value, vallen, 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) { + ERR_raise(ERR_LIB_X509V3, 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) + ERR_raise(ERR_LIB_X509V3, 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) + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NULL_VALUE); + return NULL; + } + bn = BN_new(); + if (bn == NULL) { + ERR_raise(ERR_LIB_X509V3, 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); + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, 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: + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_BOOLEAN_STRING); + X509V3_conf_add_error_name_value(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_add_error_name_value(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) { + ERR_raise(ERR_LIB_X509V3, 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) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_EMPTY_NAME); + goto err; + } + q = p + 1; + } else if (c == ',') { + *p = 0; + ntmp = strip_spaces(q); + q = p + 1; + if (!ntmp) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_EMPTY_NAME); + goto err; + } + if (!X509V3_add_value(ntmp, NULL, &values)) { + goto err; + } + } + break; + + case HDR_VALUE: + if (c == ',') { + state = HDR_NAME; + *p = 0; + vtmp = strip_spaces(q); + if (!vtmp) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NULL_VALUE); + goto err; + } + if (!X509V3_add_value(ntmp, vtmp, &values)) { + goto err; + } + ntmp = NULL; + q = p + 1; + } + + } + } + + if (state == HDR_VALUE) { + vtmp = strip_spaces(q); + if (!vtmp) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_NULL_VALUE); + goto err; + } + if (!X509V3_add_value(ntmp, vtmp, &values)) { + goto err; + } + } else { + ntmp = strip_spaces(q); + if (!ntmp) { + ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_EMPTY_NAME); + goto err; + } + if (!X509V3_add_value(ntmp, NULL, &values)) { + goto err; + } + } + 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 == '\0') + return NULL; + q = p + strlen(p) - 1; + while ((q != p) && ossl_isspace(*q)) + q--; + if (p != q) + q[1] = 0; + if (*p == '\0') + return NULL; + return p; +} + + +/* + * V2I name comparison function: returns zero if 'name' matches cmp or cmp.* + */ + +int ossl_v3_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(const 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 == NULL || email->length == 0) + return 1; + if (memchr(email->data, 0, email->length) != NULL) + return 1; + if (*sk == NULL) + *sk = sk_OPENSSL_STRING_new(sk_strcmp); + if (*sk == NULL) + return 0; + + emtmp = OPENSSL_strndup((char *)email->data, email->length); + if (emtmp == NULL) { + X509_email_free(*sk); + *sk = NULL; + return 0; + } + + /* Don't add duplicates */ + if (sk_OPENSSL_STRING_find(*sk, emtmp) != -1) { + OPENSSL_free(emtmp); + return 1; + } + if (!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 != 0) { + 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 && OPENSSL_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 && OPENSSL_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 != NULL) { + *peername = OPENSSL_strndup((char *)a->data, a->length); + if (*peername == NULL) + return -1; + } + } 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 != NULL) { + *peername = OPENSSL_strndup((char *)astr, astrlen); + if (*peername == NULL) { + OPENSSL_free(astr); + return -1; + } + } + 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; + const 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); + switch (gen->type) { + default: + continue; + case GEN_OTHERNAME: + switch (OBJ_obj2nid(gen->d.otherName->type_id)) { + default: + continue; + case NID_id_on_SmtpUTF8Mailbox: + /*- + * https://datatracker.ietf.org/doc/html/rfc8398#section-3 + * + * Due to name constraint compatibility reasons described + * in Section 6, SmtpUTF8Mailbox subjectAltName MUST NOT + * be used unless the local-part of the email address + * contains non-ASCII characters. When the local-part is + * ASCII, rfc822Name subjectAltName MUST be used instead + * of SmtpUTF8Mailbox. This is compatible with legacy + * software that supports only rfc822Name (and not + * SmtpUTF8Mailbox). [...] + * + * SmtpUTF8Mailbox is encoded as UTF8String. + * + * If it is not a UTF8String then that is unexpected, and + * we ignore the invalid SAN (neither set san_present nor + * consider it a candidate for equality). This does mean + * that the subject CN may be considered, as would be the + * case when the malformed SmtpUtf8Mailbox SAN is instead + * simply absent. + * + * When CN-ID matching is not desirable, applications can + * choose to turn it off, doing so is at this time a best + * practice. + */ + if (check_type != GEN_EMAIL + || gen->d.otherName->value->type != V_ASN1_UTF8STRING) + continue; + alt_type = 0; + cstr = gen->d.otherName->value->value.utf8string; + break; + } + break; + case GEN_EMAIL: + if (check_type != GEN_EMAIL) + continue; + cstr = gen->d.rfc822Name; + break; + case GEN_DNS: + if (check_type != GEN_DNS) + continue; + cstr = gen->d.dNSName; + break; + case GEN_IPADD: + if (check_type != GEN_IPADD) + continue; + cstr = gen->d.iPAddress; + break; + } + san_present = 1; + /* 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)ossl_a2i_ipadd(ipout, ipasc); + if (iplen == 0) + return -2; + return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL); +} + +char *ossl_ipaddr_to_asc(unsigned char *p, int len) +{ + /* + * 40 is enough space for the longest IPv6 address + nul terminator byte + * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX\0 + */ + char buf[40], *out; + int i = 0, remain = 0, bytes = 0; + + switch (len) { + case 4: /* IPv4 */ + BIO_snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + break; + case 16: /* IPv6 */ + for (out = buf, i = 8, remain = sizeof(buf); + i-- > 0 && bytes >= 0; + remain -= bytes, out += bytes) { + const char *template = (i > 0 ? "%X:" : "%X"); + + bytes = BIO_snprintf(out, remain, template, p[0] << 8 | p[1]); + p += 2; + } + break; + default: + BIO_snprintf(buf, sizeof(buf), "<invalid length=%d>", len); + break; + } + return OPENSSL_strdup(buf); +} + +/* + * 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 = ossl_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 == NULL) + return NULL; + iptmp = OPENSSL_strdup(ipasc); + if (iptmp == NULL) + return NULL; + p = iptmp + (p - ipasc); + *p++ = 0; + + iplen1 = ossl_a2i_ipadd(ipout, iptmp); + + if (!iplen1) + goto err; + + iplen2 = ossl_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 ossl_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) +{ + const char *p; + int a0, a1, a2, a3, n; + + if (sscanf(in, "%d.%d.%d.%d%n", &a0, &a1, &a2, &a3, &n) != 4) + return 0; + if ((a0 < 0) || (a0 > 255) || (a1 < 0) || (a1 > 255) + || (a2 < 0) || (a2 > 255) || (a3 < 0) || (a3 > 255)) + return 0; + p = in + n; + if (!(*p == '\0' || ossl_isspace(*p))) + 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; + } else if (v6stat.zero_cnt == 2) { + /* Can only have two zeroes if at start or end */ + 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 000000000000..6f38034c1afe --- /dev/null +++ b/crypto/x509/v3err.c @@ -0,0 +1,147 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2021 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> +#include "crypto/x509v3err.h" + +#ifndef OPENSSL_NO_ERR + +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_EMPTY_KEY_USAGE), "empty key usage"}, + {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_CERTIFICATE), + "invalid certificate"}, + {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_EMPTY_NAME), + "invalid empty name"}, + {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_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_NEGATIVE_PATHLEN), + "negative pathlen"}, + {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 ossl_err_load_X509V3_strings(void) +{ +#ifndef OPENSSL_NO_ERR + if (ERR_reason_error_string(X509V3_str_reasons[0].error) == NULL) + ERR_load_strings_const(X509V3_str_reasons); +#endif + return 1; +} diff --git a/crypto/x509/x509_att.c b/crypto/x509/x509_att.c index cc9f9d19099d..6a541d7980a3 100644 --- a/crypto/x509/x509_att.c +++ b/crypto/x509/x509_att.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -15,6 +15,7 @@ #include <openssl/evp.h> #include <openssl/x509.h> #include <openssl/x509v3.h> +#include "crypto/x509.h" #include "x509_local.h" int X509at_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x) @@ -70,22 +71,23 @@ X509_ATTRIBUTE *X509at_delete_attr(STACK_OF(X509_ATTRIBUTE) *x, int loc) return ret; } -STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x, - X509_ATTRIBUTE *attr) +STACK_OF(X509_ATTRIBUTE) *ossl_x509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x, + X509_ATTRIBUTE *attr) { X509_ATTRIBUTE *new_attr = NULL; STACK_OF(X509_ATTRIBUTE) *sk = NULL; if (x == NULL) { - X509err(X509_F_X509AT_ADD1_ATTR, ERR_R_PASSED_NULL_PARAMETER); - goto err2; + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; } if (*x == NULL) { if ((sk = sk_X509_ATTRIBUTE_new_null()) == NULL) goto err; - } else + } else { sk = *x; + } if ((new_attr = X509_ATTRIBUTE_dup(attr)) == NULL) goto err2; @@ -95,25 +97,76 @@ STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x, *x = sk; return sk; err: - X509err(X509_F_X509AT_ADD1_ATTR, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); err2: X509_ATTRIBUTE_free(new_attr); - sk_X509_ATTRIBUTE_free(sk); + if (*x == NULL) + sk_X509_ATTRIBUTE_free(sk); return NULL; } +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x, + X509_ATTRIBUTE *attr) +{ + if (x == NULL || attr == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if (*x != NULL && X509at_get_attr_by_OBJ(*x, attr->object, -1) != -1) { + ERR_raise(ERR_LIB_X509, X509_R_DUPLICATE_ATTRIBUTE); + return NULL; + } + + return ossl_x509at_add1_attr(x, attr); +} + +STACK_OF(X509_ATTRIBUTE) *ossl_x509at_add1_attr_by_OBJ(STACK_OF(X509_ATTRIBUTE) **x, + const ASN1_OBJECT *obj, + int type, + const unsigned char *bytes, + int len) +{ + X509_ATTRIBUTE *attr; + STACK_OF(X509_ATTRIBUTE) *ret; + + attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, type, bytes, len); + if (attr == NULL) + return 0; + ret = ossl_x509at_add1_attr(x, attr); + X509_ATTRIBUTE_free(attr); + return ret; +} + STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_OBJ(STACK_OF(X509_ATTRIBUTE) **x, const ASN1_OBJECT *obj, int type, const unsigned char *bytes, int len) { + if (x == NULL || obj == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if (*x != NULL && X509at_get_attr_by_OBJ(*x, obj, -1) != -1) { + ERR_raise(ERR_LIB_X509, X509_R_DUPLICATE_ATTRIBUTE); + return NULL; + } + + return ossl_x509at_add1_attr_by_OBJ(x, obj, type, bytes, len); +} + +STACK_OF(X509_ATTRIBUTE) *ossl_x509at_add1_attr_by_NID(STACK_OF(X509_ATTRIBUTE) **x, + int nid, int type, + const unsigned char *bytes, + int len) +{ X509_ATTRIBUTE *attr; STACK_OF(X509_ATTRIBUTE) *ret; - attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, type, bytes, len); - if (!attr) + + attr = X509_ATTRIBUTE_create_by_NID(NULL, nid, type, bytes, len); + if (attr == NULL) return 0; - ret = X509at_add1_attr(x, attr); + ret = ossl_x509at_add1_attr(x, attr); X509_ATTRIBUTE_free(attr); return ret; } @@ -123,12 +176,31 @@ STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_NID(STACK_OF(X509_ATTRIBUTE) const unsigned char *bytes, int len) { + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if (*x != NULL && X509at_get_attr_by_NID(*x, nid, -1) != -1) { + ERR_raise(ERR_LIB_X509, X509_R_DUPLICATE_ATTRIBUTE); + return NULL; + } + + return ossl_x509at_add1_attr_by_NID(x, nid, type, bytes, len); +} + +STACK_OF(X509_ATTRIBUTE) *ossl_x509at_add1_attr_by_txt(STACK_OF(X509_ATTRIBUTE) **x, + const char *attrname, + int type, + const unsigned char *bytes, + int len) +{ X509_ATTRIBUTE *attr; STACK_OF(X509_ATTRIBUTE) *ret; - attr = X509_ATTRIBUTE_create_by_NID(NULL, nid, type, bytes, len); - if (!attr) + + attr = X509_ATTRIBUTE_create_by_txt(NULL, attrname, type, bytes, len); + if (attr == NULL) return 0; - ret = X509at_add1_attr(x, attr); + ret = ossl_x509at_add1_attr(x, attr); X509_ATTRIBUTE_free(attr); return ret; } @@ -165,6 +237,23 @@ void *X509at_get0_data_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *x, return X509_ATTRIBUTE_get0_data(at, 0, type, NULL); } +STACK_OF(X509_ATTRIBUTE) *ossl_x509at_dup(const STACK_OF(X509_ATTRIBUTE) *x) +{ + int i, n; + STACK_OF(X509_ATTRIBUTE) *sk = NULL; + + n = sk_X509_ATTRIBUTE_num(x); + for (i = 0; i < n; ++i) { + X509_ATTRIBUTE *attr = sk_X509_ATTRIBUTE_value(x, i); + + if (X509at_add1_attr(&sk, attr) == NULL) { + sk_X509_ATTRIBUTE_pop_free(sk, X509_ATTRIBUTE_free); + return NULL; + } + } + return sk; +} + X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid, int atrtype, const void *data, int len) @@ -174,7 +263,7 @@ X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid, obj = OBJ_nid2obj(nid); if (obj == NULL) { - X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_NID, X509_R_UNKNOWN_NID); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_NID); return NULL; } ret = X509_ATTRIBUTE_create_by_OBJ(attr, obj, atrtype, data, len); @@ -192,8 +281,7 @@ X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr, if ((attr == NULL) || (*attr == NULL)) { if ((ret = X509_ATTRIBUTE_new()) == NULL) { - X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ, - ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } } else @@ -223,9 +311,8 @@ X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_txt(X509_ATTRIBUTE **attr, obj = OBJ_txt2obj(atrname, 0); if (obj == NULL) { - X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_TXT, - X509_R_INVALID_FIELD_NAME); - ERR_add_error_data(2, "name=", atrname); + ERR_raise_data(ERR_LIB_X509, X509_R_INVALID_FIELD_NAME, + "name=%s", atrname); return NULL; } nattr = X509_ATTRIBUTE_create_by_OBJ(attr, obj, type, bytes, len); @@ -254,7 +341,7 @@ int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, stmp = ASN1_STRING_set_by_NID(NULL, data, len, attrtype, OBJ_obj2nid(attr->object)); if (!stmp) { - X509err(X509_F_X509_ATTRIBUTE_SET1_DATA, ERR_R_ASN1_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); return 0; } atype = stmp->type; @@ -287,7 +374,7 @@ int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, goto err; return 1; err: - X509err(X509_F_X509_ATTRIBUTE_SET1_DATA, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); ASN1_TYPE_free(ttmp); ASN1_STRING_free(stmp); return 0; @@ -317,7 +404,7 @@ void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *attr, int idx, if (atrtype == V_ASN1_BOOLEAN || atrtype == V_ASN1_NULL || atrtype != ASN1_TYPE_get(ttmp)) { - X509err(X509_F_X509_ATTRIBUTE_GET0_DATA, X509_R_WRONG_TYPE); + ERR_raise(ERR_LIB_X509, X509_R_WRONG_TYPE); return NULL; } return ttmp->value.ptr; diff --git a/crypto/x509/x509_cmp.c b/crypto/x509/x509_cmp.c index 3724a118f343..f2c3a568198d 100644 --- a/crypto/x509/x509_cmp.c +++ b/crypto/x509/x509_cmp.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -13,6 +13,7 @@ #include <openssl/objects.h> #include <openssl/x509.h> #include <openssl/x509v3.h> +#include <openssl/core_names.h> #include "crypto/x509.h" int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b) @@ -20,11 +21,15 @@ int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b) int i; const X509_CINF *ai, *bi; + if (b == NULL) + return a != NULL; + if (a == NULL) + return -1; ai = &a->cert_info; bi = &b->cert_info; i = ASN1_INTEGER_cmp(&ai->serialNumber, &bi->serialNumber); - if (i) - return i; + if (i != 0) + return i < 0 ? -1 : 1; return X509_NAME_cmp(ai->issuer, bi->issuer); } @@ -35,13 +40,18 @@ unsigned long X509_issuer_and_serial_hash(X509 *a) EVP_MD_CTX *ctx = EVP_MD_CTX_new(); unsigned char md[16]; char *f = NULL; + EVP_MD *digest = NULL; if (ctx == NULL) goto err; f = X509_NAME_oneline(a->cert_info.issuer, NULL, 0); if (f == NULL) goto err; - if (!EVP_DigestInit_ex(ctx, EVP_md5(), NULL)) + digest = EVP_MD_fetch(a->libctx, SN_md5, a->propq); + if (digest == NULL) + goto err; + + if (!EVP_DigestInit_ex(ctx, digest, NULL)) goto err; if (!EVP_DigestUpdate(ctx, (unsigned char *)f, strlen(f))) goto err; @@ -56,6 +66,7 @@ unsigned long X509_issuer_and_serial_hash(X509 *a) ) & 0xffffffffL; err: OPENSSL_free(f); + EVP_MD_free(digest); EVP_MD_CTX_free(ctx); return ret; } @@ -78,7 +89,15 @@ int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b) int X509_CRL_match(const X509_CRL *a, const X509_CRL *b) { - return memcmp(a->sha1_hash, b->sha1_hash, 20); + int rv; + + if ((a->flags & EXFLAG_NO_FINGERPRINT) == 0 + && (b->flags & EXFLAG_NO_FINGERPRINT) == 0) + rv = memcmp(a->sha1_hash, b->sha1_hash, SHA_DIGEST_LENGTH); + else + return -2; + + return rv < 0 ? -1 : rv > 0; } X509_NAME *X509_get_issuer_name(const X509 *a) @@ -88,7 +107,7 @@ X509_NAME *X509_get_issuer_name(const X509 *a) unsigned long X509_issuer_name_hash(X509 *x) { - return X509_NAME_hash(x->cert_info.issuer); + return X509_NAME_hash_ex(x->cert_info.issuer, NULL, NULL, NULL); } #ifndef OPENSSL_NO_MD5 @@ -115,7 +134,7 @@ const ASN1_INTEGER *X509_get0_serialNumber(const X509 *a) unsigned long X509_subject_name_hash(X509 *x) { - return X509_NAME_hash(x->cert_info.subject); + return X509_NAME_hash_ex(x->cert_info.subject, NULL, NULL, NULL); } #ifndef OPENSSL_NO_MD5 @@ -140,7 +159,7 @@ int X509_cmp(const X509 *a, const X509 *b) if (a == b) /* for efficiency */ return 0; - /* try to make sure hash is valid */ + /* attempt to compute cert hash */ (void)X509_check_purpose((X509 *)a, -1, 0); (void)X509_check_purpose((X509 *)b, -1, 0); @@ -148,7 +167,7 @@ int X509_cmp(const X509 *a, const X509 *b) && (b->ex_flags & EXFLAG_NO_FINGERPRINT) == 0) rv = memcmp(a->sha1_hash, b->sha1_hash, SHA_DIGEST_LENGTH); if (rv != 0) - return rv; + return rv < 0 ? -1 : 1; /* Check for match against stored encoding too */ if (!a->cert_info.enc.modified && !b->cert_info.enc.modified) { @@ -156,53 +175,140 @@ int X509_cmp(const X509 *a, const X509 *b) return -1; if (a->cert_info.enc.len > b->cert_info.enc.len) return 1; - return memcmp(a->cert_info.enc.enc, b->cert_info.enc.enc, - a->cert_info.enc.len); + rv = memcmp(a->cert_info.enc.enc, + b->cert_info.enc.enc, a->cert_info.enc.len); } - return rv; + return rv < 0 ? -1 : rv > 0; +} + +int ossl_x509_add_cert_new(STACK_OF(X509) **p_sk, X509 *cert, int flags) +{ + if (*p_sk == NULL && (*p_sk = sk_X509_new_null()) == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + return 0; + } + return X509_add_cert(*p_sk, cert, flags); +} + +int X509_add_cert(STACK_OF(X509) *sk, X509 *cert, int flags) +{ + if (sk == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (cert == NULL) + return 0; + if ((flags & X509_ADD_FLAG_NO_DUP) != 0) { + /* + * not using sk_X509_set_cmp_func() and sk_X509_find() + * because this re-orders the certs on the stack + */ + int i; + + for (i = 0; i < sk_X509_num(sk); i++) { + if (X509_cmp(sk_X509_value(sk, i), cert) == 0) + return 1; + } + } + if ((flags & X509_ADD_FLAG_NO_SS) != 0) { + int ret = X509_self_signed(cert, 0); + + if (ret != 0) + return ret > 0 ? 1 : 0; + } + if (!sk_X509_insert(sk, cert, + (flags & X509_ADD_FLAG_PREPEND) != 0 ? 0 : -1)) { + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + return 0; + } + if ((flags & X509_ADD_FLAG_UP_REF) != 0) + (void)X509_up_ref(cert); + return 1; +} + +int X509_add_certs(STACK_OF(X509) *sk, STACK_OF(X509) *certs, int flags) +/* compiler would allow 'const' for the certs, yet they may get up-ref'ed */ +{ + if (sk == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + return ossl_x509_add_certs_new(&sk, certs, flags); +} + +int ossl_x509_add_certs_new(STACK_OF(X509) **p_sk, STACK_OF(X509) *certs, + int flags) +/* compiler would allow 'const' for the certs, yet they may get up-ref'ed */ +{ + int n = sk_X509_num(certs /* may be NULL */); + int i; + + for (i = 0; i < n; i++) { + int j = (flags & X509_ADD_FLAG_PREPEND) == 0 ? i : n - 1 - i; + /* if prepend, add certs in reverse order to keep original order */ + + if (!ossl_x509_add_cert_new(p_sk, sk_X509_value(certs, j), flags)) + return 0; + } + return 1; } int X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b) { int ret; - /* Ensure canonical encoding is present and up to date */ + if (b == NULL) + return a != NULL; + if (a == NULL) + return -1; - if (!a->canon_enc || a->modified) { + /* Ensure canonical encoding is present and up to date */ + if (a->canon_enc == NULL || a->modified) { ret = i2d_X509_NAME((X509_NAME *)a, NULL); if (ret < 0) return -2; } - if (!b->canon_enc || b->modified) { + if (b->canon_enc == NULL || b->modified) { ret = i2d_X509_NAME((X509_NAME *)b, NULL); if (ret < 0) return -2; } ret = a->canon_enclen - b->canon_enclen; + if (ret == 0 && a->canon_enclen == 0) + return 0; - if (ret != 0 || a->canon_enclen == 0) - return ret; - - return memcmp(a->canon_enc, b->canon_enc, a->canon_enclen); + if (ret == 0) { + if (a->canon_enc == NULL || b->canon_enc == NULL) + return -2; + ret = memcmp(a->canon_enc, b->canon_enc, a->canon_enclen); + } + return ret < 0 ? -1 : ret > 0; } -unsigned long X509_NAME_hash(X509_NAME *x) +unsigned long X509_NAME_hash_ex(const X509_NAME *x, OSSL_LIB_CTX *libctx, + const char *propq, int *ok) { unsigned long ret = 0; unsigned char md[SHA_DIGEST_LENGTH]; + EVP_MD *sha1 = EVP_MD_fetch(libctx, "SHA1", propq); + int i2d_ret; /* Make sure X509_NAME structure contains valid cached encoding */ - i2d_X509_NAME(x, NULL); - if (!EVP_Digest(x->canon_enc, x->canon_enclen, md, NULL, EVP_sha1(), - NULL)) - return 0; - - ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | - ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) - ) & 0xffffffffL; + i2d_ret = i2d_X509_NAME(x, NULL); + if (ok != NULL) + *ok = 0; + if (i2d_ret >= 0 && sha1 != NULL + && EVP_Digest(x->canon_enc, x->canon_enclen, md, NULL, sha1, NULL)) { + ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | + ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) + ) & 0xffffffffL; + if (ok != NULL) + *ok = 1; + } + EVP_MD_free(sha1); return ret; } @@ -211,34 +317,38 @@ unsigned long X509_NAME_hash(X509_NAME *x) * I now DER encode the name and hash it. Since I cache the DER encoding, * this is reasonably efficient. */ - -unsigned long X509_NAME_hash_old(X509_NAME *x) +unsigned long X509_NAME_hash_old(const X509_NAME *x) { + EVP_MD *md5 = EVP_MD_fetch(NULL, OSSL_DIGEST_NAME_MD5, "-fips"); EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); unsigned long ret = 0; unsigned char md[16]; - if (md_ctx == NULL) - return ret; + if (md5 == NULL || md_ctx == NULL) + goto end; /* Make sure X509_NAME structure contains valid cached encoding */ - i2d_X509_NAME(x, NULL); - EVP_MD_CTX_set_flags(md_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); - if (EVP_DigestInit_ex(md_ctx, EVP_md5(), NULL) + if (i2d_X509_NAME(x, NULL) < 0) + goto end; + + if (EVP_DigestInit_ex(md_ctx, md5, NULL) && EVP_DigestUpdate(md_ctx, x->bytes->data, x->bytes->length) && EVP_DigestFinal_ex(md_ctx, md, NULL)) ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) ) & 0xffffffffL; + + end: EVP_MD_CTX_free(md_ctx); + EVP_MD_free(md5); return ret; } #endif /* Search a stack of X509 for a match */ -X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk, X509_NAME *name, - ASN1_INTEGER *serial) +X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk, const X509_NAME *name, + const ASN1_INTEGER *serial) { int i; X509 x, *x509 = NULL; @@ -247,7 +357,7 @@ X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk, X509_NAME *name, return NULL; x.cert_info.serialNumber = *serial; - x.cert_info.issuer = name; + x.cert_info.issuer = (X509_NAME *)name; /* won't modify it */ for (i = 0; i < sk_X509_num(sk); i++) { x509 = sk_X509_value(sk, i); @@ -257,7 +367,7 @@ X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk, X509_NAME *name, return NULL; } -X509 *X509_find_by_subject(STACK_OF(X509) *sk, X509_NAME *name) +X509 *X509_find_by_subject(STACK_OF(X509) *sk, const X509_NAME *name) { X509 *x509; int i; @@ -290,27 +400,24 @@ int X509_check_private_key(const X509 *x, const EVP_PKEY *k) int ret; xk = X509_get0_pubkey(x); + if (xk == NULL) { + ERR_raise(ERR_LIB_X509, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY); + return 0; + } - if (xk) - ret = EVP_PKEY_cmp(xk, k); - else - ret = -2; - - switch (ret) { - case 1: - break; + switch (ret = EVP_PKEY_eq(xk, k)) { case 0: - X509err(X509_F_X509_CHECK_PRIVATE_KEY, X509_R_KEY_VALUES_MISMATCH); + ERR_raise(ERR_LIB_X509, X509_R_KEY_VALUES_MISMATCH); break; case -1: - X509err(X509_F_X509_CHECK_PRIVATE_KEY, X509_R_KEY_TYPE_MISMATCH); + ERR_raise(ERR_LIB_X509, X509_R_KEY_TYPE_MISMATCH); break; case -2: - X509err(X509_F_X509_CHECK_PRIVATE_KEY, X509_R_UNKNOWN_KEY_TYPE); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_KEY_TYPE); + break; } - if (ret > 0) - return 1; - return 0; + + return ret > 0; } /* @@ -323,13 +430,18 @@ int X509_check_private_key(const X509 *x, const EVP_PKEY *k) static int check_suite_b(EVP_PKEY *pkey, int sign_nid, unsigned long *pflags) { - const EC_GROUP *grp = NULL; + char curve_name[80]; + size_t curve_name_len; int curve_nid; - if (pkey && EVP_PKEY_id(pkey) == EVP_PKEY_EC) - grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)); - if (!grp) + + if (pkey == NULL || !EVP_PKEY_is_a(pkey, "EC")) return X509_V_ERR_SUITE_B_INVALID_ALGORITHM; - curve_nid = EC_GROUP_get_curve_name(grp); + + if (!EVP_PKEY_get_group_name(pkey, curve_name, sizeof(curve_name), + &curve_name_len)) + return X509_V_ERR_SUITE_B_INVALID_CURVE; + + curve_nid = OBJ_txt2nid(curve_name); /* Check curve is consistent with LOS */ if (curve_nid == NID_secp384r1) { /* P-384 */ /* @@ -346,9 +458,9 @@ static int check_suite_b(EVP_PKEY *pkey, int sign_nid, unsigned long *pflags) return X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM; if (!(*pflags & X509_V_FLAG_SUITEB_128_LOS_ONLY)) return X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED; - } else + } else { return X509_V_ERR_SUITE_B_INVALID_CURVE; - + } return X509_V_OK; } @@ -366,9 +478,9 @@ int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain, if (x == NULL) { x = sk_X509_value(chain, 0); i = 1; - } else + } else { i = 0; - + } pk = X509_get0_pubkey(x); /* @@ -380,7 +492,7 @@ int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain, if (chain == NULL) return check_suite_b(pk, -1, &tflags); - if (X509_get_version(x) != 2) { + if (X509_get_version(x) != X509_VERSION_3) { rv = X509_V_ERR_SUITE_B_INVALID_VERSION; /* Correct error depth */ i = 0; @@ -397,7 +509,7 @@ int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain, for (; i < sk_X509_num(chain); i++) { sign_nid = X509_get_signature_nid(x); x = sk_X509_value(chain, i); - if (X509_get_version(x) != 2) { + if (X509_get_version(x) != X509_VERSION_3) { rv = X509_V_ERR_SUITE_B_INVALID_VERSION; goto end; } @@ -449,6 +561,7 @@ int X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk, unsigned long flags) } #endif + /* * Not strictly speaking an "up_ref" as a STACK doesn't have a reference * count but it has the same effect by duping the STACK and upping the ref of @@ -456,20 +569,22 @@ int X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk, unsigned long flags) */ STACK_OF(X509) *X509_chain_up_ref(STACK_OF(X509) *chain) { - STACK_OF(X509) *ret; + STACK_OF(X509) *ret = sk_X509_dup(chain); int i; - ret = sk_X509_dup(chain); + if (ret == NULL) return NULL; for (i = 0; i < sk_X509_num(ret); i++) { X509 *x = sk_X509_value(ret, i); + if (!X509_up_ref(x)) goto err; } return ret; + err: while (i-- > 0) - X509_free (sk_X509_value(ret, i)); + X509_free(sk_X509_value(ret, i)); sk_X509_free(ret); return NULL; } diff --git a/crypto/x509/x509_d2.c b/crypto/x509/x509_d2.c index 099ffda1e15c..62aceb7acade 100644 --- a/crypto/x509/x509_d2.c +++ b/crypto/x509/x509_d2.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -12,46 +12,100 @@ #include <openssl/crypto.h> #include <openssl/x509.h> -int X509_STORE_set_default_paths(X509_STORE *ctx) +int X509_STORE_set_default_paths_ex(X509_STORE *ctx, OSSL_LIB_CTX *libctx, + const char *propq) { X509_LOOKUP *lookup; lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_file()); if (lookup == NULL) return 0; - X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT); + X509_LOOKUP_load_file_ex(lookup, NULL, X509_FILETYPE_DEFAULT, libctx, propq); lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir()); if (lookup == NULL) return 0; X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_store()); + if (lookup == NULL) + return 0; + X509_LOOKUP_add_store_ex(lookup, NULL, libctx, propq); + /* clear any errors */ ERR_clear_error(); return 1; } +int X509_STORE_set_default_paths(X509_STORE *ctx) +{ + return X509_STORE_set_default_paths_ex(ctx, NULL, NULL); +} -int X509_STORE_load_locations(X509_STORE *ctx, const char *file, - const char *path) +int X509_STORE_load_file_ex(X509_STORE *ctx, const char *file, + OSSL_LIB_CTX *libctx, const char *propq) +{ + X509_LOOKUP *lookup; + + if (file == NULL + || (lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_file())) == NULL + || X509_LOOKUP_load_file_ex(lookup, file, X509_FILETYPE_PEM, libctx, + propq) <= 0) + return 0; + + return 1; +} + +int X509_STORE_load_file(X509_STORE *ctx, const char *file) +{ + return X509_STORE_load_file_ex(ctx, file, NULL, NULL); +} + +int X509_STORE_load_path(X509_STORE *ctx, const char *path) +{ + X509_LOOKUP *lookup; + + if (path == NULL + || (lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir())) == NULL + || X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) <= 0) + return 0; + + return 1; +} + +int X509_STORE_load_store_ex(X509_STORE *ctx, const char *uri, + OSSL_LIB_CTX *libctx, const char *propq) { X509_LOOKUP *lookup; - if (file != NULL) { - lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_file()); - if (lookup == NULL) - return 0; - if (X509_LOOKUP_load_file(lookup, file, X509_FILETYPE_PEM) != 1) - return 0; - } - if (path != NULL) { - lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir()); - if (lookup == NULL) - return 0; - if (X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1) - return 0; - } - if ((path == NULL) && (file == NULL)) + if (uri == NULL + || (lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_store())) == NULL + || X509_LOOKUP_add_store_ex(lookup, uri, libctx, propq) == 0) + return 0; + + return 1; +} + +int X509_STORE_load_store(X509_STORE *ctx, const char *uri) +{ + return X509_STORE_load_store_ex(ctx, uri, NULL, NULL); +} + +int X509_STORE_load_locations_ex(X509_STORE *ctx, const char *file, + const char *path, OSSL_LIB_CTX *libctx, + const char *propq) +{ + if (file == NULL && path == NULL) + return 0; + if (file != NULL && !X509_STORE_load_file_ex(ctx, file, libctx, propq)) + return 0; + if (path != NULL && !X509_STORE_load_path(ctx, path)) return 0; return 1; } + +int X509_STORE_load_locations(X509_STORE *ctx, const char *file, + const char *path) +{ + return X509_STORE_load_locations_ex(ctx, file, path, NULL, NULL); +} diff --git a/crypto/x509/x509_def.c b/crypto/x509/x509_def.c index bfa8d7d8522a..b8bdcb484195 100644 --- a/crypto/x509/x509_def.c +++ b/crypto/x509/x509_def.c @@ -1,7 +1,7 @@ /* * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 diff --git a/crypto/x509/x509_err.c b/crypto/x509/x509_err.c index bdd1e67cd3fd..37467935c997 100644 --- a/crypto/x509/x509_err.c +++ b/crypto/x509/x509_err.c @@ -1,8 +1,8 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -10,107 +10,10 @@ #include <openssl/err.h> #include <openssl/x509err.h> +#include "crypto/x509err.h" #ifndef OPENSSL_NO_ERR -static const ERR_STRING_DATA X509_str_functs[] = { - {ERR_PACK(ERR_LIB_X509, X509_F_ADD_CERT_DIR, 0), "add_cert_dir"}, - {ERR_PACK(ERR_LIB_X509, X509_F_BUILD_CHAIN, 0), "build_chain"}, - {ERR_PACK(ERR_LIB_X509, X509_F_BY_FILE_CTRL, 0), "by_file_ctrl"}, - {ERR_PACK(ERR_LIB_X509, X509_F_CHECK_NAME_CONSTRAINTS, 0), - "check_name_constraints"}, - {ERR_PACK(ERR_LIB_X509, X509_F_CHECK_POLICY, 0), "check_policy"}, - {ERR_PACK(ERR_LIB_X509, X509_F_DANE_I2D, 0), "dane_i2d"}, - {ERR_PACK(ERR_LIB_X509, X509_F_DIR_CTRL, 0), "dir_ctrl"}, - {ERR_PACK(ERR_LIB_X509, X509_F_GET_CERT_BY_SUBJECT, 0), - "get_cert_by_subject"}, - {ERR_PACK(ERR_LIB_X509, X509_F_I2D_X509_AUX, 0), "i2d_X509_AUX"}, - {ERR_PACK(ERR_LIB_X509, X509_F_LOOKUP_CERTS_SK, 0), "lookup_certs_sk"}, - {ERR_PACK(ERR_LIB_X509, X509_F_NETSCAPE_SPKI_B64_DECODE, 0), - "NETSCAPE_SPKI_b64_decode"}, - {ERR_PACK(ERR_LIB_X509, X509_F_NETSCAPE_SPKI_B64_ENCODE, 0), - "NETSCAPE_SPKI_b64_encode"}, - {ERR_PACK(ERR_LIB_X509, X509_F_NEW_DIR, 0), "new_dir"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509AT_ADD1_ATTR, 0), "X509at_add1_attr"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509V3_ADD_EXT, 0), "X509v3_add_ext"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_CREATE_BY_NID, 0), - "X509_ATTRIBUTE_create_by_NID"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ, 0), - "X509_ATTRIBUTE_create_by_OBJ"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_CREATE_BY_TXT, 0), - "X509_ATTRIBUTE_create_by_txt"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_GET0_DATA, 0), - "X509_ATTRIBUTE_get0_data"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_ATTRIBUTE_SET1_DATA, 0), - "X509_ATTRIBUTE_set1_data"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_CHECK_PRIVATE_KEY, 0), - "X509_check_private_key"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_CRL_DIFF, 0), "X509_CRL_diff"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_CRL_METHOD_NEW, 0), - "X509_CRL_METHOD_new"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_CRL_PRINT_FP, 0), "X509_CRL_print_fp"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_EXTENSION_CREATE_BY_NID, 0), - "X509_EXTENSION_create_by_NID"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_EXTENSION_CREATE_BY_OBJ, 0), - "X509_EXTENSION_create_by_OBJ"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_GET_PUBKEY_PARAMETERS, 0), - "X509_get_pubkey_parameters"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_LOAD_CERT_CRL_FILE, 0), - "X509_load_cert_crl_file"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_LOAD_CERT_FILE, 0), - "X509_load_cert_file"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_LOAD_CRL_FILE, 0), - "X509_load_crl_file"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_LOOKUP_METH_NEW, 0), - "X509_LOOKUP_meth_new"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_LOOKUP_NEW, 0), "X509_LOOKUP_new"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ADD_ENTRY, 0), - "X509_NAME_add_entry"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_CANON, 0), "x509_name_canon"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ENTRY_CREATE_BY_NID, 0), - "X509_NAME_ENTRY_create_by_NID"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ENTRY_CREATE_BY_TXT, 0), - "X509_NAME_ENTRY_create_by_txt"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ENTRY_SET_OBJECT, 0), - "X509_NAME_ENTRY_set_object"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_ONELINE, 0), "X509_NAME_oneline"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_NAME_PRINT, 0), "X509_NAME_print"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_OBJECT_NEW, 0), "X509_OBJECT_new"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_PRINT_EX_FP, 0), "X509_print_ex_fp"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_PUBKEY_DECODE, 0), - "x509_pubkey_decode"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_PUBKEY_GET, 0), "X509_PUBKEY_get"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_PUBKEY_GET0, 0), "X509_PUBKEY_get0"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_PUBKEY_SET, 0), "X509_PUBKEY_set"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_REQ_CHECK_PRIVATE_KEY, 0), - "X509_REQ_check_private_key"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_REQ_PRINT_EX, 0), "X509_REQ_print_ex"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_REQ_PRINT_FP, 0), "X509_REQ_print_fp"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_REQ_TO_X509, 0), "X509_REQ_to_X509"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_ADD_CERT, 0), - "X509_STORE_add_cert"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_ADD_CRL, 0), - "X509_STORE_add_crl"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_ADD_LOOKUP, 0), - "X509_STORE_add_lookup"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_GET1_ISSUER, 0), - "X509_STORE_CTX_get1_issuer"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_INIT, 0), - "X509_STORE_CTX_init"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_NEW, 0), - "X509_STORE_CTX_new"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_CTX_PURPOSE_INHERIT, 0), - "X509_STORE_CTX_purpose_inherit"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_STORE_NEW, 0), "X509_STORE_new"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_TO_X509_REQ, 0), "X509_to_X509_REQ"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_TRUST_ADD, 0), "X509_TRUST_add"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_TRUST_SET, 0), "X509_TRUST_set"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_VERIFY_CERT, 0), "X509_verify_cert"}, - {ERR_PACK(ERR_LIB_X509, X509_F_X509_VERIFY_PARAM_NEW, 0), - "X509_VERIFY_PARAM_new"}, - {0, NULL} -}; - static const ERR_STRING_DATA X509_str_reasons[] = { {ERR_PACK(ERR_LIB_X509, 0, X509_R_AKID_MISMATCH), "akid mismatch"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_BAD_SELECTOR), "bad selector"}, @@ -118,15 +21,24 @@ static const ERR_STRING_DATA X509_str_reasons[] = { {ERR_PACK(ERR_LIB_X509, 0, X509_R_BASE64_DECODE_ERROR), "base64 decode error"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_CANT_CHECK_DH_KEY), "cant check dh key"}, + {ERR_PACK(ERR_LIB_X509, 0, X509_R_CERTIFICATE_VERIFICATION_FAILED), + "certificate verification failed"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_CERT_ALREADY_IN_HASH_TABLE), "cert already in hash table"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_CRL_ALREADY_DELTA), "crl already delta"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_CRL_VERIFY_FAILURE), "crl verify failure"}, + {ERR_PACK(ERR_LIB_X509, 0, X509_R_DUPLICATE_ATTRIBUTE), + "duplicate attribute"}, + {ERR_PACK(ERR_LIB_X509, 0, X509_R_ERROR_GETTING_MD_BY_NID), + "error getting md by nid"}, + {ERR_PACK(ERR_LIB_X509, 0, X509_R_ERROR_USING_SIGINF_SET), + "error using siginf set"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_IDP_MISMATCH), "idp mismatch"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_ATTRIBUTES), "invalid attributes"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_DIRECTORY), "invalid directory"}, + {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_DISTPOINT), "invalid distpoint"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_FIELD_NAME), "invalid field name"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_INVALID_TRUST), "invalid trust"}, @@ -162,6 +74,8 @@ static const ERR_STRING_DATA X509_str_reasons[] = { {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_NID), "unknown nid"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_PURPOSE_ID), "unknown purpose id"}, + {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_SIGID_ALGS), + "unknown sigid algs"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNKNOWN_TRUST_ID), "unknown trust id"}, {ERR_PACK(ERR_LIB_X509, 0, X509_R_UNSUPPORTED_ALGORITHM), "unsupported algorithm"}, @@ -172,13 +86,11 @@ static const ERR_STRING_DATA X509_str_reasons[] = { #endif -int ERR_load_X509_strings(void) +int ossl_err_load_X509_strings(void) { #ifndef OPENSSL_NO_ERR - if (ERR_func_error_string(X509_str_functs[0].error) == NULL) { - ERR_load_strings_const(X509_str_functs); + if (ERR_reason_error_string(X509_str_reasons[0].error) == NULL) ERR_load_strings_const(X509_str_reasons); - } #endif return 1; } diff --git a/crypto/x509/x509_ext.c b/crypto/x509/x509_ext.c index 4cdab724eadf..a7b85857bdad 100644 --- a/crypto/x509/x509_ext.c +++ b/crypto/x509/x509_ext.c @@ -1,7 +1,7 @@ /* * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 diff --git a/crypto/x509/x509_local.h b/crypto/x509/x509_local.h index 10807e1def04..6d602e1d8ef5 100644 --- a/crypto/x509/x509_local.h +++ b/crypto/x509/x509_local.h @@ -1,7 +1,7 @@ /* - * Copyright 2014-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2014-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -9,6 +9,9 @@ #include "internal/refcount.h" +#define X509V3_conf_add_error_name_value(val) \ + ERR_add_error_data(4, "name=", (val)->name, ", value=", (val)->value) + /* * This structure holds all parameters associated with a verify operation by * including an X509_VERIFY_PARAM structure in related structures the @@ -36,7 +39,7 @@ struct X509_VERIFY_PARAM_st { }; /* No error callback if depth < 0 */ -int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth); +int ossl_x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth); /* a sequence of these are used */ struct x509_attributes_st { @@ -64,7 +67,7 @@ struct x509_crl_method_st { int (*crl_init) (X509_CRL *crl); int (*crl_free) (X509_CRL *crl); int (*crl_lookup) (X509_CRL *crl, X509_REVOKED **ret, - ASN1_INTEGER *ser, X509_NAME *issuer); + const ASN1_INTEGER *ser, const X509_NAME *issuer); int (*crl_verify) (X509_CRL *crl, EVP_PKEY *pk); }; @@ -77,15 +80,21 @@ struct x509_lookup_method_st { int (*ctrl) (X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret); int (*get_by_subject) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, - X509_NAME *name, X509_OBJECT *ret); + const X509_NAME *name, X509_OBJECT *ret); int (*get_by_issuer_serial) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, - X509_NAME *name, ASN1_INTEGER *serial, + const X509_NAME *name, + const ASN1_INTEGER *serial, X509_OBJECT *ret); int (*get_by_fingerprint) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, const unsigned char *bytes, int len, X509_OBJECT *ret); int (*get_by_alias) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, const char *str, int len, X509_OBJECT *ret); + int (*get_by_subject_ex) (X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const X509_NAME *name, X509_OBJECT *ret, + OSSL_LIB_CTX *libctx, const char *propq); + int (*ctrl_ex) (X509_LOOKUP *ctx, int cmd, const char *argc, long argl, + char **ret, OSSL_LIB_CTX *libctx, const char *propq); }; /* This is the functions plus an instance of the local variables. */ @@ -128,8 +137,11 @@ struct x509_store_st { int (*cert_crl) (X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x); /* Check policy status of the chain */ int (*check_policy) (X509_STORE_CTX *ctx); - STACK_OF(X509) *(*lookup_certs) (X509_STORE_CTX *ctx, X509_NAME *nm); - STACK_OF(X509_CRL) *(*lookup_crls) (X509_STORE_CTX *ctx, X509_NAME *nm); + STACK_OF(X509) *(*lookup_certs) (X509_STORE_CTX *ctx, + const X509_NAME *nm); + /* cannot constify 'ctx' param due to lookup_certs_sk() in x509_vfy.c */ + STACK_OF(X509_CRL) *(*lookup_crls) (const X509_STORE_CTX *ctx, + const X509_NAME *nm); int (*cleanup) (X509_STORE_CTX *ctx); CRYPTO_EX_DATA ex_data; CRYPTO_REF_COUNT references; @@ -143,7 +155,5 @@ DEFINE_STACK_OF(BY_DIR_ENTRY) typedef STACK_OF(X509_NAME_ENTRY) STACK_OF_X509_NAME_ENTRY; DEFINE_STACK_OF(STACK_OF_X509_NAME_ENTRY) -void x509_set_signature_info(X509_SIG_INFO *siginf, const X509_ALGOR *alg, - const ASN1_STRING *sig); -int x509_likely_issued(X509 *issuer, X509 *subject); -int x509_signing_allowed(const X509 *issuer, const X509 *subject); +int ossl_x509_likely_issued(X509 *issuer, X509 *subject); +int ossl_x509_signing_allowed(const X509 *issuer, const X509 *subject); diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index 641a41c35c78..d8927bda0706 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -20,7 +20,7 @@ X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method) X509_LOOKUP *ret = OPENSSL_zalloc(sizeof(*ret)); if (ret == NULL) { - X509err(X509_F_X509_LOOKUP_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } @@ -71,29 +71,49 @@ int X509_LOOKUP_shutdown(X509_LOOKUP *ctx) return 1; } -int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, - char **ret) +int X509_LOOKUP_ctrl_ex(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, + char **ret, OSSL_LIB_CTX *libctx, const char *propq) { if (ctx->method == NULL) return -1; + if (ctx->method->ctrl_ex != NULL) + return ctx->method->ctrl_ex(ctx, cmd, argc, argl, ret, libctx, propq); if (ctx->method->ctrl != NULL) return ctx->method->ctrl(ctx, cmd, argc, argl, ret); + return 1; +} + +int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, + char **ret) +{ + return X509_LOOKUP_ctrl_ex(ctx, cmd, argc, argl, ret, NULL, NULL); +} + +int X509_LOOKUP_by_subject_ex(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, + const X509_NAME *name, X509_OBJECT *ret, + OSSL_LIB_CTX *libctx, const char *propq) +{ + if (ctx->skip + || ctx->method == NULL + || (ctx->method->get_by_subject == NULL + && ctx->method->get_by_subject_ex == NULL)) + return 0; + if (ctx->method->get_by_subject_ex != NULL) + return ctx->method->get_by_subject_ex(ctx, type, name, ret, libctx, + propq); else - return 1; + return ctx->method->get_by_subject(ctx, type, name, ret); } int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, - X509_NAME *name, X509_OBJECT *ret) + const X509_NAME *name, X509_OBJECT *ret) { - if ((ctx->method == NULL) || (ctx->method->get_by_subject == NULL)) - return 0; - if (ctx->skip) - return 0; - return ctx->method->get_by_subject(ctx, type, name, ret); + return X509_LOOKUP_by_subject_ex(ctx, type, name, ret, NULL, NULL); } int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type, - X509_NAME *name, ASN1_INTEGER *serial, + const X509_NAME *name, + const ASN1_INTEGER *serial, X509_OBJECT *ret) { if ((ctx->method == NULL) || (ctx->method->get_by_issuer_serial == NULL)) @@ -162,34 +182,33 @@ X509_STORE *X509_STORE_new(void) X509_STORE *ret = OPENSSL_zalloc(sizeof(*ret)); if (ret == NULL) { - X509err(X509_F_X509_STORE_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } if ((ret->objs = sk_X509_OBJECT_new(x509_object_cmp)) == NULL) { - X509err(X509_F_X509_STORE_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } ret->cache = 1; if ((ret->get_cert_methods = sk_X509_LOOKUP_new_null()) == NULL) { - X509err(X509_F_X509_STORE_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } if ((ret->param = X509_VERIFY_PARAM_new()) == NULL) { - X509err(X509_F_X509_STORE_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE, ret, &ret->ex_data)) { - X509err(X509_F_X509_STORE_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } ret->lock = CRYPTO_THREAD_lock_new(); if (ret->lock == NULL) { - X509err(X509_F_X509_STORE_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } - ret->references = 1; return ret; @@ -237,7 +256,7 @@ int X509_STORE_up_ref(X509_STORE *vfy) if (CRYPTO_UP_REF(&vfy->references, &i, vfy->lock) <= 0) return 0; - REF_PRINT_COUNT("X509_STORE", a); + REF_PRINT_COUNT("X509_STORE", vfy); REF_ASSERT_ISNT(i < 2); return ((i > 1) ? 1 : 0); } @@ -258,7 +277,7 @@ X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m) /* a new one */ lu = X509_LOOKUP_new(m); if (lu == NULL) { - X509err(X509_F_X509_STORE_ADD_LOOKUP, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } @@ -266,14 +285,14 @@ X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m) if (sk_X509_LOOKUP_push(v->get_cert_methods, lu)) return lu; /* malloc failed */ - X509err(X509_F_X509_STORE_ADD_LOOKUP, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); X509_LOOKUP_free(lu); return NULL; } X509_OBJECT *X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX *vs, X509_LOOKUP_TYPE type, - X509_NAME *name) + const X509_NAME *name) { X509_OBJECT *ret = X509_OBJECT_new(); @@ -286,10 +305,12 @@ X509_OBJECT *X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX *vs, return ret; } -int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, X509_LOOKUP_TYPE type, - X509_NAME *name, X509_OBJECT *ret) +/* Also fill the cache with all matching certificates */ +int X509_STORE_CTX_get_by_subject(const X509_STORE_CTX *vs, + X509_LOOKUP_TYPE type, + const X509_NAME *name, X509_OBJECT *ret) { - X509_STORE *store = vs->ctx; + X509_STORE *store = vs->store; X509_LOOKUP *lu; X509_OBJECT stmp, *tmp; int i, j; @@ -300,15 +321,17 @@ int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, X509_LOOKUP_TYPE type, stmp.type = X509_LU_NONE; stmp.data.ptr = NULL; + if (!X509_STORE_lock(store)) + return 0; - X509_STORE_lock(store); tmp = X509_OBJECT_retrieve_by_subject(store->objs, type, name); X509_STORE_unlock(store); if (tmp == NULL || type == X509_LU_CRL) { for (i = 0; i < sk_X509_LOOKUP_num(store->get_cert_methods); i++) { lu = sk_X509_LOOKUP_value(store->get_cert_methods, i); - j = X509_LOOKUP_by_subject(lu, type, name, &stmp); + j = X509_LOOKUP_by_subject_ex(lu, type, name, &stmp, vs->libctx, + vs->propq); if (j) { tmp = &stmp; break; @@ -350,7 +373,12 @@ static int x509_store_add(X509_STORE *store, void *x, int crl) { return 0; } - X509_STORE_lock(store); + if (!X509_STORE_lock(store)) { + obj->type = X509_LU_NONE; + X509_OBJECT_free(obj); + return 0; + } + if (X509_OBJECT_retrieve_match(store->objs, obj)) { ret = 1; } else { @@ -368,7 +396,7 @@ static int x509_store_add(X509_STORE *store, void *x, int crl) { int X509_STORE_add_cert(X509_STORE *ctx, X509 *x) { if (!x509_store_add(ctx, x, 0)) { - X509err(X509_F_X509_STORE_ADD_CERT, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return 0; } return 1; @@ -377,7 +405,7 @@ int X509_STORE_add_cert(X509_STORE *ctx, X509 *x) int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x) { if (!x509_store_add(ctx, x, 1)) { - X509err(X509_F_X509_STORE_ADD_CRL, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return 0; } return 1; @@ -403,7 +431,7 @@ X509 *X509_OBJECT_get0_X509(const X509_OBJECT *a) return a->data.x509; } -X509_CRL *X509_OBJECT_get0_X509_CRL(X509_OBJECT *a) +X509_CRL *X509_OBJECT_get0_X509_CRL(const X509_OBJECT *a) { if (a == NULL || a->type != X509_LU_CRL) return NULL; @@ -420,7 +448,7 @@ X509_OBJECT *X509_OBJECT_new(void) X509_OBJECT *ret = OPENSSL_zalloc(sizeof(*ret)); if (ret == NULL) { - X509err(X509_F_X509_OBJECT_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } ret->type = X509_LU_NONE; @@ -472,7 +500,7 @@ void X509_OBJECT_free(X509_OBJECT *a) } static int x509_object_idx_cnt(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type, - X509_NAME *name, int *pnmatch) + const X509_NAME *name, int *pnmatch) { X509_OBJECT stmp; X509 x509_s; @@ -483,42 +511,30 @@ static int x509_object_idx_cnt(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type, switch (type) { case X509_LU_X509: stmp.data.x509 = &x509_s; - x509_s.cert_info.subject = name; + x509_s.cert_info.subject = (X509_NAME *)name; /* won't modify it */ break; case X509_LU_CRL: stmp.data.crl = &crl_s; - crl_s.crl.issuer = name; + crl_s.crl.issuer = (X509_NAME *)name; /* won't modify it */ break; case X509_LU_NONE: /* abort(); */ return -1; } - idx = sk_X509_OBJECT_find(h, &stmp); - if (idx >= 0 && pnmatch) { - int tidx; - const X509_OBJECT *tobj, *pstmp; - *pnmatch = 1; - pstmp = &stmp; - for (tidx = idx + 1; tidx < sk_X509_OBJECT_num(h); tidx++) { - tobj = sk_X509_OBJECT_value(h, tidx); - if (x509_object_cmp(&tobj, &pstmp)) - break; - (*pnmatch)++; - } - } + idx = sk_X509_OBJECT_find_all(h, &stmp, pnmatch); return idx; } int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type, - X509_NAME *name) + const X509_NAME *name) { return x509_object_idx_cnt(h, type, name, NULL); } X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h, X509_LOOKUP_TYPE type, - X509_NAME *name) + const X509_NAME *name) { int idx; idx = X509_OBJECT_idx_by_subject(h, type, name); @@ -527,23 +543,59 @@ X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h, return sk_X509_OBJECT_value(h, idx); } -STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *v) +STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(const X509_STORE *v) { return v->objs; } -STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) +STACK_OF(X509) *X509_STORE_get1_all_certs(X509_STORE *store) +{ + STACK_OF(X509) *sk; + STACK_OF(X509_OBJECT) *objs; + int i; + + if (store == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if ((sk = sk_X509_new_null()) == NULL) + return NULL; + if (!X509_STORE_lock(store)) + goto out_free; + + objs = X509_STORE_get0_objects(store); + for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { + X509 *cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i)); + + if (cert != NULL + && !X509_add_cert(sk, cert, X509_ADD_FLAG_UP_REF)) + goto err; + } + X509_STORE_unlock(store); + return sk; + + err: + X509_STORE_unlock(store); + out_free: + sk_X509_pop_free(sk, X509_free); + return NULL; +} + +STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, + const X509_NAME *nm) { int i, idx, cnt; STACK_OF(X509) *sk = NULL; X509 *x; X509_OBJECT *obj; - X509_STORE *store = ctx->ctx; + X509_STORE *store = ctx->store; if (store == NULL) return NULL; - X509_STORE_lock(store); + if (!X509_STORE_lock(store)) + return NULL; + idx = x509_object_idx_cnt(store->objs, X509_LU_X509, nm, &cnt); if (idx < 0) { /* @@ -561,7 +613,8 @@ STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) return NULL; } X509_OBJECT_free(xobj); - X509_STORE_lock(store); + if (!X509_STORE_lock(store)) + return NULL; idx = x509_object_idx_cnt(store->objs, X509_LU_X509, nm, &cnt); if (idx < 0) { X509_STORE_unlock(store); @@ -573,29 +626,24 @@ STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) for (i = 0; i < cnt; i++, idx++) { obj = sk_X509_OBJECT_value(store->objs, idx); x = obj->data.x509; - if (!X509_up_ref(x)) { + if (!X509_add_cert(sk, x, X509_ADD_FLAG_UP_REF)) { X509_STORE_unlock(store); sk_X509_pop_free(sk, X509_free); return NULL; } - if (!sk_X509_push(sk, x)) { - X509_STORE_unlock(store); - X509_free(x); - sk_X509_pop_free(sk, X509_free); - return NULL; - } } X509_STORE_unlock(store); return sk; } -STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm) +STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(const X509_STORE_CTX *ctx, + const X509_NAME *nm) { int i, idx, cnt; STACK_OF(X509_CRL) *sk = sk_X509_CRL_new_null(); X509_CRL *x; X509_OBJECT *obj, *xobj = X509_OBJECT_new(); - X509_STORE *store = ctx->ctx; + X509_STORE *store = ctx->store; /* Always do lookup to possibly add new CRLs to cache */ if (sk == NULL @@ -607,7 +655,10 @@ STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm) return NULL; } X509_OBJECT_free(xobj); - X509_STORE_lock(store); + if (!X509_STORE_lock(store)) { + sk_X509_CRL_free(sk); + return NULL; + } idx = x509_object_idx_cnt(store->objs, X509_LU_CRL, nm, &cnt); if (idx < 0) { X509_STORE_unlock(store); @@ -654,7 +705,7 @@ X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, if (!X509_cmp(obj->data.x509, x->data.x509)) return obj; } else if (x->type == X509_LU_CRL) { - if (!X509_CRL_match(obj->data.crl, x->data.crl)) + if (X509_CRL_match(obj->data.crl, x->data.crl) == 0) return obj; } else return obj; @@ -663,11 +714,8 @@ X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, } /*- - * Try to get issuer certificate from store. Due to limitations - * of the API this can only retrieve a single certificate matching - * a given subject name. However it will fill the cache with all - * matching certificates, so we can examine the cache for all - * matches. + * Try to get issuer cert from |ctx->store| matching the subject name of |x|. + * Prefer the first non-expired one, else take the most recently expired one. * * Return values are: * 1 lookup successful. @@ -676,10 +724,10 @@ X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, */ int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) { - X509_NAME *xn; + const X509_NAME *xn; X509_OBJECT *obj = X509_OBJECT_new(), *pobj = NULL; - X509_STORE *store = ctx->ctx; - int i, ok, idx, ret; + X509_STORE *store = ctx->store; + int i, ok, idx, ret, nmatch = 0; if (obj == NULL) return -1; @@ -690,53 +738,58 @@ int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) X509_OBJECT_free(obj); return 0; } - /* If certificate matches all OK */ + /* If certificate matches and is currently valid all OK */ if (ctx->check_issued(ctx, x, obj->data.x509)) { - if (x509_check_cert_time(ctx, obj->data.x509, -1)) { + if (ossl_x509_check_cert_time(ctx, obj->data.x509, -1)) { *issuer = obj->data.x509; - if (!X509_up_ref(*issuer)) { - *issuer = NULL; - ok = -1; - } + /* |*issuer| has taken over the cert reference from |obj| */ + obj->type = X509_LU_NONE; X509_OBJECT_free(obj); - return ok; + return 1; } } X509_OBJECT_free(obj); + /* + * Due to limitations of the API this can only retrieve a single cert. + * However it will fill the cache with all matching certificates, + * so we can examine the cache for all matches. + */ if (store == NULL) return 0; - /* Else find index of first cert accepted by 'check_issued' */ + /* Find index of first currently valid cert accepted by 'check_issued' */ ret = 0; - X509_STORE_lock(store); - idx = X509_OBJECT_idx_by_subject(store->objs, X509_LU_X509, xn); - if (idx != -1) { /* should be true as we've had at least one - * match */ + if (!X509_STORE_lock(store)) + return 0; + + idx = x509_object_idx_cnt(store->objs, X509_LU_X509, xn, &nmatch); + if (idx != -1) { /* should be true as we've had at least one match */ /* Look through all matching certs for suitable issuer */ - for (i = idx; i < sk_X509_OBJECT_num(store->objs); i++) { + for (i = idx; i < idx + nmatch; i++) { pobj = sk_X509_OBJECT_value(store->objs, i); /* See if we've run past the matches */ if (pobj->type != X509_LU_X509) break; - if (X509_NAME_cmp(xn, X509_get_subject_name(pobj->data.x509))) - break; if (ctx->check_issued(ctx, x, pobj->data.x509)) { - *issuer = pobj->data.x509; ret = 1; + /* If times check fine, exit with match, else keep looking. */ + if (ossl_x509_check_cert_time(ctx, pobj->data.x509, -1)) { + *issuer = pobj->data.x509; + break; + } /* - * If times check, exit with match, - * otherwise keep looking. Leave last - * match in issuer so we return nearest - * match if no certificate time is OK. + * Leave the so far most recently expired match in *issuer + * so we return nearest match if no certificate time is OK. */ - - if (x509_check_cert_time(ctx, *issuer, -1)) - break; + if (*issuer == NULL + || ASN1_TIME_compare(X509_get0_notAfter(pobj->data.x509), + X509_get0_notAfter(*issuer)) > 0) + *issuer = pobj->data.x509; } } } - if (*issuer && !X509_up_ref(*issuer)) { + if (*issuer != NULL && !X509_up_ref(*issuer)) { *issuer = NULL; ret = -1; } @@ -765,12 +818,12 @@ int X509_STORE_set_trust(X509_STORE *ctx, int trust) return X509_VERIFY_PARAM_set_trust(ctx->param, trust); } -int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *param) +int X509_STORE_set1_param(X509_STORE *ctx, const X509_VERIFY_PARAM *param) { return X509_VERIFY_PARAM_set1(ctx->param, param); } -X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *ctx) +X509_VERIFY_PARAM *X509_STORE_get0_param(const X509_STORE *ctx) { return ctx->param; } @@ -780,7 +833,7 @@ void X509_STORE_set_verify(X509_STORE *ctx, X509_STORE_CTX_verify_fn verify) ctx->verify = verify; } -X509_STORE_CTX_verify_fn X509_STORE_get_verify(X509_STORE *ctx) +X509_STORE_CTX_verify_fn X509_STORE_get_verify(const X509_STORE *ctx) { return ctx->verify; } @@ -791,7 +844,7 @@ void X509_STORE_set_verify_cb(X509_STORE *ctx, ctx->verify_cb = verify_cb; } -X509_STORE_CTX_verify_cb X509_STORE_get_verify_cb(X509_STORE *ctx) +X509_STORE_CTX_verify_cb X509_STORE_get_verify_cb(const X509_STORE *ctx) { return ctx->verify_cb; } @@ -802,7 +855,7 @@ void X509_STORE_set_get_issuer(X509_STORE *ctx, ctx->get_issuer = get_issuer; } -X509_STORE_CTX_get_issuer_fn X509_STORE_get_get_issuer(X509_STORE *ctx) +X509_STORE_CTX_get_issuer_fn X509_STORE_get_get_issuer(const X509_STORE *ctx) { return ctx->get_issuer; } @@ -813,7 +866,7 @@ void X509_STORE_set_check_issued(X509_STORE *ctx, ctx->check_issued = check_issued; } -X509_STORE_CTX_check_issued_fn X509_STORE_get_check_issued(X509_STORE *ctx) +X509_STORE_CTX_check_issued_fn X509_STORE_get_check_issued(const X509_STORE *ctx) { return ctx->check_issued; } @@ -824,7 +877,7 @@ void X509_STORE_set_check_revocation(X509_STORE *ctx, ctx->check_revocation = check_revocation; } -X509_STORE_CTX_check_revocation_fn X509_STORE_get_check_revocation(X509_STORE *ctx) +X509_STORE_CTX_check_revocation_fn X509_STORE_get_check_revocation(const X509_STORE *ctx) { return ctx->check_revocation; } @@ -835,7 +888,7 @@ void X509_STORE_set_get_crl(X509_STORE *ctx, ctx->get_crl = get_crl; } -X509_STORE_CTX_get_crl_fn X509_STORE_get_get_crl(X509_STORE *ctx) +X509_STORE_CTX_get_crl_fn X509_STORE_get_get_crl(const X509_STORE *ctx) { return ctx->get_crl; } @@ -846,7 +899,7 @@ void X509_STORE_set_check_crl(X509_STORE *ctx, ctx->check_crl = check_crl; } -X509_STORE_CTX_check_crl_fn X509_STORE_get_check_crl(X509_STORE *ctx) +X509_STORE_CTX_check_crl_fn X509_STORE_get_check_crl(const X509_STORE *ctx) { return ctx->check_crl; } @@ -857,7 +910,7 @@ void X509_STORE_set_cert_crl(X509_STORE *ctx, ctx->cert_crl = cert_crl; } -X509_STORE_CTX_cert_crl_fn X509_STORE_get_cert_crl(X509_STORE *ctx) +X509_STORE_CTX_cert_crl_fn X509_STORE_get_cert_crl(const X509_STORE *ctx) { return ctx->cert_crl; } @@ -868,7 +921,7 @@ void X509_STORE_set_check_policy(X509_STORE *ctx, ctx->check_policy = check_policy; } -X509_STORE_CTX_check_policy_fn X509_STORE_get_check_policy(X509_STORE *ctx) +X509_STORE_CTX_check_policy_fn X509_STORE_get_check_policy(const X509_STORE *ctx) { return ctx->check_policy; } @@ -879,7 +932,7 @@ void X509_STORE_set_lookup_certs(X509_STORE *ctx, ctx->lookup_certs = lookup_certs; } -X509_STORE_CTX_lookup_certs_fn X509_STORE_get_lookup_certs(X509_STORE *ctx) +X509_STORE_CTX_lookup_certs_fn X509_STORE_get_lookup_certs(const X509_STORE *ctx) { return ctx->lookup_certs; } @@ -890,7 +943,7 @@ void X509_STORE_set_lookup_crls(X509_STORE *ctx, ctx->lookup_crls = lookup_crls; } -X509_STORE_CTX_lookup_crls_fn X509_STORE_get_lookup_crls(X509_STORE *ctx) +X509_STORE_CTX_lookup_crls_fn X509_STORE_get_lookup_crls(const X509_STORE *ctx) { return ctx->lookup_crls; } @@ -901,7 +954,7 @@ void X509_STORE_set_cleanup(X509_STORE *ctx, ctx->cleanup = ctx_cleanup; } -X509_STORE_CTX_cleanup_fn X509_STORE_get_cleanup(X509_STORE *ctx) +X509_STORE_CTX_cleanup_fn X509_STORE_get_cleanup(const X509_STORE *ctx) { return ctx->cleanup; } @@ -911,12 +964,12 @@ int X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data) return CRYPTO_set_ex_data(&ctx->ex_data, idx, data); } -void *X509_STORE_get_ex_data(X509_STORE *ctx, int idx) +void *X509_STORE_get_ex_data(const X509_STORE *ctx, int idx) { return CRYPTO_get_ex_data(&ctx->ex_data, idx); } -X509_STORE *X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx) +X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) { - return ctx->ctx; + return ctx->store; } diff --git a/crypto/x509/x509_meth.c b/crypto/x509/x509_meth.c index 9348cc8eb788..a8eedd9b59af 100644 --- a/crypto/x509/x509_meth.c +++ b/crypto/x509/x509_meth.c @@ -1,7 +1,7 @@ /* - * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2018-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -14,7 +14,7 @@ #include "internal/cryptlib.h" #include <openssl/asn1.h> #include <openssl/x509.h> -#include <openssl/ossl_typ.h> +#include <openssl/types.h> #include "x509_local.h" X509_LOOKUP_METHOD *X509_LOOKUP_meth_new(const char *name) @@ -24,7 +24,7 @@ X509_LOOKUP_METHOD *X509_LOOKUP_meth_new(const char *name) if (method != NULL) { method->name = OPENSSL_strdup(name); if (method->name == NULL) { - X509err(X509_F_X509_LOOKUP_METH_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } } diff --git a/crypto/x509/x509_obj.c b/crypto/x509/x509_obj.c index f54d483cc4dd..12c6d6f78b6e 100644 --- a/crypto/x509/x509_obj.c +++ b/crypto/x509/x509_obj.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -13,6 +13,7 @@ #include <openssl/x509.h> #include <openssl/buffer.h> #include "crypto/x509.h" +#include "crypto/ctype.h" /* * Limit to ensure we don't overflow: much greater than @@ -26,6 +27,7 @@ char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) const X509_NAME_ENTRY *ne; int i; int n, lold, l, l1, l2, num, j, type; + int prev_set = -1; const char *s; char *p; unsigned char *q; @@ -71,7 +73,7 @@ char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) type = ne->value->type; num = ne->value->length; if (num > NAME_ONELINE_MAX) { - X509err(X509_F_X509_NAME_ONELINE, X509_R_NAME_TOO_LONG); + ERR_raise(ERR_LIB_X509, X509_R_NAME_TOO_LONG); goto end; } q = ne->value->data; @@ -107,20 +109,17 @@ char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) if (!gs_doit[j & 3]) continue; l2++; -#ifndef CHARSET_EBCDIC - if ((q[j] < ' ') || (q[j] > '~')) - l2 += 3; -#else - if ((os_toascii[q[j]] < os_toascii[' ']) || - (os_toascii[q[j]] > os_toascii['~'])) + if (q[j] == '/' || q[j] == '+') + l2++; /* char needs to be escaped */ + else if ((ossl_toascii(q[j]) < ossl_toascii(' ')) || + (ossl_toascii(q[j]) > ossl_toascii('~'))) l2 += 3; -#endif } lold = l; l += 1 + l1 + 1 + l2; if (l > NAME_ONELINE_MAX) { - X509err(X509_F_X509_NAME_ONELINE, X509_R_NAME_TOO_LONG); + ERR_raise(ERR_LIB_X509, X509_R_NAME_TOO_LONG); goto end; } if (b != NULL) { @@ -131,7 +130,7 @@ char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) break; } else p = &(buf[lold]); - *(p++) = '/'; + *(p++) = prev_set == ne->set ? '+' : '/'; memcpy(p, s, (unsigned int)l1); p += l1; *(p++) = '='; @@ -150,8 +149,11 @@ char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) *(p++) = 'x'; *(p++) = hex[(n >> 4) & 0x0f]; *(p++) = hex[n & 0x0f]; - } else + } else { + if (n == '/' || n == '+') + *(p++) = '\\'; *(p++) = n; + } #else n = os_toascii[q[j]]; if ((n < os_toascii[' ']) || (n > os_toascii['~'])) { @@ -159,11 +161,15 @@ char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) *(p++) = 'x'; *(p++) = hex[(n >> 4) & 0x0f]; *(p++) = hex[n & 0x0f]; - } else + } else { + if (n == os_toascii['/'] || n == os_toascii['+']) + *(p++) = '\\'; *(p++) = q[j]; + } #endif } *p = '\0'; + prev_set = ne->set; } if (b != NULL) { p = b->data; @@ -174,7 +180,7 @@ char *X509_NAME_oneline(const X509_NAME *a, char *buf, int len) *p = '\0'; return p; err: - X509err(X509_F_X509_NAME_ONELINE, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); end: BUF_MEM_free(b); return NULL; diff --git a/crypto/x509/x509_r2x.c b/crypto/x509/x509_r2x.c index 6b1623feacb7..c7f6181c4465 100644 --- a/crypto/x509/x509_r2x.c +++ b/crypto/x509/x509_r2x.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -21,11 +21,11 @@ X509 *X509_REQ_to_X509(X509_REQ *r, int days, EVP_PKEY *pkey) { X509 *ret = NULL; X509_CINF *xi = NULL; - X509_NAME *xn; + const X509_NAME *xn; EVP_PKEY *pubkey = NULL; if ((ret = X509_new()) == NULL) { - X509err(X509_F_X509_REQ_TO_X509, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } diff --git a/crypto/x509/x509_req.c b/crypto/x509/x509_req.c index c2b8cb9f3e2d..0434fbbc6b70 100644 --- a/crypto/x509/x509_req.c +++ b/crypto/x509/x509_req.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -26,9 +26,9 @@ X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, const EVP_MD *md) int i; EVP_PKEY *pktmp; - ret = X509_REQ_new(); + ret = X509_REQ_new_ex(x->libctx, x->propq); if (ret == NULL) { - X509err(X509_F_X509_TO_X509_REQ, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } @@ -85,33 +85,18 @@ int X509_REQ_check_private_key(X509_REQ *x, EVP_PKEY *k) int ok = 0; xk = X509_REQ_get_pubkey(x); - switch (EVP_PKEY_cmp(xk, k)) { + switch (EVP_PKEY_eq(xk, k)) { case 1: ok = 1; break; case 0: - X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, - X509_R_KEY_VALUES_MISMATCH); + ERR_raise(ERR_LIB_X509, X509_R_KEY_VALUES_MISMATCH); break; case -1: - X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, X509_R_KEY_TYPE_MISMATCH); + ERR_raise(ERR_LIB_X509, X509_R_KEY_TYPE_MISMATCH); break; case -2: -#ifndef OPENSSL_NO_EC - if (EVP_PKEY_id(k) == EVP_PKEY_EC) { - X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, ERR_R_EC_LIB); - break; - } -#endif -#ifndef OPENSSL_NO_DH - if (EVP_PKEY_id(k) == EVP_PKEY_DH) { - /* No idea */ - X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, - X509_R_CANT_CHECK_DH_KEY); - break; - } -#endif - X509err(X509_F_X509_REQ_CHECK_PRIVATE_KEY, X509_R_UNKNOWN_KEY_TYPE); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_KEY_TYPE); } EVP_PKEY_free(xk); @@ -131,6 +116,7 @@ static int *ext_nids = ext_nid_list; int X509_REQ_extension_nid(int req_nid) { int i, nid; + for (i = 0;; i++) { nid = ext_nids[i]; if (nid == NID_undef) @@ -157,7 +143,7 @@ STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req) int idx, *pnid; const unsigned char *p; - if ((req == NULL) || !ext_nids) + if (req == NULL || !ext_nids) return NULL; for (pnid = ext_nids; *pnid != NID_undef; pnid++) { idx = X509_REQ_get_attr_by_NID(req, *pnid, -1); @@ -181,15 +167,15 @@ STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req) * Add a STACK_OF extensions to a certificate request: allow alternative OIDs * in case we want to create a non standard one. */ - -int X509_REQ_add_extensions_nid(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts, - int nid) +int X509_REQ_add_extensions_nid(X509_REQ *req, + const STACK_OF(X509_EXTENSION) *exts, int nid) { int extlen; int rv = 0; unsigned char *ext = NULL; + /* Generate encoding of extensions */ - extlen = ASN1_item_i2d((ASN1_VALUE *)exts, &ext, + extlen = ASN1_item_i2d((const ASN1_VALUE *)exts, &ext, ASN1_ITEM_rptr(X509_EXTENSIONS)); if (extlen <= 0) return 0; @@ -199,7 +185,7 @@ int X509_REQ_add_extensions_nid(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts, } /* This is the normal usage: use the "official" OID */ -int X509_REQ_add_extensions(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts) +int X509_REQ_add_extensions(X509_REQ *req, const STACK_OF(X509_EXTENSION) *exts) { return X509_REQ_add_extensions_nid(req, exts, NID_ext_req); } @@ -229,8 +215,13 @@ X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *req, int loc) X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc) { - X509_ATTRIBUTE *attr = X509at_delete_attr(req->req_info.attributes, loc); + X509_ATTRIBUTE *attr; + if (req == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + attr = X509at_delete_attr(req->req_info.attributes, loc); if (attr != NULL) req->req_info.enc.modified = 1; return attr; @@ -238,6 +229,10 @@ X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc) int X509_REQ_add1_attr(X509_REQ *req, X509_ATTRIBUTE *attr) { + if (req == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } if (!X509at_add1_attr(&req->req_info.attributes, attr)) return 0; req->req_info.enc.modified = 1; @@ -248,6 +243,10 @@ int X509_REQ_add1_attr_by_OBJ(X509_REQ *req, const ASN1_OBJECT *obj, int type, const unsigned char *bytes, int len) { + if (req == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } if (!X509at_add1_attr_by_OBJ(&req->req_info.attributes, obj, type, bytes, len)) return 0; @@ -259,6 +258,10 @@ int X509_REQ_add1_attr_by_NID(X509_REQ *req, int nid, int type, const unsigned char *bytes, int len) { + if (req == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } if (!X509at_add1_attr_by_NID(&req->req_info.attributes, nid, type, bytes, len)) return 0; @@ -270,6 +273,10 @@ int X509_REQ_add1_attr_by_txt(X509_REQ *req, const char *attrname, int type, const unsigned char *bytes, int len) { + if (req == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } if (!X509at_add1_attr_by_txt(&req->req_info.attributes, attrname, type, bytes, len)) return 0; @@ -299,7 +306,7 @@ void X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig, void X509_REQ_set0_signature(X509_REQ *req, ASN1_BIT_STRING *psig) { if (req->signature) - ASN1_BIT_STRING_free(req->signature); + ASN1_BIT_STRING_free(req->signature); req->signature = psig; } @@ -315,6 +322,10 @@ int X509_REQ_get_signature_nid(const X509_REQ *req) int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp) { + if (req == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } req->req_info.enc.modified = 1; return i2d_X509_REQ_INFO(&req->req_info, pp); } diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c index 164b4e2be136..d8ddde8aaa51 100644 --- a/crypto/x509/x509_set.c +++ b/crypto/x509/x509_set.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -47,21 +47,21 @@ int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial) return 1; } -int X509_set_issuer_name(X509 *x, X509_NAME *name) +int X509_set_issuer_name(X509 *x, const X509_NAME *name) { if (x == NULL) return 0; return X509_NAME_set(&x->cert_info.issuer, name); } -int X509_set_subject_name(X509 *x, X509_NAME *name) +int X509_set_subject_name(X509 *x, const X509_NAME *name) { if (x == NULL) return 0; return X509_NAME_set(&x->cert_info.subject, name); } -int x509_set1_time(ASN1_TIME **ptm, const ASN1_TIME *tm) +int ossl_x509_set1_time(ASN1_TIME **ptm, const ASN1_TIME *tm) { ASN1_TIME *in; in = *ptm; @@ -79,14 +79,14 @@ int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm) { if (x == NULL) return 0; - return x509_set1_time(&x->cert_info.validity.notBefore, tm); + return ossl_x509_set1_time(&x->cert_info.validity.notBefore, tm); } int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm) { if (x == NULL) return 0; - return x509_set1_time(&x->cert_info.validity.notAfter, tm); + return ossl_x509_set1_time(&x->cert_info.validity.notAfter, tm); } int X509_set_pubkey(X509 *x, EVP_PKEY *pkey) @@ -192,46 +192,85 @@ int X509_get_signature_info(X509 *x, int *mdnid, int *pknid, int *secbits, return X509_SIG_INFO_get(&x->siginf, mdnid, pknid, secbits, flags); } -static void x509_sig_info_init(X509_SIG_INFO *siginf, const X509_ALGOR *alg, - const ASN1_STRING *sig) +/* Modify *siginf according to alg and sig. Return 1 on success, else 0. */ +static int x509_sig_info_init(X509_SIG_INFO *siginf, const X509_ALGOR *alg, + const ASN1_STRING *sig) { int pknid, mdnid; const EVP_MD *md; + const EVP_PKEY_ASN1_METHOD *ameth; siginf->mdnid = NID_undef; siginf->pknid = NID_undef; siginf->secbits = -1; siginf->flags = 0; if (!OBJ_find_sigid_algs(OBJ_obj2nid(alg->algorithm), &mdnid, &pknid) - || pknid == NID_undef) - return; + || pknid == NID_undef) { + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_SIGID_ALGS); + return 0; + } + siginf->mdnid = mdnid; siginf->pknid = pknid; - if (mdnid == NID_undef) { + + switch (mdnid) { + case NID_undef: /* If we have one, use a custom handler for this algorithm */ - const EVP_PKEY_ASN1_METHOD *ameth = EVP_PKEY_asn1_find(NULL, pknid); + ameth = EVP_PKEY_asn1_find(NULL, pknid); if (ameth == NULL || ameth->siginf_set == NULL - || ameth->siginf_set(siginf, alg, sig) == 0) - return; - siginf->flags |= X509_SIG_INFO_VALID; - return; + || !ameth->siginf_set(siginf, alg, sig)) { + ERR_raise(ERR_LIB_X509, X509_R_ERROR_USING_SIGINF_SET); + return 0; + } + break; + /* + * SHA1 and MD5 are known to be broken. Reduce security bits so that + * they're no longer accepted at security level 1. + * The real values don't really matter as long as they're lower than 80, + * which is our security level 1. + */ + case NID_sha1: + /* + * https://eprint.iacr.org/2020/014 puts a chosen-prefix attack + * for SHA1 at2^63.4 + */ + siginf->secbits = 63; + break; + case NID_md5: + /* + * https://documents.epfl.ch/users/l/le/lenstra/public/papers/lat.pdf + * puts a chosen-prefix attack for MD5 at 2^39. + */ + siginf->secbits = 39; + break; + case NID_id_GostR3411_94: + /* + * There is a collision attack on GOST R 34.11-94 at 2^105, see + * https://link.springer.com/chapter/10.1007%2F978-3-540-85174-5_10 + */ + siginf->secbits = 105; + break; + default: + /* Security bits: half number of bits in digest */ + if ((md = EVP_get_digestbynid(mdnid)) == NULL) { + ERR_raise(ERR_LIB_X509, X509_R_ERROR_GETTING_MD_BY_NID); + return 0; + } + siginf->secbits = EVP_MD_get_size(md) * 4; + break; } - siginf->flags |= X509_SIG_INFO_VALID; - siginf->mdnid = mdnid; - md = EVP_get_digestbynid(mdnid); - if (md == NULL) - return; - /* Security bits: half number of bits in digest */ - siginf->secbits = EVP_MD_size(md) * 4; switch (mdnid) { - case NID_sha1: - case NID_sha256: - case NID_sha384: - case NID_sha512: + case NID_sha1: + case NID_sha256: + case NID_sha384: + case NID_sha512: siginf->flags |= X509_SIG_INFO_TLS; } + siginf->flags |= X509_SIG_INFO_VALID; + return 1; } -void x509_init_sig_info(X509 *x) +/* Returns 1 on success, 0 on failure */ +int ossl_x509_init_sig_info(X509 *x) { - x509_sig_info_init(&x->siginf, &x->sig_alg, &x->signature); + return x509_sig_info_init(&x->siginf, &x->sig_alg, &x->signature); } diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trust.c index a10d437735b8..fd77b0c6fe61 100644 --- a/crypto/x509/x509_trs.c +++ b/crypto/x509/x509_trust.c @@ -1,7 +1,7 @@ /* - * Copyright 1999-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1999-2022 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -72,7 +72,7 @@ int X509_check_trust(X509 *x, int id, int flags) return obj_trust(NID_anyExtendedKeyUsage, x, flags | X509_TRUST_DO_SS_COMPAT); idx = X509_TRUST_get_by_id(id); - if (idx == -1) + if (idx < 0) return default_trust(id, x, flags); pt = X509_TRUST_get0(idx); return pt->check_trust(pt, x, flags); @@ -112,8 +112,8 @@ int X509_TRUST_get_by_id(int id) int X509_TRUST_set(int *t, int trust) { - if (X509_TRUST_get_by_id(trust) == -1) { - X509err(X509_F_X509_TRUST_SET, X509_R_INVALID_TRUST); + if (X509_TRUST_get_by_id(trust) < 0) { + ERR_raise(ERR_LIB_X509, X509_R_INVALID_TRUST); return 0; } *t = trust; @@ -134,9 +134,9 @@ int X509_TRUST_add(int id, int flags, int (*ck) (X509_TRUST *, X509 *, int), /* Get existing entry if any */ idx = X509_TRUST_get_by_id(id); /* Need a new entry */ - if (idx == -1) { + if (idx < 0) { if ((trtmp = OPENSSL_malloc(sizeof(*trtmp))) == NULL) { - X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return 0; } trtmp->flags = X509_TRUST_DYNAMIC; @@ -148,7 +148,7 @@ int X509_TRUST_add(int id, int flags, int (*ck) (X509_TRUST *, X509 *, int), OPENSSL_free(trtmp->name); /* dup supplied name */ if ((trtmp->name = OPENSSL_strdup(name)) == NULL) { - X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } /* Keep the dynamic flag of existing entry */ @@ -162,20 +162,20 @@ int X509_TRUST_add(int id, int flags, int (*ck) (X509_TRUST *, X509 *, int), trtmp->arg2 = arg2; /* If its a new entry manage the dynamic table */ - if (idx == -1) { + if (idx < 0) { if (trtable == NULL && (trtable = sk_X509_TRUST_new(tr_cmp)) == NULL) { - X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err;; } if (!sk_X509_TRUST_push(trtable, trtmp)) { - X509err(X509_F_X509_TRUST_ADD, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } } return 1; err: - if (idx == -1) { + if (idx < 0) { OPENSSL_free(trtmp->name); OPENSSL_free(trtmp); } @@ -184,7 +184,7 @@ int X509_TRUST_add(int id, int flags, int (*ck) (X509_TRUST *, X509 *, int), static void trtable_free(X509_TRUST *p) { - if (!p) + if (p == NULL) return; if (p->flags & X509_TRUST_DYNAMIC) { if (p->flags & X509_TRUST_DYNAMIC_NAME) @@ -220,7 +220,7 @@ static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags) * Declare the chain verified if the desired trust OID is not rejected in * any auxiliary trust info for this certificate, and the OID is either * expressly trusted, or else either "anyEKU" is trusted, or the - * certificate is self-signed. + * certificate is self-signed and X509_TRUST_NO_SS_COMPAT is not set. */ flags |= X509_TRUST_DO_SS_COMPAT | X509_TRUST_OK_ANY_EKU; return obj_trust(trust->arg1, x, flags); @@ -239,7 +239,7 @@ static int trust_1oid(X509_TRUST *trust, X509 *x, int flags) static int trust_compat(X509_TRUST *trust, X509 *x, int flags) { - /* Call for side-effect of computing hash and caching extensions */ + /* Call for side-effect of setting EXFLAG_SS for self-signed-certs */ if (X509_check_purpose(x, -1, 0) != 1) return X509_TRUST_UNTRUSTED; if ((flags & X509_TRUST_NO_SS_COMPAT) == 0 && (x->ex_flags & EXFLAG_SS)) diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c index 02bde640d8e8..61d41117e2c0 100644 --- a/crypto/x509/x509_txt.c +++ b/crypto/x509/x509_txt.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -58,9 +58,9 @@ const char *X509_verify_cert_error_string(long n) case X509_V_ERR_OUT_OF_MEM: return "out of memory"; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - return "self signed certificate"; + return "self-signed certificate"; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - return "self signed certificate in certificate chain"; + return "self-signed certificate in certificate chain"; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: return "unable to get local issuer certificate"; case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: @@ -69,12 +69,12 @@ const char *X509_verify_cert_error_string(long n) return "certificate chain too long"; case X509_V_ERR_CERT_REVOKED: return "certificate revoked"; - case X509_V_ERR_INVALID_CA: - return "invalid CA certificate"; + case X509_V_ERR_NO_ISSUER_PUBLIC_KEY: + return "issuer certificate doesn't have a public key"; case X509_V_ERR_PATH_LENGTH_EXCEEDED: return "path length constraint exceeded"; case X509_V_ERR_INVALID_PURPOSE: - return "unsupported certificate purpose"; + return "unsuitable certificate purpose"; case X509_V_ERR_CERT_UNTRUSTED: return "certificate not trusted"; case X509_V_ERR_CERT_REJECTED: @@ -111,9 +111,9 @@ const char *X509_verify_cert_error_string(long n) case X509_V_ERR_NO_EXPLICIT_POLICY: return "no explicit policy"; case X509_V_ERR_DIFFERENT_CRL_SCOPE: - return "Different CRL scope"; + return "different CRL scope"; case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: - return "Unsupported extension feature"; + return "unsupported extension feature"; case X509_V_ERR_UNNESTED_RESOURCE: return "RFC 3779 resource not subset of parent's resources"; case X509_V_ERR_PERMITTED_VIOLATION: @@ -133,7 +133,7 @@ const char *X509_verify_cert_error_string(long n) case X509_V_ERR_CRL_PATH_VALIDATION_ERROR: return "CRL path validation error"; case X509_V_ERR_PATH_LOOP: - return "Path Loop"; + return "path loop"; case X509_V_ERR_SUITE_B_INVALID_VERSION: return "Suite B: certificate version invalid"; case X509_V_ERR_SUITE_B_INVALID_ALGORITHM: @@ -147,13 +147,13 @@ const char *X509_verify_cert_error_string(long n) case X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256: return "Suite B: cannot sign P-384 with P-256"; case X509_V_ERR_HOSTNAME_MISMATCH: - return "Hostname mismatch"; + return "hostname mismatch"; case X509_V_ERR_EMAIL_MISMATCH: - return "Email address mismatch"; + return "email address mismatch"; case X509_V_ERR_IP_ADDRESS_MISMATCH: return "IP address mismatch"; case X509_V_ERR_DANE_NO_MATCH: - return "No matching DANE TLSA records"; + return "no matching DANE TLSA records"; case X509_V_ERR_EE_KEY_TOO_SMALL: return "EE certificate key too weak"; case X509_V_ERR_CA_KEY_TOO_SMALL: @@ -161,9 +161,9 @@ const char *X509_verify_cert_error_string(long n) case X509_V_ERR_CA_MD_TOO_WEAK: return "CA signature digest algorithm too weak"; case X509_V_ERR_INVALID_CALL: - return "Invalid certificate verification context"; + return "invalid certificate verification context"; case X509_V_ERR_STORE_LOOKUP: - return "Issuer certificate lookup error"; + return "issuer certificate lookup error"; case X509_V_ERR_NO_VALID_SCTS: return "Certificate Transparency required, but no valid SCTs found"; case X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION: @@ -174,9 +174,50 @@ const char *X509_verify_cert_error_string(long n) return "OCSP verification failed"; case X509_V_ERR_OCSP_CERT_UNKNOWN: return "OCSP unknown cert"; + case X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM: + return "Cannot find certificate signature algorithm"; + case X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH: + return "subject signature algorithm and issuer public key algorithm mismatch"; + case X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY: + return "cert info signature and signature algorithm mismatch"; + case X509_V_ERR_INVALID_CA: + return "invalid CA certificate"; + case X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA: + return "Path length invalid for non-CA cert"; + case X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN: + return "Path length given without key usage keyCertSign"; + case X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA: + return "Key usage keyCertSign invalid for non-CA cert"; + case X509_V_ERR_ISSUER_NAME_EMPTY: + return "Issuer name empty"; + case X509_V_ERR_SUBJECT_NAME_EMPTY: + return "Subject name empty"; + case X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER: + return "Missing Authority Key Identifier"; + case X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER: + return "Missing Subject Key Identifier"; + case X509_V_ERR_EMPTY_SUBJECT_ALT_NAME: + return "Empty Subject Alternative Name extension"; + case X509_V_ERR_CA_BCONS_NOT_CRITICAL: + return "Basic Constraints of CA cert not marked critical"; + case X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL: + return "Subject empty and Subject Alt Name extension not critical"; + case X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL: + return "Authority Key Identifier marked critical"; + case X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL: + return "Subject Key Identifier marked critical"; + case X509_V_ERR_CA_CERT_MISSING_KEY_USAGE: + return "CA cert does not include key usage extension"; + case X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3: + return "Using cert extension requires at least X509v3"; case X509_V_ERR_EC_KEY_EXPLICIT_PARAMS: return "Certificate public key has explicit ECC parameters"; + /* + * Entries must be kept consistent with include/openssl/x509_vfy.h.in + * and with doc/man3/X509_STORE_CTX_get_error.pod + */ + default: /* Printing an error number into a static buffer is not thread-safe */ return "unknown certificate verification error"; diff --git a/crypto/x509/x509_v3.c b/crypto/x509/x509_v3.c index c7876023304c..62ae7d6b8d62 100644 --- a/crypto/x509/x509_v3.c +++ b/crypto/x509/x509_v3.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -19,9 +19,12 @@ int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x) { + int ret; + if (x == NULL) return 0; - return sk_X509_EXTENSION_num(x); + ret = sk_X509_EXTENSION_num(x); + return ret > 0 ? ret : 0; } int X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x, int nid, @@ -101,7 +104,7 @@ STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x, STACK_OF(X509_EXTENSION) *sk = NULL; if (x == NULL) { - X509err(X509_F_X509V3_ADD_EXT, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); goto err2; } @@ -125,7 +128,7 @@ STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x, *x = sk; return sk; err: - X509err(X509_F_X509V3_ADD_EXT, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); err2: X509_EXTENSION_free(new_ex); if (x != NULL && *x == NULL) @@ -142,7 +145,7 @@ X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, int nid, obj = OBJ_nid2obj(nid); if (obj == NULL) { - X509err(X509_F_X509_EXTENSION_CREATE_BY_NID, X509_R_UNKNOWN_NID); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_NID); return NULL; } ret = X509_EXTENSION_create_by_OBJ(ex, obj, crit, data); @@ -159,8 +162,7 @@ X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **ex, if ((ex == NULL) || (*ex == NULL)) { if ((ret = X509_EXTENSION_new()) == NULL) { - X509err(X509_F_X509_EXTENSION_CREATE_BY_OBJ, - ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } } else diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 925fbb541258..d19efeaa9919 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -1,12 +1,14 @@ /* - * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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/deprecated.h" + #include <stdio.h> #include <time.h> #include <errno.h> @@ -21,47 +23,23 @@ #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/objects.h> +#include <openssl/core_names.h> #include "internal/dane.h" #include "crypto/x509.h" #include "x509_local.h" /* CRL score values */ -/* No unhandled critical extensions */ - -#define CRL_SCORE_NOCRITICAL 0x100 - -/* certificate is within CRL scope */ - -#define CRL_SCORE_SCOPE 0x080 - -/* CRL times valid */ - -#define CRL_SCORE_TIME 0x040 - -/* Issuer name matches certificate */ - -#define CRL_SCORE_ISSUER_NAME 0x020 - -/* If this score or above CRL is probably valid */ - -#define CRL_SCORE_VALID (CRL_SCORE_NOCRITICAL|CRL_SCORE_TIME|CRL_SCORE_SCOPE) - -/* CRL issuer is certificate issuer */ - -#define CRL_SCORE_ISSUER_CERT 0x018 - -/* CRL issuer is on certificate path */ - -#define CRL_SCORE_SAME_PATH 0x008 - -/* CRL issuer matches CRL AKID */ - -#define CRL_SCORE_AKID 0x004 - -/* Have a delta CRL with valid times */ - -#define CRL_SCORE_TIME_DELTA 0x002 +#define CRL_SCORE_NOCRITICAL 0x100 /* No unhandled critical extensions */ +#define CRL_SCORE_SCOPE 0x080 /* certificate is within CRL scope */ +#define CRL_SCORE_TIME 0x040 /* CRL times valid */ +#define CRL_SCORE_ISSUER_NAME 0x020 /* Issuer name matches certificate */ +#define CRL_SCORE_VALID /* If this score or above CRL is probably valid */ \ + (CRL_SCORE_NOCRITICAL | CRL_SCORE_TIME | CRL_SCORE_SCOPE) +#define CRL_SCORE_ISSUER_CERT 0x018 /* CRL issuer is certificate issuer */ +#define CRL_SCORE_SAME_PATH 0x008 /* CRL issuer is on certificate path */ +#define CRL_SCORE_AKID 0x004 /* CRL issuer matches CRL AKID */ +#define CRL_SCORE_TIME_DELTA 0x002 /* Have a delta CRL with valid times */ static int build_chain(X509_STORE_CTX *ctx); static int verify_chain(X509_STORE_CTX *ctx); @@ -69,7 +47,7 @@ static int dane_verify(X509_STORE_CTX *ctx); static int null_callback(int ok, X509_STORE_CTX *e); static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); -static int check_chain_extensions(X509_STORE_CTX *ctx); +static int check_extensions(X509_STORE_CTX *ctx); static int check_name_constraints(X509_STORE_CTX *ctx); static int check_id(X509_STORE_CTX *ctx); static int check_trust(X509_STORE_CTX *ctx, int num_untrusted); @@ -105,64 +83,90 @@ static int null_callback(int ok, X509_STORE_CTX *e) return ok; } -/* - * Return 1 if given cert is considered self-signed, 0 if not or on error. - * This does not verify self-signedness but relies on x509v3_cache_extensions() - * matching issuer and subject names (i.e., the cert being self-issued) and any - * present authority key identifier matching the subject key identifier, etc. +/*- + * Return 1 if given cert is considered self-signed, 0 if not, or -1 on error. + * This actually verifies self-signedness only if requested. + * It calls ossl_x509v3_cache_extensions() + * to match issuer and subject names (i.e., the cert being self-issued) and any + * present authority key identifier to match the subject key identifier, etc. */ -static int cert_self_signed(X509 *x) +int X509_self_signed(X509 *cert, int verify_signature) { - if (X509_check_purpose(x, -1, 0) != 1) + EVP_PKEY *pkey; + + if ((pkey = X509_get0_pubkey(cert)) == NULL) { /* handles cert == NULL */ + ERR_raise(ERR_LIB_X509, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY); + return -1; + } + if (!ossl_x509v3_cache_extensions(cert)) + return -1; + if ((cert->ex_flags & EXFLAG_SS) == 0) return 0; - if (x->ex_flags & EXFLAG_SS) + if (!verify_signature) return 1; - else - return 0; + return X509_verify(cert, pkey); } -/* Given a certificate try and find an exact match in the store */ - -static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x) +/* + * Given a certificate, try and find an exact match in the store. + * Returns 1 on success, 0 on not found, -1 on internal error. + */ +static int lookup_cert_match(X509 **result, X509_STORE_CTX *ctx, X509 *x) { STACK_OF(X509) *certs; X509 *xtmp = NULL; - int i; + int i, ret; + + *result = NULL; /* Lookup all certs with matching subject name */ + ERR_set_mark(); certs = ctx->lookup_certs(ctx, X509_get_subject_name(x)); + ERR_pop_to_mark(); if (certs == NULL) - return NULL; + return -1; /* Look for exact match */ for (i = 0; i < sk_X509_num(certs); i++) { xtmp = sk_X509_value(certs, i); - if (!X509_cmp(xtmp, x)) + if (X509_cmp(xtmp, x) == 0) break; xtmp = NULL; } - if (xtmp != NULL && !X509_up_ref(xtmp)) - xtmp = NULL; + ret = xtmp != NULL; + if (ret) { + if (!X509_up_ref(xtmp)) + ret = -1; + else + *result = xtmp; + } sk_X509_pop_free(certs, X509_free); - return xtmp; + return ret; } /*- * Inform the verify callback of an error. - * If B<x> is not NULL it is the error cert, otherwise use the chain cert at - * B<depth>. - * If B<err> is not X509_V_OK, that's the error value, otherwise leave - * unchanged (presumably set by the caller). + * The error code is set to |err| if |err| is not X509_V_OK, else + * |ctx->error| is left unchanged (under the assumption it is set elsewhere). + * The error depth is |depth| if >= 0, else it defaults to |ctx->error_depth|. + * The error cert is |x| if not NULL, else defaults to the chain cert at depth. * * Returns 0 to abort verification with an error, non-zero to continue. */ static int verify_cb_cert(X509_STORE_CTX *ctx, X509 *x, int depth, int err) { - ctx->error_depth = depth; + if (depth < 0) + depth = ctx->error_depth; + else + ctx->error_depth = depth; ctx->current_cert = (x != NULL) ? x : sk_X509_value(ctx->chain, depth); if (err != X509_V_OK) ctx->error = err; return ctx->verify_cb(0, ctx); } +#define CB_FAIL_IF(cond, ctx, cert, depth, err) \ + if ((cond) && verify_cb_cert(ctx, cert, depth, err) == 0) \ + return 0 + /*- * Inform the verify callback of an error, CRL-specific variant. Here, the * error depth and certificate are already set, we just specify the error @@ -191,73 +195,79 @@ static int check_auth_level(X509_STORE_CTX *ctx) * We've already checked the security of the leaf key, so here we only * check the security of issuer keys. */ - if (i > 0 && !check_key_level(ctx, cert) && - verify_cb_cert(ctx, cert, i, X509_V_ERR_CA_KEY_TOO_SMALL) == 0) - return 0; + CB_FAIL_IF(i > 0 && !check_key_level(ctx, cert), + ctx, cert, i, X509_V_ERR_CA_KEY_TOO_SMALL); /* * We also check the signature algorithm security of all certificates * except those of the trust anchor at index num-1. */ - if (i < num - 1 && !check_sig_level(ctx, cert) && - verify_cb_cert(ctx, cert, i, X509_V_ERR_CA_MD_TOO_WEAK) == 0) - return 0; + CB_FAIL_IF(i < num - 1 && !check_sig_level(ctx, cert), + ctx, cert, i, X509_V_ERR_CA_MD_TOO_WEAK); } return 1; } +/* Returns -1 on internal error */ static int verify_chain(X509_STORE_CTX *ctx) { int err; int ok; - /* - * Before either returning with an error, or continuing with CRL checks, - * instantiate chain public key parameters. - */ - if ((ok = build_chain(ctx)) == 0 || - (ok = check_chain_extensions(ctx)) == 0 || - (ok = check_auth_level(ctx)) == 0 || - (ok = check_id(ctx)) == 0 || 1) - X509_get_pubkey_parameters(NULL, ctx->chain); - if (ok == 0 || (ok = ctx->check_revocation(ctx)) == 0) + if ((ok = build_chain(ctx)) <= 0 + || (ok = check_extensions(ctx)) <= 0 + || (ok = check_auth_level(ctx)) <= 0 + || (ok = check_id(ctx)) <= 0 + || (ok = X509_get_pubkey_parameters(NULL, ctx->chain) ? 1 : -1) <= 0 + || (ok = ctx->check_revocation(ctx)) <= 0) return ok; err = X509_chain_check_suiteb(&ctx->error_depth, NULL, ctx->chain, ctx->param->flags); - if (err != X509_V_OK) { - if ((ok = verify_cb_cert(ctx, NULL, ctx->error_depth, err)) == 0) - return ok; - } + CB_FAIL_IF(err != X509_V_OK, ctx, NULL, ctx->error_depth, err); /* Verify chain signatures and expiration times */ - ok = (ctx->verify != NULL) ? ctx->verify(ctx) : internal_verify(ctx); - if (!ok) + ok = ctx->verify != NULL ? ctx->verify(ctx) : internal_verify(ctx); + if (ok <= 0) return ok; - if ((ok = check_name_constraints(ctx)) == 0) + if ((ok = check_name_constraints(ctx)) <= 0) return ok; #ifndef OPENSSL_NO_RFC3779 /* RFC 3779 path validation, now that CRL check has been done */ - if ((ok = X509v3_asid_validate_path(ctx)) == 0) + if ((ok = X509v3_asid_validate_path(ctx)) <= 0) return ok; - if ((ok = X509v3_addr_validate_path(ctx)) == 0) + if ((ok = X509v3_addr_validate_path(ctx)) <= 0) return ok; #endif /* If we get this far evaluate policies */ - if (ctx->param->flags & X509_V_FLAG_POLICY_CHECK) + if ((ctx->param->flags & X509_V_FLAG_POLICY_CHECK) != 0) ok = ctx->check_policy(ctx); return ok; } +int X509_STORE_CTX_verify(X509_STORE_CTX *ctx) +{ + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return -1; + } + if (ctx->cert == NULL && sk_X509_num(ctx->untrusted) >= 1) + ctx->cert = sk_X509_value(ctx->untrusted, 0); + return X509_verify_cert(ctx); +} + int X509_verify_cert(X509_STORE_CTX *ctx) { - SSL_DANE *dane = ctx->dane; int ret; + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return -1; + } if (ctx->cert == NULL) { - X509err(X509_F_X509_VERIFY_CERT, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); + ERR_raise(ERR_LIB_X509, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); ctx->error = X509_V_ERR_INVALID_CALL; return -1; } @@ -267,40 +277,22 @@ int X509_verify_cert(X509_STORE_CTX *ctx) * This X509_STORE_CTX has already been used to verify a cert. We * cannot do another one. */ - X509err(X509_F_X509_VERIFY_CERT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + ERR_raise(ERR_LIB_X509, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); ctx->error = X509_V_ERR_INVALID_CALL; return -1; } - if (!X509_up_ref(ctx->cert)) { - X509err(X509_F_X509_VERIFY_CERT, ERR_R_INTERNAL_ERROR); - ctx->error = X509_V_ERR_UNSPECIFIED; - return -1; - } - - /* - * first we make sure the chain we are going to build is present and that - * the first entry is in place - */ - if ((ctx->chain = sk_X509_new_null()) == NULL - || !sk_X509_push(ctx->chain, ctx->cert)) { - X509_free(ctx->cert); - X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); + if (!ossl_x509_add_cert_new(&ctx->chain, ctx->cert, X509_ADD_FLAG_UP_REF)) { ctx->error = X509_V_ERR_OUT_OF_MEM; return -1; } - ctx->num_untrusted = 1; /* If the peer's public key is too weak, we can stop early. */ - if (!check_key_level(ctx, ctx->cert) && - !verify_cb_cert(ctx, ctx->cert, 0, X509_V_ERR_EE_KEY_TOO_SMALL)) - return 0; + CB_FAIL_IF(!check_key_level(ctx, ctx->cert), + ctx, ctx->cert, 0, X509_V_ERR_EE_KEY_TOO_SMALL); - if (DANETLS_ENABLED(dane)) - ret = dane_verify(ctx); - else - ret = verify_chain(ctx); + ret = DANETLS_ENABLED(ctx->dane) ? dane_verify(ctx) : verify_chain(ctx); /* * Safety-net. If we are returning an error, we must also set ctx->error, @@ -323,10 +315,10 @@ static int sk_X509_contains(STACK_OF(X509) *sk, X509 *cert) } /* - * Find in given STACK_OF(X509) sk an issuer cert of given cert x. - * The issuer must not yet be in ctx->chain, where the exceptional case - * that x is self-issued and ctx->chain has just one element is allowed. - * Prefer the first one that is not expired, else take the last expired one. + * Find in given STACK_OF(X509) |sk| an issuer cert (if any) of given cert |x|. + * The issuer must not yet be in |ctx->chain|, yet allowing the exception that + * |x| is self-issued and |ctx->chain| has just one element. + * Prefer the first non-expired one, else take the most recently expired one. */ static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) { @@ -338,56 +330,60 @@ static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) if (ctx->check_issued(ctx, x, issuer) && (((x->ex_flags & EXFLAG_SI) != 0 && sk_X509_num(ctx->chain) == 1) || !sk_X509_contains(ctx->chain, issuer))) { - rv = issuer; - if (x509_check_cert_time(ctx, rv, -1)) - break; + if (ossl_x509_check_cert_time(ctx, issuer, -1)) + return issuer; + if (rv == NULL || ASN1_TIME_compare(X509_get0_notAfter(issuer), + X509_get0_notAfter(rv)) > 0) + rv = issuer; } } return rv; } /* Check that the given certificate 'x' is issued by the certificate 'issuer' */ -static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer) +static int check_issued(ossl_unused X509_STORE_CTX *ctx, X509 *x, X509 *issuer) { - return x509_likely_issued(issuer, x) == X509_V_OK; + int err = ossl_x509_likely_issued(issuer, x); + + if (err == X509_V_OK) + return 1; + /* + * SUBJECT_ISSUER_MISMATCH just means 'x' is clearly not issued by 'issuer'. + * Every other error code likely indicates a real error. + */ + return 0; } -/* Alternative lookup method: look from a STACK stored in other_ctx */ +/*- + * Alternative get_issuer method: look up from a STACK_OF(X509) in other_ctx. + * Returns -1 on internal error. + */ static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) { *issuer = find_issuer(ctx, ctx->other_ctx, x); - - if (*issuer == NULL || !X509_up_ref(*issuer)) - goto err; - - return 1; - - err: - *issuer = NULL; + if (*issuer != NULL) + return X509_up_ref(*issuer) ? 1 : -1; return 0; } -static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm) +/*- + * Alternative lookup method: look from a STACK stored in other_ctx. + * Returns NULL on internal error (such as out of memory). + */ +static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, + const X509_NAME *nm) { - STACK_OF(X509) *sk = NULL; + STACK_OF(X509) *sk = sk_X509_new_null(); X509 *x; int i; + if (sk == NULL) + return NULL; for (i = 0; i < sk_X509_num(ctx->other_ctx); i++) { x = sk_X509_value(ctx->other_ctx, i); if (X509_NAME_cmp(nm, X509_get_subject_name(x)) == 0) { - if (!X509_up_ref(x)) { + if (!X509_add_cert(sk, x, X509_ADD_FLAG_UP_REF)) { sk_X509_pop_free(sk, X509_free); - X509err(X509_F_LOOKUP_CERTS_SK, ERR_R_INTERNAL_ERROR); - ctx->error = X509_V_ERR_UNSPECIFIED; - return NULL; - } - if (sk == NULL) - sk = sk_X509_new_null(); - if (sk == NULL || !sk_X509_push(sk, x)) { - X509_free(x); - sk_X509_pop_free(sk, X509_free); - X509err(X509_F_LOOKUP_CERTS_SK, ERR_R_MALLOC_FAILURE); ctx->error = X509_V_ERR_OUT_OF_MEM; return NULL; } @@ -399,6 +395,7 @@ static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm) /* * Check EE or CA certificate purpose. For trusted certificates explicit local * auxiliary trust can be used to override EKU-restrictions. + * Sadly, returns 0 also on internal error. */ static int check_purpose(X509_STORE_CTX *ctx, X509 *x, int purpose, int depth, int must_be_ca) @@ -448,18 +445,15 @@ static int check_purpose(X509_STORE_CTX *ctx, X509 *x, int purpose, int depth, } /* - * Check a certificate chains extensions for consistency with the supplied - * purpose + * Check extensions of a cert chain for consistency with the supplied purpose. + * Sadly, returns 0 also on internal error. */ - -static int check_chain_extensions(X509_STORE_CTX *ctx) +static int check_extensions(X509_STORE_CTX *ctx) { int i, must_be_ca, plen = 0; X509 *x; - int proxy_path_length = 0; - int purpose; - int allow_proxy_certs; - int num = sk_X509_num(ctx->chain); + int ret, proxy_path_length = 0; + int purpose, allow_proxy_certs, num = sk_X509_num(ctx->chain); /*- * must_be_ca can have 1 of 3 values: @@ -473,87 +467,127 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) must_be_ca = -1; /* CRL path validation */ - if (ctx->parent) { + if (ctx->parent != NULL) { allow_proxy_certs = 0; purpose = X509_PURPOSE_CRL_SIGN; } else { allow_proxy_certs = - ! !(ctx->param->flags & X509_V_FLAG_ALLOW_PROXY_CERTS); + (ctx->param->flags & X509_V_FLAG_ALLOW_PROXY_CERTS) != 0; purpose = ctx->param->purpose; } for (i = 0; i < num; i++) { - int ret; x = sk_X509_value(ctx->chain, i); - if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) - && (x->ex_flags & EXFLAG_CRITICAL)) { - if (!verify_cb_cert(ctx, x, i, - X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)) - return 0; - } - if (!allow_proxy_certs && (x->ex_flags & EXFLAG_PROXY)) { - if (!verify_cb_cert(ctx, x, i, - X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED)) - return 0; - } + CB_FAIL_IF((ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) == 0 + && (x->ex_flags & EXFLAG_CRITICAL) != 0, + ctx, x, i, X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION); + CB_FAIL_IF(!allow_proxy_certs && (x->ex_flags & EXFLAG_PROXY) != 0, + ctx, x, i, X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED); ret = X509_check_ca(x); switch (must_be_ca) { case -1: - if ((ctx->param->flags & X509_V_FLAG_X509_STRICT) - && (ret != 1) && (ret != 0)) { - ret = 0; - ctx->error = X509_V_ERR_INVALID_CA; - } else - ret = 1; + CB_FAIL_IF((ctx->param->flags & X509_V_FLAG_X509_STRICT) != 0 + && ret != 1 && ret != 0, + ctx, x, i, X509_V_ERR_INVALID_CA); break; case 0: - if (ret != 0) { - ret = 0; - ctx->error = X509_V_ERR_INVALID_NON_CA; - } else - ret = 1; + CB_FAIL_IF(ret != 0, ctx, x, i, X509_V_ERR_INVALID_NON_CA); break; default: /* X509_V_FLAG_X509_STRICT is implicit for intermediate CAs */ - if ((ret == 0) - || ((i + 1 < num || ctx->param->flags & X509_V_FLAG_X509_STRICT) - && (ret != 1))) { - ret = 0; - ctx->error = X509_V_ERR_INVALID_CA; - } else - ret = 1; + CB_FAIL_IF(ret == 0 + || ((i + 1 < num + || (ctx->param->flags & X509_V_FLAG_X509_STRICT) != 0) + && ret != 1), ctx, x, i, X509_V_ERR_INVALID_CA); break; } - if (ret > 0 - && (ctx->param->flags & X509_V_FLAG_X509_STRICT) && num > 1) { + if (num > 1) { /* Check for presence of explicit elliptic curve parameters */ ret = check_curve(x); - if (ret < 0) { - ctx->error = X509_V_ERR_UNSPECIFIED; - ret = 0; - } else if (ret == 0) { - ctx->error = X509_V_ERR_EC_KEY_EXPLICIT_PARAMS; - } + CB_FAIL_IF(ret < 0, ctx, x, i, X509_V_ERR_UNSPECIFIED); + CB_FAIL_IF(ret == 0, ctx, x, i, X509_V_ERR_EC_KEY_EXPLICIT_PARAMS); } - if (ret > 0 - && (x->ex_flags & EXFLAG_CA) == 0 - && x->ex_pathlen != -1 - && (ctx->param->flags & X509_V_FLAG_X509_STRICT)) { - ctx->error = X509_V_ERR_INVALID_EXTENSION; - ret = 0; + /* + * Do the following set of checks only if strict checking is requested + * and not for self-issued (including self-signed) EE (non-CA) certs + * because RFC 5280 does not apply to them according RFC 6818 section 2. + */ + if ((ctx->param->flags & X509_V_FLAG_X509_STRICT) != 0 + && num > 1) { /* + * this should imply + * !(i == 0 && (x->ex_flags & EXFLAG_CA) == 0 + * && (x->ex_flags & EXFLAG_SI) != 0) + */ + /* Check Basic Constraints according to RFC 5280 section 4.2.1.9 */ + if (x->ex_pathlen != -1) { + CB_FAIL_IF((x->ex_flags & EXFLAG_CA) == 0, + ctx, x, i, X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA); + CB_FAIL_IF((x->ex_kusage & KU_KEY_CERT_SIGN) == 0, ctx, + x, i, X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN); + } + CB_FAIL_IF((x->ex_flags & EXFLAG_CA) != 0 + && (x->ex_flags & EXFLAG_BCONS) != 0 + && (x->ex_flags & EXFLAG_BCONS_CRITICAL) == 0, + ctx, x, i, X509_V_ERR_CA_BCONS_NOT_CRITICAL); + /* Check Key Usage according to RFC 5280 section 4.2.1.3 */ + if ((x->ex_flags & EXFLAG_CA) != 0) { + CB_FAIL_IF((x->ex_flags & EXFLAG_KUSAGE) == 0, + ctx, x, i, X509_V_ERR_CA_CERT_MISSING_KEY_USAGE); + } else { + CB_FAIL_IF((x->ex_kusage & KU_KEY_CERT_SIGN) != 0, ctx, x, i, + X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA); + } + /* Check issuer is non-empty acc. to RFC 5280 section 4.1.2.4 */ + CB_FAIL_IF(X509_NAME_entry_count(X509_get_issuer_name(x)) == 0, + ctx, x, i, X509_V_ERR_ISSUER_NAME_EMPTY); + /* Check subject is non-empty acc. to RFC 5280 section 4.1.2.6 */ + CB_FAIL_IF(((x->ex_flags & EXFLAG_CA) != 0 + || (x->ex_kusage & KU_CRL_SIGN) != 0 + || x->altname == NULL) + && X509_NAME_entry_count(X509_get_subject_name(x)) == 0, + ctx, x, i, X509_V_ERR_SUBJECT_NAME_EMPTY); + CB_FAIL_IF(X509_NAME_entry_count(X509_get_subject_name(x)) == 0 + && x->altname != NULL + && (x->ex_flags & EXFLAG_SAN_CRITICAL) == 0, + ctx, x, i, X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL); + /* Check SAN is non-empty according to RFC 5280 section 4.2.1.6 */ + CB_FAIL_IF(x->altname != NULL + && sk_GENERAL_NAME_num(x->altname) <= 0, + ctx, x, i, X509_V_ERR_EMPTY_SUBJECT_ALT_NAME); + /* Check sig alg consistency acc. to RFC 5280 section 4.1.1.2 */ + CB_FAIL_IF(X509_ALGOR_cmp(&x->sig_alg, &x->cert_info.signature) != 0, + ctx, x, i, X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY); + CB_FAIL_IF(x->akid != NULL + && (x->ex_flags & EXFLAG_AKID_CRITICAL) != 0, + ctx, x, i, X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL); + CB_FAIL_IF(x->skid != NULL + && (x->ex_flags & EXFLAG_SKID_CRITICAL) != 0, + ctx, x, i, X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL); + if (X509_get_version(x) >= X509_VERSION_3) { + /* Check AKID presence acc. to RFC 5280 section 4.2.1.1 */ + CB_FAIL_IF(i + 1 < num /* + * this means not last cert in chain, + * taken as "generated by conforming CAs" + */ + && (x->akid == NULL || x->akid->keyid == NULL), ctx, + x, i, X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER); + /* Check SKID presence acc. to RFC 5280 section 4.2.1.2 */ + CB_FAIL_IF((x->ex_flags & EXFLAG_CA) != 0 && x->skid == NULL, + ctx, x, i, X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER); + } else { + CB_FAIL_IF(sk_X509_EXTENSION_num(X509_get0_extensions(x)) > 0, + ctx, x, i, X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3); + } } - if (ret == 0 && !verify_cb_cert(ctx, x, i, X509_V_OK)) - return 0; + /* check_purpose() makes the callback as needed */ if (purpose > 0 && !check_purpose(ctx, x, purpose, i, must_be_ca)) return 0; - /* Check pathlen */ - if ((i > 1) && (x->ex_pathlen != -1) - && (plen > (x->ex_pathlen + proxy_path_length))) { - if (!verify_cb_cert(ctx, x, i, X509_V_ERR_PATH_LENGTH_EXCEEDED)) - return 0; - } - /* Increment path length if not a self issued intermediate CA */ + /* Check path length */ + CB_FAIL_IF(i > 1 && x->ex_pathlen != -1 + && plen > x->ex_pathlen + proxy_path_length, + ctx, x, i, X509_V_ERR_PATH_LENGTH_EXCEEDED); + /* Increment path length if not a self-issued intermediate CA */ if (i > 0 && (x->ex_flags & EXFLAG_SI) == 0) plen++; /* @@ -574,17 +608,15 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) * increment proxy_path_length. */ if (x->ex_pcpathlen != -1) { - if (proxy_path_length > x->ex_pcpathlen) { - if (!verify_cb_cert(ctx, x, i, - X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED)) - return 0; - } + CB_FAIL_IF(proxy_path_length > x->ex_pcpathlen, + ctx, x, i, X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED); proxy_path_length = x->ex_pcpathlen; } proxy_path_length++; must_be_ca = 0; - } else + } else { must_be_ca = 1; + } } return 1; } @@ -610,6 +642,7 @@ static int has_san_id(X509 *x, int gtype) return ret; } +/* Returns -1 on internal error */ static int check_name_constraints(X509_STORE_CTX *ctx) { int i; @@ -619,8 +652,8 @@ static int check_name_constraints(X509_STORE_CTX *ctx) X509 *x = sk_X509_value(ctx->chain, i); int j; - /* Ignore self issued certs unless last in chain */ - if (i && (x->ex_flags & EXFLAG_SI)) + /* Ignore self-issued certs unless last in chain */ + if (i != 0 && (x->ex_flags & EXFLAG_SI) != 0) continue; /* @@ -629,16 +662,16 @@ static int check_name_constraints(X509_STORE_CTX *ctx) * added. * (RFC 3820: 3.4, 4.1.3 (a)(4)) */ - if (x->ex_flags & EXFLAG_PROXY) { + if ((x->ex_flags & EXFLAG_PROXY) != 0) { X509_NAME *tmpsubject = X509_get_subject_name(x); X509_NAME *tmpissuer = X509_get_issuer_name(x); X509_NAME_ENTRY *tmpentry = NULL; - int last_object_nid = 0; + int last_nid = 0; int err = X509_V_OK; - int last_object_loc = X509_NAME_entry_count(tmpsubject) - 1; + int last_loc = X509_NAME_entry_count(tmpsubject) - 1; /* Check that there are at least two RDNs */ - if (last_object_loc < 1) { + if (last_loc < 1) { err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION; goto proxy_name_done; } @@ -655,12 +688,11 @@ static int check_name_constraints(X509_STORE_CTX *ctx) /* * Check that the last subject component isn't part of a - * multivalued RDN + * multi-valued RDN */ - if (X509_NAME_ENTRY_set(X509_NAME_get_entry(tmpsubject, - last_object_loc)) + if (X509_NAME_ENTRY_set(X509_NAME_get_entry(tmpsubject, last_loc)) == X509_NAME_ENTRY_set(X509_NAME_get_entry(tmpsubject, - last_object_loc - 1))) { + last_loc - 1))) { err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION; goto proxy_name_done; } @@ -671,17 +703,15 @@ static int check_name_constraints(X509_STORE_CTX *ctx) */ tmpsubject = X509_NAME_dup(tmpsubject); if (tmpsubject == NULL) { - X509err(X509_F_CHECK_NAME_CONSTRAINTS, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); ctx->error = X509_V_ERR_OUT_OF_MEM; - return 0; + return -1; } - tmpentry = - X509_NAME_delete_entry(tmpsubject, last_object_loc); - last_object_nid = - OBJ_obj2nid(X509_NAME_ENTRY_get_object(tmpentry)); + tmpentry = X509_NAME_delete_entry(tmpsubject, last_loc); + last_nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(tmpentry)); - if (last_object_nid != NID_commonName + if (last_nid != NID_commonName || X509_NAME_cmp(tmpsubject, tmpissuer) != 0) { err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION; } @@ -689,10 +719,8 @@ static int check_name_constraints(X509_STORE_CTX *ctx) X509_NAME_ENTRY_free(tmpentry); X509_NAME_free(tmpsubject); - proxy_name_done: - if (err != X509_V_OK - && !verify_cb_cert(ctx, x, i, err)) - return 0; + proxy_name_done: + CB_FAIL_IF(err != X509_V_OK, ctx, x, i, err); } /* @@ -706,6 +734,7 @@ static int check_name_constraints(X509_STORE_CTX *ctx) if (nc) { int rv = NAME_CONSTRAINTS_check(x, nc); + int ret = 1; /* If EE certificate check commonName too */ if (rv == X509_V_OK && i == 0 @@ -713,17 +742,18 @@ static int check_name_constraints(X509_STORE_CTX *ctx) & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT) == 0 && ((ctx->param->hostflags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) != 0 - || !has_san_id(x, GEN_DNS))) + || (ret = has_san_id(x, GEN_DNS)) == 0)) rv = NAME_CONSTRAINTS_check_CN(x, nc); + if (ret < 0) + return ret; switch (rv) { case X509_V_OK: break; case X509_V_ERR_OUT_OF_MEM: - return 0; + return -1; default: - if (!verify_cb_cert(ctx, x, i, rv)) - return 0; + CB_FAIL_IF(1, ctx, x, i, rv); break; } } @@ -759,24 +789,27 @@ static int check_id(X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *vpm = ctx->param; X509 *x = ctx->cert; - if (vpm->hosts && check_hosts(x, vpm) <= 0) { + + if (vpm->hosts != NULL && check_hosts(x, vpm) <= 0) { if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH)) return 0; } - if (vpm->email && X509_check_email(x, vpm->email, vpm->emaillen, 0) <= 0) { + if (vpm->email != NULL + && X509_check_email(x, vpm->email, vpm->emaillen, 0) <= 0) { if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH)) return 0; } - if (vpm->ip && X509_check_ip(x, vpm->ip, vpm->iplen, 0) <= 0) { + if (vpm->ip != NULL && X509_check_ip(x, vpm->ip, vpm->iplen, 0) <= 0) { if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH)) return 0; } return 1; } +/* Returns -1 on internal error */ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) { - int i; + int i, res; X509 *x = NULL; X509 *mx; SSL_DANE *dane = ctx->dane; @@ -788,11 +821,9 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) * match, we're done, otherwise we'll merely record the match depth. */ if (DANETLS_HAS_TA(dane) && num_untrusted > 0 && num_untrusted < num) { - switch (trust = check_dane_issuer(ctx, num_untrusted)) { - case X509_TRUST_TRUSTED: - case X509_TRUST_REJECTED: + trust = check_dane_issuer(ctx, num_untrusted); + if (trust != X509_TRUST_UNTRUSTED) return trust; - } } /* @@ -804,7 +835,7 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) for (i = num_untrusted; i < num; i++) { x = sk_X509_value(ctx->chain, i); trust = X509_check_trust(x, ctx->param->trust, 0); - /* If explicitly trusted return trusted */ + /* If explicitly trusted (so not neutral nor rejected) return trusted */ if (trust == X509_TRUST_TRUSTED) goto trusted; if (trust == X509_TRUST_REJECTED) @@ -816,20 +847,23 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) * the chain is PKIX trusted. */ if (num_untrusted < num) { - if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) + if ((ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) != 0) goto trusted; return X509_TRUST_UNTRUSTED; } - if (num_untrusted == num && ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) { + if (num_untrusted == num + && (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) != 0) { /* * Last-resort call with no new trusted certificates, check the leaf * for a direct trust store match. */ i = 0; x = sk_X509_value(ctx->chain, i); - mx = lookup_cert_match(ctx, x); - if (!mx) + res = lookup_cert_match(&mx, ctx, x); + if (res < 0) + return res; + if (mx == NULL) return X509_TRUST_UNTRUSTED; /* @@ -843,7 +877,7 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) } /* Replace leaf with trusted match */ - (void) sk_X509_set(ctx->chain, 0, mx); + (void)sk_X509_set(ctx->chain, 0, mx); X509_free(x); ctx->num_untrusted = 0; goto trusted; @@ -856,9 +890,8 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) return X509_TRUST_UNTRUSTED; rejected: - if (!verify_cb_cert(ctx, x, i, X509_V_ERR_CERT_REJECTED)) - return X509_TRUST_REJECTED; - return X509_TRUST_UNTRUSTED; + return verify_cb_cert(ctx, x, i, X509_V_ERR_CERT_REJECTED) == 0 + ? X509_TRUST_REJECTED : X509_TRUST_UNTRUSTED; trusted: if (!DANETLS_ENABLED(dane)) @@ -871,14 +904,16 @@ static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) return X509_TRUST_UNTRUSTED; } +/* Sadly, returns 0 also on internal error. */ static int check_revocation(X509_STORE_CTX *ctx) { int i = 0, last = 0, ok = 0; - if (!(ctx->param->flags & X509_V_FLAG_CRL_CHECK)) + + if ((ctx->param->flags & X509_V_FLAG_CRL_CHECK) == 0) return 1; - if (ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) + if ((ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) != 0) { last = sk_X509_num(ctx->chain) - 1; - else { + } else { /* If checking CRL paths this isn't the EE certificate */ if (ctx->parent) return 1; @@ -893,6 +928,7 @@ static int check_revocation(X509_STORE_CTX *ctx) return 1; } +/* Sadly, returns 0 also on internal error. */ static int check_cert(X509_STORE_CTX *ctx) { X509_CRL *crl = NULL, *dcrl = NULL; @@ -905,20 +941,18 @@ static int check_cert(X509_STORE_CTX *ctx) ctx->current_crl_score = 0; ctx->current_reasons = 0; - if (x->ex_flags & EXFLAG_PROXY) + if ((x->ex_flags & EXFLAG_PROXY) != 0) return 1; while (ctx->current_reasons != CRLDP_ALL_REASONS) { unsigned int last_reasons = ctx->current_reasons; /* Try to retrieve relevant CRL */ - if (ctx->get_crl) + if (ctx->get_crl != NULL) ok = ctx->get_crl(ctx, &crl, x); else ok = get_crl_delta(ctx, &crl, &dcrl, x); - /* - * If error looking up CRL, nothing we can do except notify callback - */ + /* If error looking up CRL, nothing we can do except notify callback */ if (!ok) { ok = verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_GET_CRL); goto done; @@ -928,15 +962,16 @@ static int check_cert(X509_STORE_CTX *ctx) if (!ok) goto done; - if (dcrl) { + if (dcrl != NULL) { ok = ctx->check_crl(ctx, dcrl); if (!ok) goto done; ok = ctx->cert_crl(ctx, dcrl, x); if (!ok) goto done; - } else + } else { ok = 1; + } /* Don't look in full CRL if delta reason is removefromCRL */ if (ok != 2) { @@ -967,15 +1002,14 @@ static int check_cert(X509_STORE_CTX *ctx) } /* Check CRL times against values in X509_STORE_CTX */ - static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) { time_t *ptime; int i; - if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) + if ((ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) != 0) ptime = &ctx->param->check_time; - else if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) + else if ((ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) != 0) return 1; else ptime = NULL; @@ -1006,11 +1040,9 @@ static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) if (!verify_cb_crl(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD)) return 0; } - /* Ignore expiry of base CRL is delta is valid */ - if ((i < 0) && !(ctx->current_crl_score & CRL_SCORE_TIME_DELTA)) { - if (!notify) - return 0; - if (!verify_cb_crl(ctx, X509_V_ERR_CRL_HAS_EXPIRED)) + /* Ignore expiration of base CRL is delta is valid */ + if (i < 0 && (ctx->current_crl_score & CRL_SCORE_TIME_DELTA) == 0) { + if (!notify || !verify_cb_crl(ctx, X509_V_ERR_CRL_HAS_EXPIRED)) return 0; } } @@ -1040,6 +1072,7 @@ static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509_CRL **pdcrl, /* If current CRL is equivalent use it if it is newer */ if (crl_score == best_score && best_crl != NULL) { int day, sec; + if (ASN1_TIME_diff(&day, &sec, X509_CRL_get0_lastUpdate(best_crl), X509_CRL_get0_lastUpdate(crl)) == 0) continue; @@ -1056,7 +1089,7 @@ static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509_CRL **pdcrl, best_reasons = reasons; } - if (best_crl) { + if (best_crl != NULL) { X509_CRL_free(*pcrl); *pcrl = best_crl; *pissuer = best_crl_issuer; @@ -1078,54 +1111,46 @@ static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509_CRL **pdcrl, * Compare two CRL extensions for delta checking purposes. They should be * both present or both absent. If both present all fields must be identical. */ - static int crl_extension_match(X509_CRL *a, X509_CRL *b, int nid) { - ASN1_OCTET_STRING *exta, *extb; - int i; - i = X509_CRL_get_ext_by_NID(a, nid, -1); + ASN1_OCTET_STRING *exta = NULL, *extb = NULL; + int i = X509_CRL_get_ext_by_NID(a, nid, -1); + if (i >= 0) { /* Can't have multiple occurrences */ if (X509_CRL_get_ext_by_NID(a, nid, i) != -1) return 0; exta = X509_EXTENSION_get_data(X509_CRL_get_ext(a, i)); - } else - exta = NULL; + } i = X509_CRL_get_ext_by_NID(b, nid, -1); - if (i >= 0) { - if (X509_CRL_get_ext_by_NID(b, nid, i) != -1) return 0; extb = X509_EXTENSION_get_data(X509_CRL_get_ext(b, i)); - } else - extb = NULL; + } - if (!exta && !extb) + if (exta == NULL && extb == NULL) return 1; - if (!exta || !extb) + if (exta == NULL || extb == NULL) return 0; - if (ASN1_OCTET_STRING_cmp(exta, extb)) - return 0; - - return 1; + return ASN1_OCTET_STRING_cmp(exta, extb) == 0; } /* See if a base and delta are compatible */ - static int check_delta_base(X509_CRL *delta, X509_CRL *base) { /* Delta CRL must be a delta */ - if (!delta->base_crl_number) + if (delta->base_crl_number == NULL) return 0; /* Base must have a CRL number */ - if (!base->crl_number) + if (base->crl_number == NULL) return 0; /* Issuer names must match */ - if (X509_NAME_cmp(X509_CRL_get_issuer(base), X509_CRL_get_issuer(delta))) + if (X509_NAME_cmp(X509_CRL_get_issuer(base), + X509_CRL_get_issuer(delta)) != 0) return 0; /* AKID and IDP must match */ if (!crl_extension_match(delta, base, NID_authority_key_identifier)) @@ -1136,24 +1161,22 @@ static int check_delta_base(X509_CRL *delta, X509_CRL *base) if (ASN1_INTEGER_cmp(delta->base_crl_number, base->crl_number) > 0) return 0; /* Delta CRL number must exceed full CRL number */ - if (ASN1_INTEGER_cmp(delta->crl_number, base->crl_number) > 0) - return 1; - return 0; + return ASN1_INTEGER_cmp(delta->crl_number, base->crl_number) > 0; } /* * For a given base CRL find a delta... maybe extend to delta scoring or * retrieve a chain of deltas... */ - static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, int *pscore, X509_CRL *base, STACK_OF(X509_CRL) *crls) { X509_CRL *delta; int i; - if (!(ctx->param->flags & X509_V_FLAG_USE_DELTAS)) + + if ((ctx->param->flags & X509_V_FLAG_USE_DELTAS) == 0) return; - if (!((ctx->current_cert->ex_flags | base->flags) & EXFLAG_FRESHEST)) + if (((ctx->current_cert->ex_flags | base->flags) & EXFLAG_FRESHEST) == 0) return; for (i = 0; i < sk_X509_CRL_num(crls); i++) { delta = sk_X509_CRL_value(crls, i); @@ -1175,42 +1198,41 @@ static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, int *pscore, * also used to determine if the CRL is suitable: if no new reasons the CRL * is rejected, otherwise reasons is updated. */ - static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, unsigned int *preasons, X509_CRL *crl, X509 *x) { - int crl_score = 0; unsigned int tmp_reasons = *preasons, crl_reasons; /* First see if we can reject CRL straight away */ /* Invalid IDP cannot be processed */ - if (crl->idp_flags & IDP_INVALID) + if ((crl->idp_flags & IDP_INVALID) != 0) return 0; /* Reason codes or indirect CRLs need extended CRL support */ - if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT)) { + if ((ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT) == 0) { if (crl->idp_flags & (IDP_INDIRECT | IDP_REASONS)) return 0; - } else if (crl->idp_flags & IDP_REASONS) { + } else if ((crl->idp_flags & IDP_REASONS) != 0) { /* If no new reasons reject */ - if (!(crl->idp_reasons & ~tmp_reasons)) + if ((crl->idp_reasons & ~tmp_reasons) == 0) return 0; } /* Don't process deltas at this stage */ - else if (crl->base_crl_number) + else if (crl->base_crl_number != NULL) return 0; /* If issuer name doesn't match certificate need indirect CRL */ - if (X509_NAME_cmp(X509_get_issuer_name(x), X509_CRL_get_issuer(crl))) { - if (!(crl->idp_flags & IDP_INDIRECT)) + if (X509_NAME_cmp(X509_get_issuer_name(x), X509_CRL_get_issuer(crl)) != 0) { + if ((crl->idp_flags & IDP_INDIRECT) == 0) return 0; - } else + } else { crl_score |= CRL_SCORE_ISSUER_NAME; + } - if (!(crl->flags & EXFLAG_CRITICAL)) + if ((crl->flags & EXFLAG_CRITICAL) == 0) crl_score |= CRL_SCORE_NOCRITICAL; - /* Check expiry */ + /* Check expiration */ if (check_crl_time(ctx, crl, 0)) crl_score |= CRL_SCORE_TIME; @@ -1218,15 +1240,13 @@ static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, crl_akid_check(ctx, crl, pissuer, &crl_score); /* If we can't locate certificate issuer at this point forget it */ - - if (!(crl_score & CRL_SCORE_AKID)) + if ((crl_score & CRL_SCORE_AKID) == 0) return 0; /* Check cert for matching CRL distribution points */ - if (crl_crldp_check(x, crl, crl_score, &crl_reasons)) { /* If no new reasons reject */ - if (!(crl_reasons & ~tmp_reasons)) + if ((crl_reasons & ~tmp_reasons) == 0) return 0; tmp_reasons |= crl_reasons; crl_score |= CRL_SCORE_SCOPE; @@ -1242,7 +1262,7 @@ static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer, int *pcrl_score) { X509 *crl_issuer = NULL; - X509_NAME *cnm = X509_CRL_get_issuer(crl); + const X509_NAME *cnm = X509_CRL_get_issuer(crl); int cidx = ctx->error_depth; int i; @@ -1271,8 +1291,7 @@ static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, } /* Anything else needs extended CRL support */ - - if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT)) + if ((ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT) == 0) return; /* @@ -1281,7 +1300,7 @@ static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, */ for (i = 0; i < sk_X509_num(ctx->untrusted); i++) { crl_issuer = sk_X509_value(ctx->untrusted, i); - if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm)) + if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm) != 0) continue; if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK) { *pissuer = crl_issuer; @@ -1297,16 +1316,15 @@ static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, * parent. This could be optimised somewhat since a lot of path checking will * be duplicated by the parent, but this will rarely be used in practice. */ - static int check_crl_path(X509_STORE_CTX *ctx, X509 *x) { - X509_STORE_CTX crl_ctx; + X509_STORE_CTX crl_ctx = {0}; int ret; /* Don't allow recursive CRL path validation */ - if (ctx->parent) + if (ctx->parent != NULL) return 0; - if (!X509_STORE_CTX_init(&crl_ctx, ctx->ctx, x, ctx->untrusted)) + if (!X509_STORE_CTX_init(&crl_ctx, ctx->store, x, ctx->untrusted)) return -1; crl_ctx.crls = ctx->crls; @@ -1331,22 +1349,19 @@ static int check_crl_path(X509_STORE_CTX *ctx, X509 *x) /* * RFC3280 says nothing about the relationship between CRL path and * certificate path, which could lead to situations where a certificate could - * be revoked or validated by a CA not authorised to do so. RFC5280 is more + * be revoked or validated by a CA not authorized to do so. RFC5280 is more * strict and states that the two paths must end in the same trust anchor, * though some discussions remain... until this is resolved we use the * RFC5280 version */ - static int check_crl_chain(X509_STORE_CTX *ctx, STACK_OF(X509) *cert_path, STACK_OF(X509) *crl_path) { - X509 *cert_ta, *crl_ta; - cert_ta = sk_X509_value(cert_path, sk_X509_num(cert_path) - 1); - crl_ta = sk_X509_value(crl_path, sk_X509_num(crl_path) - 1); - if (!X509_cmp(cert_ta, crl_ta)) - return 1; - return 0; + X509 *cert_ta = sk_X509_value(cert_path, sk_X509_num(cert_path) - 1); + X509 *crl_ta = sk_X509_value(crl_path, sk_X509_num(crl_path) - 1); + + return X509_cmp(cert_ta, crl_ta) == 0; } /*- @@ -1356,32 +1371,29 @@ static int check_crl_chain(X509_STORE_CTX *ctx, * 3. Both are full names and compare two GENERAL_NAMES. * 4. One is NULL: automatic match. */ - static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b) { X509_NAME *nm = NULL; GENERAL_NAMES *gens = NULL; GENERAL_NAME *gena, *genb; int i, j; - if (!a || !b) + + if (a == NULL || b == NULL) return 1; if (a->type == 1) { - if (!a->dpname) + if (a->dpname == NULL) return 0; /* Case 1: two X509_NAME */ if (b->type == 1) { - if (!b->dpname) - return 0; - if (!X509_NAME_cmp(a->dpname, b->dpname)) - return 1; - else + if (b->dpname == NULL) return 0; + return X509_NAME_cmp(a->dpname, b->dpname) == 0; } /* Case 2: set name and GENERAL_NAMES appropriately */ nm = a->dpname; gens = b->name.fullname; } else if (b->type == 1) { - if (!b->dpname) + if (b->dpname == NULL) return 0; /* Case 2: set name and GENERAL_NAMES appropriately */ gens = a->name.fullname; @@ -1389,12 +1401,12 @@ static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b) } /* Handle case 2 with one GENERAL_NAMES and one X509_NAME */ - if (nm) { + if (nm != NULL) { for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { gena = sk_GENERAL_NAME_value(gens, i); if (gena->type != GEN_DIRNAME) continue; - if (!X509_NAME_cmp(nm, gena->d.directoryName)) + if (X509_NAME_cmp(nm, gena->d.directoryName) == 0) return 1; } return 0; @@ -1406,7 +1418,7 @@ static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b) gena = sk_GENERAL_NAME_value(a->name.fullname, i); for (j = 0; j < sk_GENERAL_NAME_num(b->name.fullname); j++) { genb = sk_GENERAL_NAME_value(b->name.fullname, j); - if (!GENERAL_NAME_cmp(gena, genb)) + if (GENERAL_NAME_cmp(gena, genb) == 0) return 1; } } @@ -1418,56 +1430,57 @@ static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b) static int crldp_check_crlissuer(DIST_POINT *dp, X509_CRL *crl, int crl_score) { int i; - X509_NAME *nm = X509_CRL_get_issuer(crl); + const X509_NAME *nm = X509_CRL_get_issuer(crl); + /* If no CRLissuer return is successful iff don't need a match */ - if (!dp->CRLissuer) - return ! !(crl_score & CRL_SCORE_ISSUER_NAME); + if (dp->CRLissuer == NULL) + return (crl_score & CRL_SCORE_ISSUER_NAME) != 0; 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) continue; - if (!X509_NAME_cmp(gen->d.directoryName, nm)) + if (X509_NAME_cmp(gen->d.directoryName, nm) == 0) return 1; } return 0; } /* Check CRLDP and IDP */ - static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score, unsigned int *preasons) { int i; - if (crl->idp_flags & IDP_ONLYATTR) + + if ((crl->idp_flags & IDP_ONLYATTR) != 0) return 0; - if (x->ex_flags & EXFLAG_CA) { - if (crl->idp_flags & IDP_ONLYUSER) + if ((x->ex_flags & EXFLAG_CA) != 0) { + if ((crl->idp_flags & IDP_ONLYUSER) != 0) return 0; } else { - if (crl->idp_flags & IDP_ONLYCA) + if ((crl->idp_flags & IDP_ONLYCA) != 0) return 0; } *preasons = crl->idp_reasons; for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++) { DIST_POINT *dp = sk_DIST_POINT_value(x->crldp, i); + if (crldp_check_crlissuer(dp, crl, crl_score)) { - if (!crl->idp || idp_check_dp(dp->distpoint, crl->idp->distpoint)) { + if (crl->idp == NULL + || idp_check_dp(dp->distpoint, crl->idp->distpoint)) { *preasons &= dp->dp_reasons; return 1; } } } - if ((!crl->idp || !crl->idp->distpoint) - && (crl_score & CRL_SCORE_ISSUER_NAME)) - return 1; - return 0; + return (crl->idp == NULL || crl->idp->distpoint == NULL) + && (crl_score & CRL_SCORE_ISSUER_NAME) != 0; } /* * Retrieve CRL corresponding to current certificate. If deltas enabled try * to find a delta CRL too */ - static int get_crl_delta(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509_CRL **pdcrl, X509 *x) { @@ -1477,7 +1490,7 @@ static int get_crl_delta(X509_STORE_CTX *ctx, unsigned int reasons; X509_CRL *crl = NULL, *dcrl = NULL; STACK_OF(X509_CRL) *skcrl; - X509_NAME *nm = X509_get_issuer_name(x); + const X509_NAME *nm = X509_get_issuer_name(x); reasons = ctx->current_reasons; ok = get_crl_sk(ctx, &crl, &dcrl, @@ -1486,11 +1499,10 @@ static int get_crl_delta(X509_STORE_CTX *ctx, goto done; /* Lookup CRLs from store */ - skcrl = ctx->lookup_crls(ctx, nm); /* If no CRLs found and a near match from get_crl_sk use that */ - if (!skcrl && crl) + if (skcrl == NULL && crl != NULL) goto done; get_crl_sk(ctx, &crl, &dcrl, &issuer, &crl_score, &reasons, skcrl); @@ -1499,7 +1511,7 @@ static int get_crl_delta(X509_STORE_CTX *ctx, done: /* If we got any kind of CRL use it and return success */ - if (crl) { + if (crl != NULL) { ctx->current_issuer = issuer; ctx->current_crl_score = crl_score; ctx->current_reasons = reasons; @@ -1518,18 +1530,18 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) int cnum = ctx->error_depth; int chnum = sk_X509_num(ctx->chain) - 1; - /* if we have an alternative CRL issuer cert use that */ - if (ctx->current_issuer) + /* If we have an alternative CRL issuer cert use that */ + if (ctx->current_issuer != NULL) { issuer = ctx->current_issuer; /* * Else find CRL issuer: if not last certificate then issuer is next * certificate in chain. */ - else if (cnum < chnum) + } else if (cnum < chnum) { issuer = sk_X509_value(ctx->chain, cnum + 1); - else { + } else { issuer = sk_X509_value(ctx->chain, chnum); - /* If not self signed, can't check signature */ + /* If not self-issued, can't check signature */ if (!ctx->check_issued(ctx, issuer, issuer) && !verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER)) return 0; @@ -1541,39 +1553,38 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) /* * Skip most tests for deltas because they have already been done */ - if (!crl->base_crl_number) { + if (crl->base_crl_number == NULL) { /* Check for cRLSign bit if keyUsage present */ - if ((issuer->ex_flags & EXFLAG_KUSAGE) && - !(issuer->ex_kusage & KU_CRL_SIGN) && + if ((issuer->ex_flags & EXFLAG_KUSAGE) != 0 && + (issuer->ex_kusage & KU_CRL_SIGN) == 0 && !verify_cb_crl(ctx, X509_V_ERR_KEYUSAGE_NO_CRL_SIGN)) return 0; - if (!(ctx->current_crl_score & CRL_SCORE_SCOPE) && + if ((ctx->current_crl_score & CRL_SCORE_SCOPE) == 0 && !verify_cb_crl(ctx, X509_V_ERR_DIFFERENT_CRL_SCOPE)) return 0; - if (!(ctx->current_crl_score & CRL_SCORE_SAME_PATH) && + if ((ctx->current_crl_score & CRL_SCORE_SAME_PATH) == 0 && check_crl_path(ctx, ctx->current_issuer) <= 0 && !verify_cb_crl(ctx, X509_V_ERR_CRL_PATH_VALIDATION_ERROR)) return 0; - if ((crl->idp_flags & IDP_INVALID) && + if ((crl->idp_flags & IDP_INVALID) != 0 && !verify_cb_crl(ctx, X509_V_ERR_INVALID_EXTENSION)) return 0; } - if (!(ctx->current_crl_score & CRL_SCORE_TIME) && + if ((ctx->current_crl_score & CRL_SCORE_TIME) == 0 && !check_crl_time(ctx, crl, 1)) return 0; /* Attempt to get issuer certificate public key */ ikey = X509_get0_pubkey(issuer); - - if (!ikey && + if (ikey == NULL && !verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY)) return 0; - if (ikey) { + if (ikey != NULL) { int rv = X509_CRL_check_suiteb(crl, ikey, ctx->param->flags); if (rv != X509_V_OK && !verify_cb_crl(ctx, rv)) @@ -1597,8 +1608,8 @@ static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x) * was revoked. This has since been changed since critical extensions can * change the meaning of CRL entries. */ - if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) - && (crl->flags & EXFLAG_CRITICAL) && + if ((ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) == 0 + && (crl->flags & EXFLAG_CRITICAL) != 0 && !verify_cb_crl(ctx, X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION)) return 0; /* @@ -1632,35 +1643,34 @@ static int check_policy(X509_STORE_CTX *ctx) * was verified via a bare public key, and pop it off right after the * X509_policy_check() call. */ - if (ctx->bare_ta_signed && !sk_X509_push(ctx->chain, NULL)) { - X509err(X509_F_CHECK_POLICY, ERR_R_MALLOC_FAILURE); - ctx->error = X509_V_ERR_OUT_OF_MEM; - return 0; - } + if (ctx->bare_ta_signed && !sk_X509_push(ctx->chain, NULL)) + goto memerr; ret = X509_policy_check(&ctx->tree, &ctx->explicit_policy, ctx->chain, ctx->param->policies, ctx->param->flags); if (ctx->bare_ta_signed) - sk_X509_pop(ctx->chain); + (void)sk_X509_pop(ctx->chain); - if (ret == X509_PCY_TREE_INTERNAL) { - X509err(X509_F_CHECK_POLICY, ERR_R_MALLOC_FAILURE); - ctx->error = X509_V_ERR_OUT_OF_MEM; - return 0; - } + if (ret == X509_PCY_TREE_INTERNAL) + goto memerr; /* Invalid or inconsistent extensions */ if (ret == X509_PCY_TREE_INVALID) { - int i; + int i, cbcalled = 0; /* Locate certificates with bad extensions and notify callback. */ - for (i = 1; i < sk_X509_num(ctx->chain); i++) { + for (i = 0; i < sk_X509_num(ctx->chain); i++) { X509 *x = sk_X509_value(ctx->chain, i); - if (!(x->ex_flags & EXFLAG_INVALID_POLICY)) - continue; - if (!verify_cb_cert(ctx, x, i, - X509_V_ERR_INVALID_POLICY_EXTENSION)) - return 0; + if ((x->ex_flags & EXFLAG_INVALID_POLICY) != 0) + cbcalled = 1; + CB_FAIL_IF((x->ex_flags & EXFLAG_INVALID_POLICY) != 0, + ctx, x, i, X509_V_ERR_INVALID_POLICY_EXTENSION); + } + if (!cbcalled) { + /* Should not be able to get here */ + ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR); + return 0; } + /* The callback ignored the error so we return success */ return 1; } if (ret == X509_PCY_TREE_FAILURE) { @@ -1669,11 +1679,11 @@ static int check_policy(X509_STORE_CTX *ctx) return ctx->verify_cb(0, ctx); } if (ret != X509_PCY_TREE_VALID) { - X509err(X509_F_CHECK_POLICY, ERR_R_INTERNAL_ERROR); + ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR); return 0; } - if (ctx->param->flags & X509_V_FLAG_NOTIFY_POLICY) { + if ((ctx->param->flags & X509_V_FLAG_NOTIFY_POLICY) != 0) { ctx->current_cert = NULL; /* * Verification errors need to be "sticky", a callback may have allowed @@ -1686,6 +1696,11 @@ static int check_policy(X509_STORE_CTX *ctx) } return 1; + + memerr: + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + return -1; } /*- @@ -1695,14 +1710,14 @@ static int check_policy(X509_STORE_CTX *ctx) * * Return 1 on success, 0 otherwise. */ -int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) +int ossl_x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) { time_t *ptime; int i; - if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) + if ((ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) != 0) ptime = &ctx->param->check_time; - else if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) + else if ((ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) != 0) return 1; else ptime = NULL; @@ -1710,79 +1725,73 @@ int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) i = X509_cmp_time(X509_get0_notBefore(x), ptime); if (i >= 0 && depth < 0) return 0; - if (i == 0 && !verify_cb_cert(ctx, x, depth, - X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD)) - return 0; - if (i > 0 && !verify_cb_cert(ctx, x, depth, X509_V_ERR_CERT_NOT_YET_VALID)) - return 0; + CB_FAIL_IF(i == 0, ctx, x, depth, X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD); + CB_FAIL_IF(i > 0, ctx, x, depth, X509_V_ERR_CERT_NOT_YET_VALID); i = X509_cmp_time(X509_get0_notAfter(x), ptime); if (i <= 0 && depth < 0) return 0; - if (i == 0 && !verify_cb_cert(ctx, x, depth, - X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD)) - return 0; - if (i < 0 && !verify_cb_cert(ctx, x, depth, X509_V_ERR_CERT_HAS_EXPIRED)) - return 0; + CB_FAIL_IF(i == 0, ctx, x, depth, X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); + CB_FAIL_IF(i < 0, ctx, x, depth, X509_V_ERR_CERT_HAS_EXPIRED); return 1; } -/* verify the issuer signatures and cert times of ctx->chain */ +/* + * Verify the issuer signatures and cert times of ctx->chain. + * Sadly, returns 0 also on internal error. + */ static int internal_verify(X509_STORE_CTX *ctx) { int n = sk_X509_num(ctx->chain) - 1; X509 *xi = sk_X509_value(ctx->chain, n); - X509 *xs; + X509 *xs = xi; - /* - * With DANE-verified bare public key TA signatures, it remains only to - * check the timestamps of the top certificate. We report the issuer as - * NULL, since all we have is a bare key. - */ + ctx->error_depth = n; if (ctx->bare_ta_signed) { - xs = xi; + /* + * With DANE-verified bare public key TA signatures, + * on the top certificate we check only the timestamps. + * We report the issuer as NULL because all we have is a bare key. + */ xi = NULL; - goto check_cert_time; - } - - if (ctx->check_issued(ctx, xi, xi)) - xs = xi; /* the typical case: last cert in the chain is self-issued */ - else { - if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) { - xs = xi; - goto check_cert_time; - } - if (n <= 0) { - if (!verify_cb_cert(ctx, xi, 0, - X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) - return 0; - - xs = xi; - goto check_cert_time; + } else if (ossl_x509_likely_issued(xi, xi) != X509_V_OK + /* exceptional case: last cert in the chain is not self-issued */ + && ((ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) == 0)) { + if (n > 0) { + n--; + ctx->error_depth = n; + xs = sk_X509_value(ctx->chain, n); + } else { + CB_FAIL_IF(1, ctx, xi, 0, + X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); } - - n--; - ctx->error_depth = n; - xs = sk_X509_value(ctx->chain, n); + /* + * The below code will certainly not do a + * self-signature check on xi because it is not self-issued. + */ } /* - * Do not clear ctx->error=0, it must be "sticky", only the user's callback - * is allowed to reset errors (at its own peril). + * Do not clear error (by ctx->error = X509_V_OK), it must be "sticky", + * only the user's callback is allowed to reset errors (at its own peril). */ while (n >= 0) { - /* + /*- * For each iteration of this loop: * n is the subject depth * xs is the subject cert, for which the signature is to be checked - * xi is the supposed issuer cert containing the public key to use + * xi is NULL for DANE-verified bare public key TA signatures + * else the supposed issuer cert containing the public key to use * Initially xs == xi if the last cert in the chain is self-issued. - * - * Skip signature check for self-signed certificates unless explicitly + */ + /* + * Do signature check for self-signed certificates only if explicitly * asked for because it does not add any security and just wastes time. */ - if (xs != xi || ((ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE) - && (xi->ex_flags & EXFLAG_SS) != 0)) { + if (xi != NULL + && (xs != xi + || ((ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE) != 0 + && (xi->ex_flags & EXFLAG_SS) != 0))) { EVP_PKEY *pkey; /* * If the issuer's public key is not available or its key usage @@ -1795,8 +1804,8 @@ static int internal_verify(X509_STORE_CTX *ctx) * step (n) we must check any given key usage extension in a CA cert * when preparing the verification of a certificate issued by it. * According to https://tools.ietf.org/html/rfc5280#section-4.2.1.3 - * we must not verify a certifiate signature if the key usage of the - * CA certificate that issued the certificate prohibits signing. + * we must not verify a certificate signature if the key usage of + * the CA certificate that issued the certificate prohibits signing. * In case the 'issuing' certificate is the last in the chain and is * not a CA certificate but a 'self-issued' end-entity cert (i.e., * xs == xi && !(xi->ex_flags & EXFLAG_CA)) RFC 5280 does not apply @@ -1804,24 +1813,21 @@ static int internal_verify(X509_STORE_CTX *ctx) * we are free to ignore any key usage restrictions on such certs. */ int ret = xs == xi && (xi->ex_flags & EXFLAG_CA) == 0 - ? X509_V_OK : x509_signing_allowed(xi, xs); + ? X509_V_OK : ossl_x509_signing_allowed(xi, xs); - if (ret != X509_V_OK && !verify_cb_cert(ctx, xi, issuer_depth, ret)) - return 0; + CB_FAIL_IF(ret != X509_V_OK, ctx, xi, issuer_depth, ret); if ((pkey = X509_get0_pubkey(xi)) == NULL) { - ret = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY; - if (!verify_cb_cert(ctx, xi, issuer_depth, ret)) - return 0; - } else if (X509_verify(xs, pkey) <= 0) { - ret = X509_V_ERR_CERT_SIGNATURE_FAILURE; - if (!verify_cb_cert(ctx, xs, n, ret)) - return 0; + CB_FAIL_IF(1, ctx, xi, issuer_depth, + X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); + } else { + CB_FAIL_IF(X509_verify(xs, pkey) <= 0, + ctx, xs, n, X509_V_ERR_CERT_SIGNATURE_FAILURE); } } - check_cert_time: /* in addition to RFC 5280, do also for trusted (root) cert */ + /* In addition to RFC 5280 requirements do also for trust anchor cert */ /* Calls verify callback as needed */ - if (!x509_check_cert_time(ctx, xs, n)) + if (!ossl_x509_check_cert_time(ctx, xs, n)) return 0; /* @@ -1858,7 +1864,8 @@ int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) #else const char upper_z = 'Z'; #endif - /* + + /*- * Note that ASN.1 allows much more slack in the time format than RFC5280. * In RFC5280, the representation is fixed: * UTCTime: YYMMDDHHMMSSZ @@ -1888,7 +1895,7 @@ int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) * Digit and date ranges will be verified in the conversion methods. */ for (i = 0; i < ctm->length - 1; i++) { - if (!ascii_isdigit(ctm->data[i])) + if (!ossl_ascii_isdigit(ctm->data[i])) return 0; } if (ctm->data[ctm->length - 1] != upper_z) @@ -1902,7 +1909,7 @@ int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) asn1_cmp_time = X509_time_adj(NULL, 0, cmp_time); if (asn1_cmp_time == NULL) goto err; - if (!ASN1_TIME_diff(&day, &sec, ctm, asn1_cmp_time)) + if (ASN1_TIME_diff(&day, &sec, ctm, asn1_cmp_time) == 0) goto err; /* @@ -1916,6 +1923,31 @@ int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) return ret; } +/* + * Return 0 if time should not be checked or reference time is in range, + * or else 1 if it is past the end, or -1 if it is before the start + */ +int X509_cmp_timeframe(const X509_VERIFY_PARAM *vpm, + const ASN1_TIME *start, const ASN1_TIME *end) +{ + time_t ref_time; + time_t *time = NULL; + unsigned long flags = vpm == NULL ? 0 : X509_VERIFY_PARAM_get_flags(vpm); + + if ((flags & X509_V_FLAG_USE_CHECK_TIME) != 0) { + ref_time = X509_VERIFY_PARAM_get_time(vpm); + time = &ref_time; + } else if ((flags & X509_V_FLAG_NO_CHECK_TIME) != 0) { + return 0; /* this means ok */ + } /* else reference time is the current time */ + + if (end != NULL && X509_cmp_time(end, time) < 0) + return 1; + if (start != NULL && X509_cmp_time(start, time) > 0) + return -1; + return 0; +} + ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj) { return X509_time_adj(s, adj, NULL); @@ -1936,7 +1968,7 @@ ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s, else time(&t); - if (s && !(s->flags & ASN1_STRING_FLAG_MSTRING)) { + if (s != NULL && (s->flags & ASN1_STRING_FLAG_MSTRING) == 0) { if (s->type == V_ASN1_UTCTIME) return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec); if (s->type == V_ASN1_GENERALIZEDTIME) @@ -1945,87 +1977,92 @@ ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s, return ASN1_TIME_adj(s, t, offset_day, offset_sec); } +/* Copy any missing public key parameters up the chain towards pkey */ int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain) { EVP_PKEY *ktmp = NULL, *ktmp2; int i, j; - if ((pkey != NULL) && !EVP_PKEY_missing_parameters(pkey)) + if (pkey != NULL && !EVP_PKEY_missing_parameters(pkey)) return 1; for (i = 0; i < sk_X509_num(chain); i++) { ktmp = X509_get0_pubkey(sk_X509_value(chain, i)); if (ktmp == NULL) { - X509err(X509_F_X509_GET_PUBKEY_PARAMETERS, - X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY); + ERR_raise(ERR_LIB_X509, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY); return 0; } if (!EVP_PKEY_missing_parameters(ktmp)) break; + ktmp = NULL; } if (ktmp == NULL) { - X509err(X509_F_X509_GET_PUBKEY_PARAMETERS, - X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN); + ERR_raise(ERR_LIB_X509, X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN); return 0; } /* first, populate the other certs */ for (j = i - 1; j >= 0; j--) { ktmp2 = X509_get0_pubkey(sk_X509_value(chain, j)); - EVP_PKEY_copy_parameters(ktmp2, ktmp); + if (!EVP_PKEY_copy_parameters(ktmp2, ktmp)) + return 0; } if (pkey != NULL) - EVP_PKEY_copy_parameters(pkey, ktmp); + return EVP_PKEY_copy_parameters(pkey, ktmp); return 1; } -/* Make a delta CRL as the diff between two full CRLs */ - +/* + * Make a delta CRL as the difference between two full CRLs. + * Sadly, returns NULL also on internal error. + */ X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, EVP_PKEY *skey, const EVP_MD *md, unsigned int flags) { X509_CRL *crl = NULL; int i; + STACK_OF(X509_REVOKED) *revs = NULL; /* CRLs can't be delta already */ - if (base->base_crl_number || newer->base_crl_number) { - X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_ALREADY_DELTA); + if (base->base_crl_number != NULL || newer->base_crl_number != NULL) { + ERR_raise(ERR_LIB_X509, X509_R_CRL_ALREADY_DELTA); return NULL; } /* Base and new CRL must have a CRL number */ - if (!base->crl_number || !newer->crl_number) { - X509err(X509_F_X509_CRL_DIFF, X509_R_NO_CRL_NUMBER); + if (base->crl_number == NULL || newer->crl_number == NULL) { + ERR_raise(ERR_LIB_X509, X509_R_NO_CRL_NUMBER); return NULL; } /* Issuer names must match */ - if (X509_NAME_cmp(X509_CRL_get_issuer(base), X509_CRL_get_issuer(newer))) { - X509err(X509_F_X509_CRL_DIFF, X509_R_ISSUER_MISMATCH); + if (X509_NAME_cmp(X509_CRL_get_issuer(base), + X509_CRL_get_issuer(newer)) != 0) { + ERR_raise(ERR_LIB_X509, X509_R_ISSUER_MISMATCH); return NULL; } /* AKID and IDP must match */ if (!crl_extension_match(base, newer, NID_authority_key_identifier)) { - X509err(X509_F_X509_CRL_DIFF, X509_R_AKID_MISMATCH); + ERR_raise(ERR_LIB_X509, X509_R_AKID_MISMATCH); return NULL; } if (!crl_extension_match(base, newer, NID_issuing_distribution_point)) { - X509err(X509_F_X509_CRL_DIFF, X509_R_IDP_MISMATCH); + ERR_raise(ERR_LIB_X509, X509_R_IDP_MISMATCH); return NULL; } /* Newer CRL number must exceed full CRL number */ if (ASN1_INTEGER_cmp(newer->crl_number, base->crl_number) <= 0) { - X509err(X509_F_X509_CRL_DIFF, X509_R_NEWER_CRL_NOT_NEWER); + ERR_raise(ERR_LIB_X509, X509_R_NEWER_CRL_NOT_NEWER); return NULL; } /* CRLs must verify */ - if (skey && (X509_CRL_verify(base, skey) <= 0 || - X509_CRL_verify(newer, skey) <= 0)) { - X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_VERIFY_FAILURE); + if (skey != NULL && (X509_CRL_verify(base, skey) <= 0 || + X509_CRL_verify(newer, skey) <= 0)) { + ERR_raise(ERR_LIB_X509, X509_R_CRL_VERIFY_FAILURE); return NULL; } /* Create new CRL */ - crl = X509_CRL_new(); - if (crl == NULL || !X509_CRL_set_version(crl, 1)) + crl = X509_CRL_new_ex(base->libctx, base->propq); + if (crl == NULL || !X509_CRL_set_version(crl, X509_CRL_VERSION_2)) goto memerr; /* Set issuer name */ if (!X509_CRL_set_issuer_name(crl, X509_CRL_get_issuer(newer))) @@ -2037,7 +2074,6 @@ X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, goto memerr; /* Set base CRL number: must be critical */ - if (!X509_CRL_add1_ext_i2d(crl, NID_delta_crl, base->crl_number, 1, 0)) goto memerr; @@ -2045,28 +2081,28 @@ X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, * Copy extensions across from newest CRL to delta: this will set CRL * number to correct value too. */ - for (i = 0; i < X509_CRL_get_ext_count(newer); i++) { - X509_EXTENSION *ext; - ext = X509_CRL_get_ext(newer, i); + X509_EXTENSION *ext = X509_CRL_get_ext(newer, i); + if (!X509_CRL_add_ext(crl, ext, -1)) goto memerr; } /* Go through revoked entries, copying as needed */ - revs = X509_CRL_get_REVOKED(newer); for (i = 0; i < sk_X509_REVOKED_num(revs); i++) { X509_REVOKED *rvn, *rvtmp; + rvn = sk_X509_REVOKED_value(revs, i); /* - * Add only if not also in base. TODO: need something cleverer here - * for some more complex CRLs covering multiple CAs. + * Add only if not also in base. + * Need something cleverer here for some more complex CRLs covering + * multiple CAs. */ if (!X509_CRL_get0_by_serial(base, &rvtmp, &rvn->serialNumber)) { rvtmp = X509_REVOKED_dup(rvn); - if (!rvtmp) + if (rvtmp == NULL) goto memerr; if (!X509_CRL_add0_revoked(crl, rvtmp)) { X509_REVOKED_free(rvtmp); @@ -2074,15 +2110,14 @@ X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, } } } - /* TODO: optionally prune deleted entries */ - if (skey && md && !X509_CRL_sign(crl, skey, md)) + if (skey != NULL && md != NULL && !X509_CRL_sign(crl, skey, md)) goto memerr; return crl; memerr: - X509err(X509_F_X509_CRL_DIFF, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); X509_CRL_free(crl); return NULL; } @@ -2092,12 +2127,12 @@ int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx, int idx, void *data) return CRYPTO_set_ex_data(&ctx->ex_data, idx, data); } -void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx) +void *X509_STORE_CTX_get_ex_data(const X509_STORE_CTX *ctx, int idx) { return CRYPTO_get_ex_data(&ctx->ex_data, idx); } -int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx) +int X509_STORE_CTX_get_error(const X509_STORE_CTX *ctx) { return ctx->error; } @@ -2107,7 +2142,7 @@ void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int err) ctx->error = err; } -int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx) +int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX *ctx) { return ctx->error_depth; } @@ -2117,7 +2152,7 @@ void X509_STORE_CTX_set_error_depth(X509_STORE_CTX *ctx, int depth) ctx->error_depth = depth; } -X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx) +X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx) { return ctx->current_cert; } @@ -2127,29 +2162,29 @@ void X509_STORE_CTX_set_current_cert(X509_STORE_CTX *ctx, X509 *x) ctx->current_cert = x; } -STACK_OF(X509) *X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx) +STACK_OF(X509) *X509_STORE_CTX_get0_chain(const X509_STORE_CTX *ctx) { return ctx->chain; } -STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx) +STACK_OF(X509) *X509_STORE_CTX_get1_chain(const X509_STORE_CTX *ctx) { - if (!ctx->chain) + if (ctx->chain == NULL) return NULL; return X509_chain_up_ref(ctx->chain); } -X509 *X509_STORE_CTX_get0_current_issuer(X509_STORE_CTX *ctx) +X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) { return ctx->current_issuer; } -X509_CRL *X509_STORE_CTX_get0_current_crl(X509_STORE_CTX *ctx) +X509_CRL *X509_STORE_CTX_get0_current_crl(const X509_STORE_CTX *ctx) { return ctx->current_crl; } -X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(X509_STORE_CTX *ctx) +X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(const X509_STORE_CTX *ctx) { return ctx->parent; } @@ -2193,13 +2228,13 @@ int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust) * application can set: if they aren't set then we use the default of SSL * client/server. */ - int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose, int purpose, int trust) { int idx; + /* If purpose not set use default */ - if (!purpose) + if (purpose == 0) purpose = def_purpose; /* * If purpose is set but we don't have a default then set the default to @@ -2208,70 +2243,91 @@ int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose, else if (def_purpose == 0) def_purpose = purpose; /* If we have a purpose then check it is valid */ - if (purpose) { + if (purpose != 0) { X509_PURPOSE *ptmp; + idx = X509_PURPOSE_get_by_id(purpose); if (idx == -1) { - X509err(X509_F_X509_STORE_CTX_PURPOSE_INHERIT, - X509_R_UNKNOWN_PURPOSE_ID); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_PURPOSE_ID); return 0; } ptmp = X509_PURPOSE_get0(idx); if (ptmp->trust == X509_TRUST_DEFAULT) { idx = X509_PURPOSE_get_by_id(def_purpose); if (idx == -1) { - X509err(X509_F_X509_STORE_CTX_PURPOSE_INHERIT, - X509_R_UNKNOWN_PURPOSE_ID); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_PURPOSE_ID); return 0; } ptmp = X509_PURPOSE_get0(idx); } /* If trust not set then get from purpose default */ - if (!trust) + if (trust == 0) trust = ptmp->trust; } - if (trust) { + if (trust != 0) { idx = X509_TRUST_get_by_id(trust); if (idx == -1) { - X509err(X509_F_X509_STORE_CTX_PURPOSE_INHERIT, - X509_R_UNKNOWN_TRUST_ID); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_TRUST_ID); return 0; } } - if (purpose && !ctx->param->purpose) + if (ctx->param->purpose == 0 && purpose != 0) ctx->param->purpose = purpose; - if (trust && !ctx->param->trust) + if (ctx->param->trust == 0 && trust != 0) ctx->param->trust = trust; return 1; } -X509_STORE_CTX *X509_STORE_CTX_new(void) +X509_STORE_CTX *X509_STORE_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq) { X509_STORE_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); if (ctx == NULL) { - X509err(X509_F_X509_STORE_CTX_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } + + ctx->libctx = libctx; + if (propq != NULL) { + ctx->propq = OPENSSL_strdup(propq); + if (ctx->propq == NULL) { + OPENSSL_free(ctx); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + return NULL; + } + } + return ctx; } +X509_STORE_CTX *X509_STORE_CTX_new(void) +{ + return X509_STORE_CTX_new_ex(NULL, NULL); +} + void X509_STORE_CTX_free(X509_STORE_CTX *ctx) { if (ctx == NULL) return; X509_STORE_CTX_cleanup(ctx); + + /* libctx and propq survive X509_STORE_CTX_cleanup() */ + OPENSSL_free(ctx->propq); OPENSSL_free(ctx); } int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, STACK_OF(X509) *chain) { - int ret = 1; + if (ctx == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + X509_STORE_CTX_cleanup(ctx); - ctx->ctx = store; + ctx->store = store; ctx->cert = x509; ctx->untrusted = chain; ctx->crls = NULL; @@ -2279,7 +2335,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, ctx->other_ctx = NULL; ctx->valid = 0; ctx->chain = NULL; - ctx->error = 0; + ctx->error = X509_V_OK; ctx->explicit_policy = 0; ctx->error_depth = 0; ctx->current_cert = NULL; @@ -2295,88 +2351,80 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, memset(&ctx->ex_data, 0, sizeof(ctx->ex_data)); /* store->cleanup is always 0 in OpenSSL, if set must be idempotent */ - if (store) + if (store != NULL) ctx->cleanup = store->cleanup; else - ctx->cleanup = 0; + ctx->cleanup = NULL; - if (store && store->check_issued) + if (store != NULL && store->check_issued != NULL) ctx->check_issued = store->check_issued; else ctx->check_issued = check_issued; - if (store && store->get_issuer) + if (store != NULL && store->get_issuer != NULL) ctx->get_issuer = store->get_issuer; else ctx->get_issuer = X509_STORE_CTX_get1_issuer; - if (store && store->verify_cb) + if (store != NULL && store->verify_cb != NULL) ctx->verify_cb = store->verify_cb; else ctx->verify_cb = null_callback; - if (store && store->verify) + if (store != NULL && store->verify != NULL) ctx->verify = store->verify; else ctx->verify = internal_verify; - if (store && store->check_revocation) + if (store != NULL && store->check_revocation != NULL) ctx->check_revocation = store->check_revocation; else ctx->check_revocation = check_revocation; - if (store && store->get_crl) + if (store != NULL && store->get_crl != NULL) ctx->get_crl = store->get_crl; else ctx->get_crl = NULL; - if (store && store->check_crl) + if (store != NULL && store->check_crl != NULL) ctx->check_crl = store->check_crl; else ctx->check_crl = check_crl; - if (store && store->cert_crl) + if (store != NULL && store->cert_crl != NULL) ctx->cert_crl = store->cert_crl; else ctx->cert_crl = cert_crl; - if (store && store->check_policy) + if (store != NULL && store->check_policy != NULL) ctx->check_policy = store->check_policy; else ctx->check_policy = check_policy; - if (store && store->lookup_certs) + if (store != NULL && store->lookup_certs != NULL) ctx->lookup_certs = store->lookup_certs; else ctx->lookup_certs = X509_STORE_CTX_get1_certs; - if (store && store->lookup_crls) + if (store != NULL && store->lookup_crls != NULL) ctx->lookup_crls = store->lookup_crls; else ctx->lookup_crls = X509_STORE_CTX_get1_crls; ctx->param = X509_VERIFY_PARAM_new(); if (ctx->param == NULL) { - X509err(X509_F_X509_STORE_CTX_INIT, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } - /* - * Inherit callbacks and flags from X509_STORE if not set use defaults. - */ - if (store) - ret = X509_VERIFY_PARAM_inherit(ctx->param, store->param); - else + /* Inherit callbacks and flags from X509_STORE if not set use defaults. */ + if (store == NULL) ctx->param->inh_flags |= X509_VP_FLAG_DEFAULT | X509_VP_FLAG_ONCE; + else if (X509_VERIFY_PARAM_inherit(ctx->param, store->param) == 0) + goto err; - if (ret) - ret = X509_VERIFY_PARAM_inherit(ctx->param, - X509_VERIFY_PARAM_lookup("default")); - - if (ret == 0) { - X509err(X509_F_X509_STORE_CTX_INIT, ERR_R_MALLOC_FAILURE); + if (!X509_STORE_CTX_set_default(ctx, "default")) goto err; - } /* * XXX: For now, continue to inherit trust from VPM, but infer from the @@ -2393,7 +2441,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, if (CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx, &ctx->ex_data)) return 1; - X509err(X509_F_X509_STORE_CTX_INIT, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); err: /* @@ -2405,8 +2453,8 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, } /* - * Set alternative lookup method: just a STACK of trusted certificates. This - * avoids X509_STORE nastiness where it isn't needed. + * Set alternative get_issuer method: just from a STACK of trusted certificates. + * This avoids the complexity of X509_STORE where it is not needed. */ void X509_STORE_CTX_set0_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk) { @@ -2423,7 +2471,7 @@ void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx) * calls cleanup() for the same object twice! Thus we must zero the * pointers below after they're freed! */ - /* Seems to always be 0 in OpenSSL, do this at most once. */ + /* Seems to always be NULL in OpenSSL, do this at most once. */ if (ctx->cleanup != NULL) { ctx->cleanup(ctx); ctx->cleanup = NULL; @@ -2457,12 +2505,12 @@ void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags, X509_VERIFY_PARAM_set_time(ctx->param, t); } -X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx) +X509 *X509_STORE_CTX_get0_cert(const X509_STORE_CTX *ctx) { return ctx->cert; } -STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(X509_STORE_CTX *ctx) +STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX *ctx) { return ctx->untrusted; } @@ -2484,7 +2532,7 @@ void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx, ctx->verify_cb = verify_cb; } -X509_STORE_CTX_verify_cb X509_STORE_CTX_get_verify_cb(X509_STORE_CTX *ctx) +X509_STORE_CTX_verify_cb X509_STORE_CTX_get_verify_cb(const X509_STORE_CTX *ctx) { return ctx->verify_cb; } @@ -2495,72 +2543,80 @@ void X509_STORE_CTX_set_verify(X509_STORE_CTX *ctx, ctx->verify = verify; } -X509_STORE_CTX_verify_fn X509_STORE_CTX_get_verify(X509_STORE_CTX *ctx) +X509_STORE_CTX_verify_fn X509_STORE_CTX_get_verify(const X509_STORE_CTX *ctx) { return ctx->verify; } -X509_STORE_CTX_get_issuer_fn X509_STORE_CTX_get_get_issuer(X509_STORE_CTX *ctx) +X509_STORE_CTX_get_issuer_fn +X509_STORE_CTX_get_get_issuer(const X509_STORE_CTX *ctx) { return ctx->get_issuer; } -X509_STORE_CTX_check_issued_fn X509_STORE_CTX_get_check_issued(X509_STORE_CTX *ctx) +X509_STORE_CTX_check_issued_fn +X509_STORE_CTX_get_check_issued(const X509_STORE_CTX *ctx) { return ctx->check_issued; } -X509_STORE_CTX_check_revocation_fn X509_STORE_CTX_get_check_revocation(X509_STORE_CTX *ctx) +X509_STORE_CTX_check_revocation_fn +X509_STORE_CTX_get_check_revocation(const X509_STORE_CTX *ctx) { return ctx->check_revocation; } -X509_STORE_CTX_get_crl_fn X509_STORE_CTX_get_get_crl(X509_STORE_CTX *ctx) +X509_STORE_CTX_get_crl_fn X509_STORE_CTX_get_get_crl(const X509_STORE_CTX *ctx) { return ctx->get_crl; } -X509_STORE_CTX_check_crl_fn X509_STORE_CTX_get_check_crl(X509_STORE_CTX *ctx) +X509_STORE_CTX_check_crl_fn +X509_STORE_CTX_get_check_crl(const X509_STORE_CTX *ctx) { return ctx->check_crl; } -X509_STORE_CTX_cert_crl_fn X509_STORE_CTX_get_cert_crl(X509_STORE_CTX *ctx) +X509_STORE_CTX_cert_crl_fn +X509_STORE_CTX_get_cert_crl(const X509_STORE_CTX *ctx) { return ctx->cert_crl; } -X509_STORE_CTX_check_policy_fn X509_STORE_CTX_get_check_policy(X509_STORE_CTX *ctx) +X509_STORE_CTX_check_policy_fn +X509_STORE_CTX_get_check_policy(const X509_STORE_CTX *ctx) { return ctx->check_policy; } -X509_STORE_CTX_lookup_certs_fn X509_STORE_CTX_get_lookup_certs(X509_STORE_CTX *ctx) +X509_STORE_CTX_lookup_certs_fn +X509_STORE_CTX_get_lookup_certs(const X509_STORE_CTX *ctx) { return ctx->lookup_certs; } -X509_STORE_CTX_lookup_crls_fn X509_STORE_CTX_get_lookup_crls(X509_STORE_CTX *ctx) +X509_STORE_CTX_lookup_crls_fn +X509_STORE_CTX_get_lookup_crls(const X509_STORE_CTX *ctx) { return ctx->lookup_crls; } -X509_STORE_CTX_cleanup_fn X509_STORE_CTX_get_cleanup(X509_STORE_CTX *ctx) +X509_STORE_CTX_cleanup_fn X509_STORE_CTX_get_cleanup(const X509_STORE_CTX *ctx) { return ctx->cleanup; } -X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(X509_STORE_CTX *ctx) +X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(const X509_STORE_CTX *ctx) { return ctx->tree; } -int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx) +int X509_STORE_CTX_get_explicit_policy(const X509_STORE_CTX *ctx) { return ctx->explicit_policy; } -int X509_STORE_CTX_get_num_untrusted(X509_STORE_CTX *ctx) +int X509_STORE_CTX_get_num_untrusted(const X509_STORE_CTX *ctx) { return ctx->num_untrusted; } @@ -2568,13 +2624,16 @@ int X509_STORE_CTX_get_num_untrusted(X509_STORE_CTX *ctx) int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name) { const X509_VERIFY_PARAM *param; + param = X509_VERIFY_PARAM_lookup(name); - if (!param) + if (param == NULL) { + ERR_raise_data(ERR_LIB_X509, X509_R_UNKNOWN_PURPOSE_ID, "name=%s", name); return 0; + } return X509_VERIFY_PARAM_inherit(ctx->param, param); } -X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx) +X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(const X509_STORE_CTX *ctx) { return ctx->param; } @@ -2590,10 +2649,8 @@ void X509_STORE_CTX_set0_dane(X509_STORE_CTX *ctx, SSL_DANE *dane) ctx->dane = dane; } -static unsigned char *dane_i2d( - X509 *cert, - uint8_t selector, - unsigned int *i2dlen) +static unsigned char *dane_i2d(X509 *cert, uint8_t selector, + unsigned int *i2dlen) { unsigned char *buf = NULL; int len; @@ -2609,12 +2666,12 @@ static unsigned char *dane_i2d( len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &buf); break; default: - X509err(X509_F_DANE_I2D, X509_R_BAD_SELECTOR); + ERR_raise(ERR_LIB_X509, X509_R_BAD_SELECTOR); return NULL; } if (len < 0 || buf == NULL) { - X509err(X509_F_DANE_I2D, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } @@ -2622,8 +2679,9 @@ static unsigned char *dane_i2d( return buf; } -#define DANETLS_NONE 256 /* impossible uint8_t */ +#define DANETLS_NONE 256 /* impossible uint8_t */ +/* Returns -1 on internal error */ static int dane_match(X509_STORE_CTX *ctx, X509 *cert, int depth) { SSL_DANE *dane = ctx->dane; @@ -2644,9 +2702,7 @@ static int dane_match(X509_STORE_CTX *ctx, X509 *cert, int depth) mask = (depth == 0) ? DANETLS_EE_MASK : DANETLS_TA_MASK; - /* - * The trust store is not applicable with DANE-TA(2) - */ + /* The trust store is not applicable with DANE-TA(2) */ if (depth >= ctx->num_untrusted) mask &= DANETLS_PKIX_MASK; @@ -2686,7 +2742,7 @@ static int dane_match(X509_STORE_CTX *ctx, X509 *cert, int depth) * exhausting all DANE-?? records, we've matched a PKIX-?? record, which is * sufficient for DANE, and what remains to do is ordinary PKIX validation. */ - recnum = (dane->umask & mask) ? sk_danetls_record_num(dane->trecs) : 0; + recnum = (dane->umask & mask) != 0 ? sk_danetls_record_num(dane->trecs) : 0; for (i = 0; matched == 0 && i < recnum; ++i) { t = sk_danetls_record_value(dane->trecs, i); if ((DANETLS_USAGE_BIT(t->usage) & mask) == 0) @@ -2730,6 +2786,7 @@ static int dane_match(X509_STORE_CTX *ctx, X509 *cert, int depth) */ if (t->mtype != mtype) { const EVP_MD *md = dane->dctx->mdevp[mtype = t->mtype]; + cmpbuf = i2dbuf; cmplen = i2dlen; @@ -2767,6 +2824,7 @@ static int dane_match(X509_STORE_CTX *ctx, X509 *cert, int depth) return matched; } +/* Returns -1 on internal error */ static int check_dane_issuer(X509_STORE_CTX *ctx, int depth) { SSL_DANE *dane = ctx->dane; @@ -2774,22 +2832,22 @@ static int check_dane_issuer(X509_STORE_CTX *ctx, int depth) X509 *cert; if (!DANETLS_HAS_TA(dane) || depth == 0) - return X509_TRUST_UNTRUSTED; + return X509_TRUST_UNTRUSTED; /* - * Record any DANE trust-anchor matches, for the first depth to test, if + * Record any DANE trust anchor matches, for the first depth to test, if * there's one at that depth. (This'll be false for length 1 chains looking * for an exact match for the leaf certificate). */ cert = sk_X509_value(ctx->chain, depth); if (cert != NULL && (matched = dane_match(ctx, cert, depth)) < 0) - return X509_TRUST_REJECTED; + return matched; if (matched > 0) { ctx->num_untrusted = depth - 1; - return X509_TRUST_TRUSTED; + return X509_TRUST_TRUSTED; } - return X509_TRUST_UNTRUSTED; + return X509_TRUST_UNTRUSTED; } static int check_dane_pkeys(X509_STORE_CTX *ctx) @@ -2831,9 +2889,7 @@ static int check_dane_pkeys(X509_STORE_CTX *ctx) static void dane_reset(SSL_DANE *dane) { - /* - * Reset state to verify another chain, or clear after failure. - */ + /* Reset state to verify another chain, or clear after failure. */ X509_free(dane->mcert); dane->mcert = NULL; dane->mtlsa = NULL; @@ -2845,11 +2901,11 @@ static int check_leaf_suiteb(X509_STORE_CTX *ctx, X509 *cert) { int err = X509_chain_check_suiteb(NULL, cert, NULL, ctx->param->flags); - if (err == X509_V_OK) - return 1; - return verify_cb_cert(ctx, cert, 0, err); + CB_FAIL_IF(err != X509_V_OK, ctx, cert, 0, err); + return 1; } +/* Returns -1 on internal error */ static int dane_verify(X509_STORE_CTX *ctx) { X509 *cert = ctx->cert; @@ -2863,7 +2919,7 @@ static int dane_verify(X509_STORE_CTX *ctx) * When testing the leaf certificate, if we match a DANE-EE(3) record, * dane_match() returns 1 and we're done. If however we match a PKIX-EE(1) * record, the match depth and matching TLSA record are recorded, but the - * return value is 0, because we still need to find a PKIX trust-anchor. + * return value is 0, because we still need to find a PKIX trust anchor. * Therefore, when DANE authentication is enabled (required), we're done * if: * + matched < 0, internal error. @@ -2874,8 +2930,8 @@ static int dane_verify(X509_STORE_CTX *ctx) matched = dane_match(ctx, ctx->cert, 0); done = matched != 0 || (!DANETLS_HAS_TA(dane) && dane->mdpth < 0); - if (done) - X509_get_pubkey_parameters(NULL, ctx->chain); + if (done && !X509_get_pubkey_parameters(NULL, ctx->chain)) + return -1; if (matched > 0) { /* Callback invoked as needed */ @@ -2912,8 +2968,11 @@ static int dane_verify(X509_STORE_CTX *ctx) return verify_chain(ctx); } -/* Get issuer, without duplicate suppression */ -static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *cert) +/* + * Get trusted issuer, without duplicate suppression + * Returns -1 on internal error. + */ +static int get1_trusted_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *cert) { STACK_OF(X509) *saved_chain = ctx->chain; int ok; @@ -2925,118 +2984,89 @@ static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *cert) return ok; } -static int augment_stack(STACK_OF(X509) *src, STACK_OF(X509) **dstPtr) -{ - if (src) { - STACK_OF(X509) *dst; - int i; - - if (*dstPtr == NULL) - return ((*dstPtr = sk_X509_dup(src)) != NULL); - - for (dst = *dstPtr, i = 0; i < sk_X509_num(src); ++i) { - if (!sk_X509_push(dst, sk_X509_value(src, i))) { - sk_X509_free(dst); - *dstPtr = NULL; - return 0; - } - } - } - return 1; -} - +/* Returns -1 on internal error */ static int build_chain(X509_STORE_CTX *ctx) { SSL_DANE *dane = ctx->dane; int num = sk_X509_num(ctx->chain); - X509 *cert = sk_X509_value(ctx->chain, num - 1); - int ss = cert_self_signed(cert); - STACK_OF(X509) *sktmp = NULL; + STACK_OF(X509) *sk_untrusted = NULL; unsigned int search; int may_trusted = 0; int may_alternate = 0; int trust = X509_TRUST_UNTRUSTED; int alt_untrusted = 0; - int depth; + int max_depth; int ok = 0; int i; /* Our chain starts with a single untrusted element. */ - if (!ossl_assert(num == 1 && ctx->num_untrusted == num)) { - X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR); - ctx->error = X509_V_ERR_UNSPECIFIED; - return 0; - } + if (!ossl_assert(num == 1 && ctx->num_untrusted == num)) + goto int_err; -#define S_DOUNTRUSTED (1 << 0) /* Search untrusted chain */ -#define S_DOTRUSTED (1 << 1) /* Search trusted store */ -#define S_DOALTERNATE (1 << 2) /* Retry with pruned alternate chain */ +#define S_DOUNTRUSTED (1 << 0) /* Search untrusted chain */ +#define S_DOTRUSTED (1 << 1) /* Search trusted store */ +#define S_DOALTERNATE (1 << 2) /* Retry with pruned alternate chain */ /* - * Set up search policy, untrusted if possible, trusted-first if enabled. + * Set up search policy, untrusted if possible, trusted-first if enabled, + * which is the default. * If we're doing DANE and not doing PKIX-TA/PKIX-EE, we never look in the * trust_store, otherwise we might look there first. If not trusted-first, * and alternate chains are not disabled, try building an alternate chain * if no luck with untrusted first. */ - search = (ctx->untrusted != NULL) ? S_DOUNTRUSTED : 0; + search = ctx->untrusted != NULL ? S_DOUNTRUSTED : 0; if (DANETLS_HAS_PKIX(dane) || !DANETLS_HAS_DANE(dane)) { - if (search == 0 || ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) + if (search == 0 || (ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) != 0) search |= S_DOTRUSTED; else if (!(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) may_alternate = 1; may_trusted = 1; } + /* Initialize empty untrusted stack. */ + if ((sk_untrusted = sk_X509_new_null()) == NULL) + goto memerr; + /* - * If we got any "Cert(0) Full(0)" issuer certificates from DNS, *prepend* - * them to our working copy of the untrusted certificate stack. Since the - * caller of X509_STORE_CTX_init() may have provided only a leaf cert with - * no corresponding stack of untrusted certificates, we may need to create - * an empty stack first. [ At present only the ssl library provides DANE - * support, and ssl_verify_cert_chain() always provides a non-null stack - * containing at least the leaf certificate, but we must be prepared for - * this to change. ] + * If we got any "Cert(0) Full(0)" trust anchors from DNS, *prepend* them + * to our working copy of the untrusted certificate stack. */ - if (DANETLS_ENABLED(dane) && !augment_stack(dane->certs, &sktmp)) { - X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); - ctx->error = X509_V_ERR_OUT_OF_MEM; - return 0; - } + if (DANETLS_ENABLED(dane) && dane->certs != NULL + && !X509_add_certs(sk_untrusted, dane->certs, X509_ADD_FLAG_DEFAULT)) + goto memerr; /* * Shallow-copy the stack of untrusted certificates (with TLS, this is - * typically the content of the peer's certificate message) so can make + * typically the content of the peer's certificate message) so we can make * multiple passes over it, while free to remove elements as we go. */ - if (!augment_stack(ctx->untrusted, &sktmp)) { - X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); - ctx->error = X509_V_ERR_OUT_OF_MEM; - return 0; - } + if (!X509_add_certs(sk_untrusted, ctx->untrusted, X509_ADD_FLAG_DEFAULT)) + goto memerr; /* * Still absurdly large, but arithmetically safe, a lower hard upper bound * might be reasonable. */ - if (ctx->param->depth > INT_MAX/2) - ctx->param->depth = INT_MAX/2; + if (ctx->param->depth > INT_MAX / 2) + ctx->param->depth = INT_MAX / 2; /* - * Try to Extend the chain until we reach an ultimately trusted issuer. + * Try to extend the chain until we reach an ultimately trusted issuer. * Build chains up to one longer the limit, later fail if we hit the limit, * with an X509_V_ERR_CERT_CHAIN_TOO_LONG error code. */ - depth = ctx->param->depth + 1; + max_depth = ctx->param->depth + 1; while (search != 0) { - X509 *x; - X509 *xtmp = NULL; + X509 *curr, *issuer = NULL; + num = sk_X509_num(ctx->chain); + ctx->error_depth = num - 1; /* * Look in the trust store if enabled for first lookup, or we've run * out of untrusted issuers and search here is not disabled. When we * reach the depth limit, we stop extending the chain, if by that point - * we've not found a trust-anchor, any trusted chain would be too long. + * we've not found a trust anchor, any trusted chain would be too long. * * The error reported to the application verify callback is at the * maximal valid depth with the current certificate equal to the last @@ -3047,7 +3077,7 @@ static int build_chain(X509_STORE_CTX *ctx) * would be a-priori too long. */ if ((search & S_DOTRUSTED) != 0) { - i = num = sk_X509_num(ctx->chain); + i = num; if ((search & S_DOALTERNATE) != 0) { /* * As high up the chain as we can, look for an alternative @@ -3066,24 +3096,30 @@ static int build_chain(X509_STORE_CTX *ctx) */ i = alt_untrusted; } - x = sk_X509_value(ctx->chain, i-1); + curr = sk_X509_value(ctx->chain, i - 1); - ok = (depth < num) ? 0 : get_issuer(&xtmp, ctx, x); + /* Note: get1_trusted_issuer() must be used even if self-signed. */ + ok = num > max_depth ? 0 : get1_trusted_issuer(&issuer, ctx, curr); if (ok < 0) { - trust = X509_TRUST_REJECTED; + trust = -1; ctx->error = X509_V_ERR_STORE_LOOKUP; - search = 0; - continue; + break; } if (ok > 0) { + int self_signed = X509_self_signed(curr, 0); + + if (self_signed < 0) { + X509_free(issuer); + goto int_err; + } /* * Alternative trusted issuer for a mid-chain untrusted cert? * Pop the untrusted cert's successors and retry. We might now * be able to complete a valid chain via the trust store. Note - * that despite the current trust-store match we might still - * fail complete the chain to a suitable trust-anchor, in which + * that despite the current trust store match we might still + * fail complete the chain to a suitable trust anchor, in which * case we may prune some more untrusted certificates and try * again. Thus the S_DOALTERNATE bit may yet be turned on * again with an even shorter untrusted chain! @@ -3093,13 +3129,9 @@ static int build_chain(X509_STORE_CTX *ctx) * certificate among the ones from the trust store. */ if ((search & S_DOALTERNATE) != 0) { - if (!ossl_assert(num > i && i > 0 && ss == 0)) { - X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR); - X509_free(xtmp); - trust = X509_TRUST_REJECTED; - ctx->error = X509_V_ERR_UNSPECIFIED; - search = 0; - continue; + if (!ossl_assert(num > i && i > 0 && !self_signed)) { + X509_free(issuer); + goto int_err; } search &= ~S_DOALTERNATE; for (; num > i; --num) @@ -3121,36 +3153,33 @@ static int build_chain(X509_STORE_CTX *ctx) * Self-signed untrusted certificates get replaced by their * trusted matching issuer. Otherwise, grow the chain. */ - if (ss == 0) { - if (!sk_X509_push(ctx->chain, x = xtmp)) { - X509_free(xtmp); - X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); - trust = X509_TRUST_REJECTED; - ctx->error = X509_V_ERR_OUT_OF_MEM; - search = 0; - continue; + if (!self_signed) { + if (!sk_X509_push(ctx->chain, issuer)) { + X509_free(issuer); + goto memerr; } - ss = cert_self_signed(x); - } else if (num == ctx->num_untrusted) { + if ((self_signed = X509_self_signed(issuer, 0)) < 0) + goto int_err; + } else { /* * We have a self-signed certificate that has the same * subject name (and perhaps keyid and/or serial number) as - * a trust-anchor. We must have an exact match to avoid + * a trust anchor. We must have an exact match to avoid * possible impersonation via key substitution etc. */ - if (X509_cmp(x, xtmp) != 0) { + if (X509_cmp(curr, issuer) != 0) { /* Self-signed untrusted mimic. */ - X509_free(xtmp); + X509_free(issuer); ok = 0; - } else { - X509_free(x); + } else { /* curr "==" issuer */ + X509_free(curr); ctx->num_untrusted = --num; - (void) sk_X509_set(ctx->chain, num, x = xtmp); + (void)sk_X509_set(ctx->chain, num, issuer); } } /* - * We've added a new trusted certificate to the chain, recheck + * We've added a new trusted certificate to the chain, re-check * trust. If not done, and not self-signed look deeper. * Whether or not we're doing "trusted first", we no longer * look for untrusted certificates from the peer's chain. @@ -3163,21 +3192,13 @@ static int build_chain(X509_STORE_CTX *ctx) * certificate with ctx->num_untrusted <= num. */ if (ok) { - if (!ossl_assert(ctx->num_untrusted <= num)) { - X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR); - trust = X509_TRUST_REJECTED; - ctx->error = X509_V_ERR_UNSPECIFIED; - search = 0; - continue; - } + if (!ossl_assert(ctx->num_untrusted <= num)) + goto int_err; search &= ~S_DOUNTRUSTED; - switch (trust = check_trust(ctx, num)) { - case X509_TRUST_TRUSTED: - case X509_TRUST_REJECTED: - search = 0; - continue; - } - if (ss == 0) + trust = check_trust(ctx, num); + if (trust != X509_TRUST_UNTRUSTED) + break; + if (!self_signed) continue; } } @@ -3199,30 +3220,25 @@ static int build_chain(X509_STORE_CTX *ctx) /* Search for a trusted issuer of a shorter chain */ search |= S_DOALTERNATE; alt_untrusted = ctx->num_untrusted - 1; - ss = 0; } } /* - * Extend chain with peer-provided certificates + * Extend chain with peer-provided untrusted certificates */ if ((search & S_DOUNTRUSTED) != 0) { num = sk_X509_num(ctx->chain); - if (!ossl_assert(num == ctx->num_untrusted)) { - X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR); - trust = X509_TRUST_REJECTED; - ctx->error = X509_V_ERR_UNSPECIFIED; - search = 0; - continue; - } - x = sk_X509_value(ctx->chain, num-1); - - /* - * Once we run out of untrusted issuers, we stop looking for more - * and start looking only in the trust store if enabled. - */ - xtmp = (ss || depth < num) ? NULL : find_issuer(ctx, sktmp, x); - if (xtmp == NULL) { + if (!ossl_assert(num == ctx->num_untrusted)) + goto int_err; + curr = sk_X509_value(ctx->chain, num - 1); + issuer = (X509_self_signed(curr, 0) > 0 || num > max_depth) ? + NULL : find_issuer(ctx, sk_untrusted, curr); + if (issuer == NULL) { + /* + * Once we have reached a self-signed cert or num > max_depth + * or can't find an issuer in the untrusted list we stop looking + * there and start looking only in the trust store if enabled. + */ search &= ~S_DOUNTRUSTED; if (may_trusted) search |= S_DOTRUSTED; @@ -3230,48 +3246,30 @@ static int build_chain(X509_STORE_CTX *ctx) } /* Drop this issuer from future consideration */ - (void) sk_X509_delete_ptr(sktmp, xtmp); - - if (!X509_up_ref(xtmp)) { - X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR); - trust = X509_TRUST_REJECTED; - ctx->error = X509_V_ERR_UNSPECIFIED; - search = 0; - continue; - } + (void)sk_X509_delete_ptr(sk_untrusted, issuer); - if (!sk_X509_push(ctx->chain, xtmp)) { - X509_free(xtmp); - X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); - trust = X509_TRUST_REJECTED; - ctx->error = X509_V_ERR_OUT_OF_MEM; - search = 0; - continue; - } + if (!X509_add_cert(ctx->chain, issuer, X509_ADD_FLAG_UP_REF)) + goto int_err; - x = xtmp; ++ctx->num_untrusted; - ss = cert_self_signed(xtmp); - /* - * Check for DANE-TA trust of the topmost untrusted certificate. - */ - switch (trust = check_dane_issuer(ctx, ctx->num_untrusted - 1)) { - case X509_TRUST_TRUSTED: - case X509_TRUST_REJECTED: - search = 0; - continue; - } + /* Check for DANE-TA trust of the topmost untrusted certificate. */ + trust = check_dane_issuer(ctx, ctx->num_untrusted - 1); + if (trust == X509_TRUST_TRUSTED || trust == X509_TRUST_REJECTED) + break; } } - sk_X509_free(sktmp); + sk_X509_free(sk_untrusted); + + if (trust < 0) /* internal error */ + return trust; /* * Last chance to make a trusted chain, either bare DANE-TA public-key * signers, or else direct leaf PKIX trust. */ num = sk_X509_num(ctx->chain); - if (num <= depth) { + if (num <= max_depth) { if (trust == X509_TRUST_UNTRUSTED && DANETLS_HAS_DANE_TA(dane)) trust = check_dane_pkeys(ctx); if (trust == X509_TRUST_UNTRUSTED && num == ctx->num_untrusted) @@ -3286,34 +3284,97 @@ static int build_chain(X509_STORE_CTX *ctx) return 0; case X509_TRUST_UNTRUSTED: default: - num = sk_X509_num(ctx->chain); - if (num > depth) - return verify_cb_cert(ctx, NULL, num-1, - X509_V_ERR_CERT_CHAIN_TOO_LONG); - if (DANETLS_ENABLED(dane) && - (!DANETLS_HAS_PKIX(dane) || dane->pdpth >= 0)) - return verify_cb_cert(ctx, NULL, num-1, X509_V_ERR_DANE_NO_MATCH); - if (ss && sk_X509_num(ctx->chain) == 1) - return verify_cb_cert(ctx, NULL, num-1, - X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); - if (ss) - return verify_cb_cert(ctx, NULL, num-1, - X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN); - if (ctx->num_untrusted < num) - return verify_cb_cert(ctx, NULL, num-1, - X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); - return verify_cb_cert(ctx, NULL, num-1, - X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + switch(ctx->error) { + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_CERT_HAS_EXPIRED: + return 0; /* Callback already issued by ossl_x509_check_cert_time() */ + default: /* A preliminary error has become final */ + return verify_cb_cert(ctx, NULL, num - 1, ctx->error); + case X509_V_OK: + break; + } + CB_FAIL_IF(num > max_depth, + ctx, NULL, num - 1, X509_V_ERR_CERT_CHAIN_TOO_LONG); + CB_FAIL_IF(DANETLS_ENABLED(dane) + && (!DANETLS_HAS_PKIX(dane) || dane->pdpth >= 0), + ctx, NULL, num - 1, X509_V_ERR_DANE_NO_MATCH); + if (X509_self_signed(sk_X509_value(ctx->chain, num - 1), 0) > 0) + return verify_cb_cert(ctx, NULL, num - 1, + num == 1 + ? X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT + : X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN); + return verify_cb_cert(ctx, NULL, num - 1, + ctx->num_untrusted < num + ? X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT + : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + } + + int_err: + ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR); + ctx->error = X509_V_ERR_UNSPECIFIED; + sk_X509_free(sk_untrusted); + return -1; + + memerr: + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + ctx->error = X509_V_ERR_OUT_OF_MEM; + sk_X509_free(sk_untrusted); + return -1; +} + +STACK_OF(X509) *X509_build_chain(X509 *target, STACK_OF(X509) *certs, + X509_STORE *store, int with_self_signed, + OSSL_LIB_CTX *libctx, const char *propq) +{ + int finish_chain = store != NULL; + X509_STORE_CTX *ctx; + int flags = X509_ADD_FLAG_UP_REF; + STACK_OF(X509) *result = NULL; + + if (target == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if ((ctx = X509_STORE_CTX_new_ex(libctx, propq)) == NULL) + return NULL; + if (!X509_STORE_CTX_init(ctx, store, target, finish_chain ? certs : NULL)) + goto err; + if (!finish_chain) + X509_STORE_CTX_set0_trusted_stack(ctx, certs); + if (!ossl_x509_add_cert_new(&ctx->chain, target, X509_ADD_FLAG_UP_REF)) { + ctx->error = X509_V_ERR_OUT_OF_MEM; + goto err; } + ctx->num_untrusted = 1; + + if (!build_chain(ctx) && finish_chain) + goto err; + + /* result list to store the up_ref'ed certificates */ + if (sk_X509_num(ctx->chain) > 1 && !with_self_signed) + flags |= X509_ADD_FLAG_NO_SS; + if (!ossl_x509_add_certs_new(&result, ctx->chain, flags)) { + sk_X509_free(result); + result = NULL; + } + + err: + X509_STORE_CTX_free(ctx); + return result; } +/* + * note that there's a corresponding minbits_table in ssl/ssl_cert.c + * in ssl_get_security_level_bits that's used for selection of DH parameters + */ static const int minbits_table[] = { 80, 112, 128, 192, 256 }; static const int NUM_AUTH_LEVELS = OSSL_NELEM(minbits_table); -/* - * Check whether the public key of ``cert`` meets the security level of - * ``ctx``. - * +/*- + * Check whether the public key of `cert` meets the security level of `ctx`. * Returns 1 on success, 0 otherwise. */ static int check_key_level(X509_STORE_CTX *ctx, X509 *cert) @@ -3337,10 +3398,10 @@ static int check_key_level(X509_STORE_CTX *ctx, X509 *cert) if (level > NUM_AUTH_LEVELS) level = NUM_AUTH_LEVELS; - return EVP_PKEY_security_bits(pkey) >= minbits_table[level - 1]; + return EVP_PKEY_get_security_bits(pkey) >= minbits_table[level - 1]; } -/* +/*- * Check whether the public key of ``cert`` does not use explicit params * for an elliptic curve. * @@ -3348,25 +3409,25 @@ static int check_key_level(X509_STORE_CTX *ctx, X509 *cert) */ static int check_curve(X509 *cert) { -#ifndef OPENSSL_NO_EC EVP_PKEY *pkey = X509_get0_pubkey(cert); /* Unsupported or malformed key */ if (pkey == NULL) return -1; - if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) { - int ret; + if (EVP_PKEY_get_id(pkey) == EVP_PKEY_EC) { + int ret, val; - ret = EC_KEY_decoded_from_explicit_params(EVP_PKEY_get0_EC_KEY(pkey)); - return ret < 0 ? ret : !ret; + ret = EVP_PKEY_get_int_param(pkey, + OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, + &val); + return ret == 1 ? !val : -1; } -#endif return 1; } -/* +/*- * Check whether the signature digest algorithm of ``cert`` meets the security * level of ``ctx``. Should not be checked for trust anchors (whether * self-signed or otherwise). diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c index 535f169a29e7..998ce8ac1ba1 100644 --- a/crypto/x509/x509_vpm.c +++ b/crypto/x509/x509_vpm.c @@ -1,7 +1,7 @@ /* - * Copyright 2004-2021 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2004-2023 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -44,7 +44,8 @@ static int int_x509_param_set_hosts(X509_VERIFY_PARAM *vpm, int mode, */ if (namelen == 0 || name == NULL) namelen = name ? strlen(name) : 0; - else if (name && memchr(name, '\0', namelen > 1 ? namelen - 1 : namelen)) + else if (name != NULL + && memchr(name, '\0', namelen > 1 ? namelen - 1 : namelen) != NULL) return 0; if (namelen > 0 && name[namelen - 1] == '\0') --namelen; @@ -78,14 +79,13 @@ static int int_x509_param_set_hosts(X509_VERIFY_PARAM *vpm, int mode, return 1; } - X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void) { X509_VERIFY_PARAM *param; param = OPENSSL_zalloc(sizeof(*param)); if (param == NULL) { - X509err(X509_F_X509_VERIFY_PARAM_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } param->trust = X509_TRUST_DEFAULT; @@ -142,39 +142,32 @@ void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param) /* Macro to test if a field should be copied from src to dest */ #define test_x509_verify_param_copy(field, def) \ - (to_overwrite || \ - ((src->field != def) && (to_default || (dest->field == def)))) + (to_overwrite || (src->field != def && (to_default || dest->field == def))) /* Macro to test and copy a field if necessary */ #define x509_verify_param_copy(field, def) \ - if (test_x509_verify_param_copy(field, def)) \ - dest->field = src->field + if (test_x509_verify_param_copy(field, def)) \ + dest->field = src->field; int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, const X509_VERIFY_PARAM *src) { unsigned long inh_flags; int to_default, to_overwrite; - if (!src) + + if (src == NULL) return 1; inh_flags = dest->inh_flags | src->inh_flags; - if (inh_flags & X509_VP_FLAG_ONCE) + if ((inh_flags & X509_VP_FLAG_ONCE) != 0) dest->inh_flags = 0; - if (inh_flags & X509_VP_FLAG_LOCKED) + if ((inh_flags & X509_VP_FLAG_LOCKED) != 0) return 1; - if (inh_flags & X509_VP_FLAG_DEFAULT) - to_default = 1; - else - to_default = 0; - - if (inh_flags & X509_VP_FLAG_OVERWRITE) - to_overwrite = 1; - else - to_overwrite = 0; + to_default = (inh_flags & X509_VP_FLAG_DEFAULT) != 0; + to_overwrite = (inh_flags & X509_VP_FLAG_OVERWRITE) != 0; x509_verify_param_copy(purpose, 0); x509_verify_param_copy(trust, X509_TRUST_DEFAULT); @@ -183,13 +176,13 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, /* If overwrite or check time not set, copy across */ - if (to_overwrite || !(dest->flags & X509_V_FLAG_USE_CHECK_TIME)) { + if (to_overwrite || (dest->flags & X509_V_FLAG_USE_CHECK_TIME) == 0) { dest->check_time = src->check_time; dest->flags &= ~X509_V_FLAG_USE_CHECK_TIME; /* Don't need to copy flag: that is done below */ } - if (inh_flags & X509_VP_FLAG_RESET_FLAGS) + if ((inh_flags & X509_VP_FLAG_RESET_FLAGS) != 0) dest->flags = 0; dest->flags |= src->flags; @@ -204,7 +197,7 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, if (test_x509_verify_param_copy(hosts, NULL)) { sk_OPENSSL_STRING_pop_free(dest->hosts, str_free); dest->hosts = NULL; - if (src->hosts) { + if (src->hosts != NULL) { dest->hosts = sk_OPENSSL_STRING_deep_copy(src->hosts, str_copy, str_free); if (dest->hosts == NULL) @@ -228,8 +221,14 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, const X509_VERIFY_PARAM *from) { - unsigned long save_flags = to->inh_flags; + unsigned long save_flags; int ret; + + if (to == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + save_flags = to->inh_flags; to->inh_flags |= X509_VP_FLAG_DEFAULT; ret = X509_VERIFY_PARAM_inherit(to, from); to->inh_flags = save_flags; @@ -239,14 +238,17 @@ int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, static int int_x509_param_set1(char **pdest, size_t *pdestlen, const char *src, size_t srclen) { - void *tmp; - if (src) { + char *tmp; + + if (src != NULL) { if (srclen == 0) srclen = strlen(src); - tmp = OPENSSL_memdup(src, srclen); + tmp = OPENSSL_malloc(srclen + 1); if (tmp == NULL) return 0; + memcpy(tmp, src, srclen); + tmp[srclen] = '\0'; /* enforce NUL termination */ } else { tmp = NULL; srclen = 0; @@ -262,15 +264,13 @@ int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name) { OPENSSL_free(param->name); param->name = OPENSSL_strdup(name); - if (param->name) - return 1; - return 0; + return param->name != NULL; } int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags) { param->flags |= flags; - if (flags & X509_V_FLAG_POLICY_MASK) + if ((flags & X509_V_FLAG_POLICY_MASK) != 0) param->flags |= X509_V_FLAG_POLICY_CHECK; return 1; } @@ -282,7 +282,7 @@ int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, return 1; } -unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param) +unsigned long X509_VERIFY_PARAM_get_flags(const X509_VERIFY_PARAM *param) { return param->flags; } @@ -332,12 +332,13 @@ void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t) int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, ASN1_OBJECT *policy) { - if (!param->policies) { + if (param->policies == NULL) { param->policies = sk_ASN1_OBJECT_new_null(); - if (!param->policies) + if (param->policies == NULL) return 0; } - if (!sk_ASN1_OBJECT_push(param->policies, policy)) + + if (sk_ASN1_OBJECT_push(param->policies, policy) <= 0) return 0; return 1; } @@ -348,23 +349,25 @@ int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, int i; ASN1_OBJECT *oid, *doid; - if (!param) + if (param == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); return 0; + } sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free); - if (!policies) { + if (policies == NULL) { param->policies = NULL; return 1; } param->policies = sk_ASN1_OBJECT_new_null(); - if (!param->policies) + if (param->policies == NULL) return 0; for (i = 0; i < sk_ASN1_OBJECT_num(policies); i++) { oid = sk_ASN1_OBJECT_value(policies, i); doid = OBJ_dup(oid); - if (!doid) + if (doid == NULL) return 0; if (!sk_ASN1_OBJECT_push(param->policies, doid)) { ASN1_OBJECT_free(doid); @@ -375,6 +378,11 @@ int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, return 1; } +char *X509_VERIFY_PARAM_get0_host(X509_VERIFY_PARAM *param, int idx) +{ + return sk_OPENSSL_STRING_value(param->hosts, idx); +} + int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, const char *name, size_t namelen) { @@ -398,7 +406,7 @@ unsigned int X509_VERIFY_PARAM_get_hostflags(const X509_VERIFY_PARAM *param) return param->hostflags; } -char *X509_VERIFY_PARAM_get0_peername(X509_VERIFY_PARAM *param) +char *X509_VERIFY_PARAM_get0_peername(const X509_VERIFY_PARAM *param) { return param->peername; } @@ -417,10 +425,15 @@ void X509_VERIFY_PARAM_move_peername(X509_VERIFY_PARAM *to, OPENSSL_free(to->peername); to->peername = peername; } - if (from) + if (from != NULL) from->peername = NULL; } +char *X509_VERIFY_PARAM_get0_email(X509_VERIFY_PARAM *param) +{ + return param->email; +} + int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param, const char *email, size_t emaillen) { @@ -428,11 +441,33 @@ int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param, email, emaillen); } +static unsigned char +*int_X509_VERIFY_PARAM_get0_ip(X509_VERIFY_PARAM *param, size_t *plen) +{ + if (param == NULL || param->ip == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if (plen != NULL) + *plen = param->iplen; + return param->ip; +} + +char *X509_VERIFY_PARAM_get1_ip_asc(X509_VERIFY_PARAM *param) +{ + size_t iplen; + unsigned char *ip = int_X509_VERIFY_PARAM_get0_ip(param, &iplen); + + return ip == NULL ? NULL : ossl_ipaddr_to_asc(ip, iplen); +} + int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, const unsigned char *ip, size_t iplen) { - if (iplen != 0 && iplen != 4 && iplen != 16) + if (iplen != 0 && iplen != 4 && iplen != 16) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_INVALID_ARGUMENT); return 0; + } return int_x509_param_set1((char **)¶m->ip, ¶m->iplen, (char *)ip, iplen); } @@ -440,9 +475,8 @@ int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc) { unsigned char ipout[16]; - size_t iplen; + size_t iplen = (size_t)ossl_a2i_ipadd(ipout, ipasc); - iplen = (size_t)a2i_ipadd(ipout, ipasc); if (iplen == 0) return 0; return X509_VERIFY_PARAM_set1_ip(param, ipout, iplen); @@ -474,8 +508,8 @@ const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param) static const X509_VERIFY_PARAM default_table[] = { { "default", /* X509 default parameters */ - 0, /* Check time */ - 0, /* internal flags */ + 0, /* check time to use */ + 0, /* inheritance flags */ X509_V_FLAG_TRUSTED_FIRST, /* flags */ 0, /* purpose */ 0, /* trust */ @@ -485,8 +519,8 @@ static const X509_VERIFY_PARAM default_table[] = { vpm_empty_id}, { "pkcs7", /* S/MIME sign parameters */ - 0, /* Check time */ - 0, /* internal flags */ + 0, /* check time to use */ + 0, /* inheritance flags */ 0, /* flags */ X509_PURPOSE_SMIME_SIGN, /* purpose */ X509_TRUST_EMAIL, /* trust */ @@ -496,8 +530,8 @@ static const X509_VERIFY_PARAM default_table[] = { vpm_empty_id}, { "smime_sign", /* S/MIME sign parameters */ - 0, /* Check time */ - 0, /* internal flags */ + 0, /* check time to use */ + 0, /* inheritance flags */ 0, /* flags */ X509_PURPOSE_SMIME_SIGN, /* purpose */ X509_TRUST_EMAIL, /* trust */ @@ -507,8 +541,8 @@ static const X509_VERIFY_PARAM default_table[] = { vpm_empty_id}, { "ssl_client", /* SSL/TLS client parameters */ - 0, /* Check time */ - 0, /* internal flags */ + 0, /* check time to use */ + 0, /* inheritance flags */ 0, /* flags */ X509_PURPOSE_SSL_CLIENT, /* purpose */ X509_TRUST_SSL_CLIENT, /* trust */ @@ -518,8 +552,8 @@ static const X509_VERIFY_PARAM default_table[] = { vpm_empty_id}, { "ssl_server", /* SSL/TLS server parameters */ - 0, /* Check time */ - 0, /* internal flags */ + 0, /* check time to use */ + 0, /* inheritance flags */ 0, /* flags */ X509_PURPOSE_SSL_SERVER, /* purpose */ X509_TRUST_SSL_SERVER, /* trust */ @@ -549,6 +583,7 @@ int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param) { int idx; X509_VERIFY_PARAM *ptmp; + if (param_table == NULL) { param_table = sk_X509_VERIFY_PARAM_new(param_cmp); if (param_table == NULL) @@ -560,7 +595,8 @@ int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param) X509_VERIFY_PARAM_free(ptmp); } } - if (!sk_X509_VERIFY_PARAM_push(param_table, param)) + + if (sk_X509_VERIFY_PARAM_push(param_table, param) <= 0) return 0; return 1; } @@ -568,7 +604,8 @@ int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param) int X509_VERIFY_PARAM_get_count(void) { int num = OSSL_NELEM(default_table); - if (param_table) + + if (param_table != NULL) num += sk_X509_VERIFY_PARAM_num(param_table); return num; } @@ -576,6 +613,7 @@ int X509_VERIFY_PARAM_get_count(void) const X509_VERIFY_PARAM *X509_VERIFY_PARAM_get0(int id) { int num = OSSL_NELEM(default_table); + if (id < num) return default_table + id; return sk_X509_VERIFY_PARAM_value(param_table, id - num); diff --git a/crypto/x509/x509cset.c b/crypto/x509/x509cset.c index 6c08509138d5..2746b9892506 100644 --- a/crypto/x509/x509cset.c +++ b/crypto/x509/x509cset.c @@ -1,7 +1,7 @@ /* - * Copyright 2001-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -27,7 +27,7 @@ int X509_CRL_set_version(X509_CRL *x, long version) return ASN1_INTEGER_set(x->crl.version, version); } -int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name) +int X509_CRL_set_issuer_name(X509_CRL *x, const X509_NAME *name) { if (x == NULL) return 0; @@ -38,14 +38,14 @@ int X509_CRL_set1_lastUpdate(X509_CRL *x, const ASN1_TIME *tm) { if (x == NULL) return 0; - return x509_set1_time(&x->crl.lastUpdate, tm); + return ossl_x509_set1_time(&x->crl.lastUpdate, tm); } int X509_CRL_set1_nextUpdate(X509_CRL *x, const ASN1_TIME *tm) { if (x == NULL) return 0; - return x509_set1_time(&x->crl.nextUpdate, tm); + return ossl_x509_set1_time(&x->crl.nextUpdate, tm); } int X509_CRL_sort(X509_CRL *c) @@ -91,7 +91,7 @@ const ASN1_TIME *X509_CRL_get0_nextUpdate(const X509_CRL *crl) return crl->crl.nextUpdate; } -#if OPENSSL_API_COMPAT < 0x10100000L +#ifndef OPENSSL_NO_DEPRECATED_1_1_0 ASN1_TIME *X509_CRL_get_lastUpdate(X509_CRL *crl) { return crl->crl.lastUpdate; diff --git a/crypto/x509/x509name.c b/crypto/x509/x509name.c index c86d8e7914f1..9ae0dc5de48f 100644 --- a/crypto/x509/x509name.c +++ b/crypto/x509/x509name.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -16,7 +16,8 @@ #include <openssl/x509.h> #include "crypto/x509.h" -int X509_NAME_get_text_by_NID(X509_NAME *name, int nid, char *buf, int len) +int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid, + char *buf, int len) { ASN1_OBJECT *obj; @@ -26,7 +27,7 @@ int X509_NAME_get_text_by_NID(X509_NAME *name, int nid, char *buf, int len) return X509_NAME_get_text_by_OBJ(name, obj, buf, len); } -int X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, +int X509_NAME_get_text_by_OBJ(const X509_NAME *name, const ASN1_OBJECT *obj, char *buf, int len) { int i; @@ -48,12 +49,15 @@ int X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int X509_NAME_entry_count(const X509_NAME *name) { + int ret; + if (name == NULL) return 0; - return sk_X509_NAME_ENTRY_num(name->entries); + ret = sk_X509_NAME_ENTRY_num(name->entries); + return ret > 0 ? ret : 0; } -int X509_NAME_get_index_by_NID(X509_NAME *name, int nid, int lastpos) +int X509_NAME_get_index_by_NID(const X509_NAME *name, int nid, int lastpos) { ASN1_OBJECT *obj; @@ -64,7 +68,8 @@ int X509_NAME_get_index_by_NID(X509_NAME *name, int nid, int lastpos) } /* NOTE: you should be passing -1, not 0 as lastpos */ -int X509_NAME_get_index_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int lastpos) +int X509_NAME_get_index_by_OBJ(const X509_NAME *name, const ASN1_OBJECT *obj, + int lastpos) { int n; X509_NAME_ENTRY *ne; @@ -216,15 +221,11 @@ int X509_NAME_add_entry(X509_NAME *name, const X509_NAME_ENTRY *ne, int loc, set = sk_X509_NAME_ENTRY_value(sk, loc)->set; } - /* - * X509_NAME_ENTRY_dup is ASN1 generated code, that can't be easily - * const'ified; harmless cast since dup() don't modify its input. - */ - if ((new_name = X509_NAME_ENTRY_dup((X509_NAME_ENTRY *)ne)) == NULL) + if ((new_name = X509_NAME_ENTRY_dup(ne)) == NULL) goto err; new_name->set = set; if (!sk_X509_NAME_ENTRY_insert(sk, new_name, loc)) { - X509err(X509_F_X509_NAME_ADD_ENTRY, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } if (inc) { @@ -248,9 +249,8 @@ X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(X509_NAME_ENTRY **ne, obj = OBJ_txt2obj(field, 0); if (obj == NULL) { - X509err(X509_F_X509_NAME_ENTRY_CREATE_BY_TXT, - X509_R_INVALID_FIELD_NAME); - ERR_add_error_data(2, "name=", field); + ERR_raise_data(ERR_LIB_X509, X509_R_INVALID_FIELD_NAME, + "name=%s", field); return NULL; } nentry = X509_NAME_ENTRY_create_by_OBJ(ne, obj, type, bytes, len); @@ -268,7 +268,7 @@ X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_NID(X509_NAME_ENTRY **ne, int nid, obj = OBJ_nid2obj(nid); if (obj == NULL) { - X509err(X509_F_X509_NAME_ENTRY_CREATE_BY_NID, X509_R_UNKNOWN_NID); + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_NID); return NULL; } nentry = X509_NAME_ENTRY_create_by_OBJ(ne, obj, type, bytes, len); @@ -306,8 +306,7 @@ X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(X509_NAME_ENTRY **ne, int X509_NAME_ENTRY_set_object(X509_NAME_ENTRY *ne, const ASN1_OBJECT *obj) { if ((ne == NULL) || (obj == NULL)) { - X509err(X509_F_X509_NAME_ENTRY_SET_OBJECT, - ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); return 0; } ASN1_OBJECT_free(ne->object); diff --git a/crypto/x509/x509rset.c b/crypto/x509/x509rset.c index 9da3f2ee27df..344993d4c78c 100644 --- a/crypto/x509/x509rset.c +++ b/crypto/x509/x509rset.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -23,7 +23,7 @@ int X509_REQ_set_version(X509_REQ *x, long version) return ASN1_INTEGER_set(x->req_info.version, version); } -int X509_REQ_set_subject_name(X509_REQ *x, X509_NAME *name) +int X509_REQ_set_subject_name(X509_REQ *x, const X509_NAME *name) { if (x == NULL) return 0; diff --git a/crypto/x509/x509spki.c b/crypto/x509/x509spki.c index fd8162af6df2..1d66697db00d 100644 --- a/crypto/x509/x509spki.c +++ b/crypto/x509/x509spki.c @@ -1,7 +1,7 @@ /* - * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1999-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -36,12 +36,12 @@ NETSCAPE_SPKI *NETSCAPE_SPKI_b64_decode(const char *str, int len) if (len <= 0) len = strlen(str); if ((spki_der = OPENSSL_malloc(len + 1)) == NULL) { - X509err(X509_F_NETSCAPE_SPKI_B64_DECODE, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } spki_len = EVP_DecodeBlock(spki_der, (const unsigned char *)str, len); if (spki_len < 0) { - X509err(X509_F_NETSCAPE_SPKI_B64_DECODE, X509_R_BASE64_DECODE_ERROR); + ERR_raise(ERR_LIB_X509, X509_R_BASE64_DECODE_ERROR); OPENSSL_free(spki_der); return NULL; } @@ -58,11 +58,14 @@ char *NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *spki) unsigned char *der_spki, *p; char *b64_str; int der_len; + der_len = i2d_NETSCAPE_SPKI(spki, NULL); + if (der_len <= 0) + return NULL; der_spki = OPENSSL_malloc(der_len); b64_str = OPENSSL_malloc(der_len * 2); if (der_spki == NULL || b64_str == NULL) { - X509err(X509_F_NETSCAPE_SPKI_B64_ENCODE, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); OPENSSL_free(der_spki); OPENSSL_free(b64_str); return NULL; diff --git a/crypto/x509/x509type.c b/crypto/x509/x509type.c index 0e33b424be51..79fd5e7db609 100644 --- a/crypto/x509/x509type.c +++ b/crypto/x509/x509type.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -29,7 +29,7 @@ int X509_certificate_type(const X509 *x, const EVP_PKEY *pkey) if (pk == NULL) return 0; - switch (EVP_PKEY_id(pk)) { + switch (EVP_PKEY_get_id(pk)) { case EVP_PKEY_RSA: ret = EVP_PK_RSA | EVP_PKT_SIGN; /* if (!sign only extension) */ diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index fcf6b5ba3780..158e11a8649c 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -1,46 +1,69 @@ /* * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 */ +/* + * Low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + #include <stdio.h> #include "internal/cryptlib.h" #include <openssl/buffer.h> #include <openssl/asn1.h> #include <openssl/evp.h> #include <openssl/x509.h> -#include "crypto/x509.h" -#include <openssl/ocsp.h> +#include <openssl/http.h> #include <openssl/rsa.h> #include <openssl/dsa.h> #include <openssl/x509v3.h> +#include "internal/asn1.h" +#include "crypto/pkcs7.h" +#include "crypto/x509.h" +#include "crypto/rsa.h" int X509_verify(X509 *a, EVP_PKEY *r) { - if (X509_ALGOR_cmp(&a->sig_alg, &a->cert_info.signature)) + if (X509_ALGOR_cmp(&a->sig_alg, &a->cert_info.signature) != 0) return 0; - return (ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF), &a->sig_alg, - &a->signature, &a->cert_info, r)); + + return ASN1_item_verify_ex(ASN1_ITEM_rptr(X509_CINF), &a->sig_alg, + &a->signature, &a->cert_info, + a->distinguishing_id, r, a->libctx, a->propq); +} + +int X509_REQ_verify_ex(X509_REQ *a, EVP_PKEY *r, OSSL_LIB_CTX *libctx, + const char *propq) +{ + return ASN1_item_verify_ex(ASN1_ITEM_rptr(X509_REQ_INFO), &a->sig_alg, + a->signature, &a->req_info, a->distinguishing_id, + r, libctx, propq); } int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r) { - return (ASN1_item_verify(ASN1_ITEM_rptr(X509_REQ_INFO), - &a->sig_alg, a->signature, &a->req_info, r)); + return X509_REQ_verify_ex(a, r, NULL, NULL); } int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *a, EVP_PKEY *r) { - return (ASN1_item_verify(ASN1_ITEM_rptr(NETSCAPE_SPKAC), - &a->sig_algor, a->signature, a->spkac, r)); + return ASN1_item_verify(ASN1_ITEM_rptr(NETSCAPE_SPKAC), + &a->sig_algor, a->signature, a->spkac, r); } int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md) { + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + /* * Setting the modified flag before signing it. This makes the cached * encoding to be ignored, so even if the certificate fields have changed, @@ -49,36 +72,63 @@ int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md) * which exist below are the same. */ x->cert_info.enc.modified = 1; - return (ASN1_item_sign(ASN1_ITEM_rptr(X509_CINF), &x->cert_info.signature, - &x->sig_alg, &x->signature, &x->cert_info, pkey, - md)); + return ASN1_item_sign_ex(ASN1_ITEM_rptr(X509_CINF), &x->cert_info.signature, + &x->sig_alg, &x->signature, &x->cert_info, NULL, + pkey, md, x->libctx, x->propq); } int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx) { + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } x->cert_info.enc.modified = 1; return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CINF), &x->cert_info.signature, &x->sig_alg, &x->signature, &x->cert_info, ctx); } -#ifndef OPENSSL_NO_OCSP -int X509_http_nbio(OCSP_REQ_CTX *rctx, X509 **pcert) +static ASN1_VALUE *simple_get_asn1(const char *url, BIO *bio, BIO *rbio, + int timeout, const ASN1_ITEM *it) { - return OCSP_REQ_CTX_nbio_d2i(rctx, - (ASN1_VALUE **)pcert, ASN1_ITEM_rptr(X509)); + size_t max_resp_len = (it == ASN1_ITEM_rptr(X509_CRL)) ? + OSSL_HTTP_DEFAULT_MAX_CRL_LEN : OSSL_HTTP_DEFAULT_MAX_RESP_LEN; + BIO *mem = OSSL_HTTP_get(url, NULL /* proxy */, NULL /* no_proxy */, + bio, rbio, NULL /* cb */, NULL /* arg */, + 1024 /* buf_size */, NULL /* headers */, + NULL /* expected_ct */, 1 /* expect_asn1 */, + max_resp_len, timeout); + ASN1_VALUE *res = ASN1_item_d2i_bio(it, mem, NULL); + + BIO_free(mem); + return res; +} + +X509 *X509_load_http(const char *url, BIO *bio, BIO *rbio, int timeout) +{ + return (X509 *)simple_get_asn1(url, bio, rbio, timeout, + ASN1_ITEM_rptr(X509)); } -#endif int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md) { + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } x->req_info.enc.modified = 1; - return (ASN1_item_sign(ASN1_ITEM_rptr(X509_REQ_INFO), &x->sig_alg, NULL, - x->signature, &x->req_info, pkey, md)); + return ASN1_item_sign_ex(ASN1_ITEM_rptr(X509_REQ_INFO), &x->sig_alg, NULL, + x->signature, &x->req_info, NULL, + pkey, md, x->libctx, x->propq); } int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx) { + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } x->req_info.enc.modified = 1; return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_REQ_INFO), &x->sig_alg, NULL, x->signature, &x->req_info, @@ -87,32 +137,39 @@ int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx) int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md) { + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } x->crl.enc.modified = 1; - return (ASN1_item_sign(ASN1_ITEM_rptr(X509_CRL_INFO), &x->crl.sig_alg, - &x->sig_alg, &x->signature, &x->crl, pkey, md)); + return ASN1_item_sign_ex(ASN1_ITEM_rptr(X509_CRL_INFO), &x->crl.sig_alg, + &x->sig_alg, &x->signature, &x->crl, NULL, + pkey, md, x->libctx, x->propq); } int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx) { + if (x == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } x->crl.enc.modified = 1; return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CRL_INFO), &x->crl.sig_alg, &x->sig_alg, &x->signature, &x->crl, ctx); } -#ifndef OPENSSL_NO_OCSP -int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl) +X509_CRL *X509_CRL_load_http(const char *url, BIO *bio, BIO *rbio, int timeout) { - return OCSP_REQ_CTX_nbio_d2i(rctx, - (ASN1_VALUE **)pcrl, - ASN1_ITEM_rptr(X509_CRL)); + return (X509_CRL *)simple_get_asn1(url, bio, rbio, timeout, + ASN1_ITEM_rptr(X509_CRL)); } -#endif int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md) { - return (ASN1_item_sign(ASN1_ITEM_rptr(NETSCAPE_SPKAC), &x->sig_algor, NULL, - x->signature, x->spkac, pkey, md)); + return + ASN1_item_sign_ex(ASN1_ITEM_rptr(NETSCAPE_SPKAC), &x->sig_algor, NULL, + x->signature, x->spkac, NULL, pkey, md, NULL, NULL); } #ifndef OPENSSL_NO_STDIO @@ -121,7 +178,7 @@ X509 *d2i_X509_fp(FILE *fp, X509 **x509) return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509), fp, x509); } -int i2d_X509_fp(FILE *fp, X509 *x509) +int i2d_X509_fp(FILE *fp, const X509 *x509) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509), fp, x509); } @@ -132,7 +189,7 @@ X509 *d2i_X509_bio(BIO *bp, X509 **x509) return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509), bp, x509); } -int i2d_X509_bio(BIO *bp, X509 *x509) +int i2d_X509_bio(BIO *bp, const X509 *x509) { return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509), bp, x509); } @@ -143,7 +200,7 @@ X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl) return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl); } -int i2d_X509_CRL_fp(FILE *fp, X509_CRL *crl) +int i2d_X509_CRL_fp(FILE *fp, const X509_CRL *crl) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl); } @@ -154,7 +211,7 @@ X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl) return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl); } -int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl) +int i2d_X509_CRL_bio(BIO *bp, const X509_CRL *crl) { return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl); } @@ -162,10 +219,22 @@ int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl) #ifndef OPENSSL_NO_STDIO PKCS7 *d2i_PKCS7_fp(FILE *fp, PKCS7 **p7) { - return ASN1_item_d2i_fp(ASN1_ITEM_rptr(PKCS7), fp, p7); + PKCS7 *ret; + OSSL_LIB_CTX *libctx = NULL; + const char *propq = NULL; + + if (p7 != NULL && *p7 != NULL) { + libctx = (*p7)->ctx.libctx; + propq = (*p7)->ctx.propq; + } + + ret = ASN1_item_d2i_fp_ex(ASN1_ITEM_rptr(PKCS7), fp, p7, libctx, propq); + if (ret != NULL) + ossl_pkcs7_resolve_libctx(ret); + return ret; } -int i2d_PKCS7_fp(FILE *fp, PKCS7 *p7) +int i2d_PKCS7_fp(FILE *fp, const PKCS7 *p7) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(PKCS7), fp, p7); } @@ -173,10 +242,22 @@ int i2d_PKCS7_fp(FILE *fp, PKCS7 *p7) PKCS7 *d2i_PKCS7_bio(BIO *bp, PKCS7 **p7) { - return ASN1_item_d2i_bio(ASN1_ITEM_rptr(PKCS7), bp, p7); + PKCS7 *ret; + OSSL_LIB_CTX *libctx = NULL; + const char *propq = NULL; + + if (p7 != NULL && *p7 != NULL) { + libctx = (*p7)->ctx.libctx; + propq = (*p7)->ctx.propq; + } + + ret = ASN1_item_d2i_bio_ex(ASN1_ITEM_rptr(PKCS7), bp, p7, libctx, propq); + if (ret != NULL) + ossl_pkcs7_resolve_libctx(ret); + return ret; } -int i2d_PKCS7_bio(BIO *bp, PKCS7 *p7) +int i2d_PKCS7_bio(BIO *bp, const PKCS7 *p7) { return ASN1_item_i2d_bio(ASN1_ITEM_rptr(PKCS7), bp, p7); } @@ -187,7 +268,7 @@ X509_REQ *d2i_X509_REQ_fp(FILE *fp, X509_REQ **req) return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_REQ), fp, req); } -int i2d_X509_REQ_fp(FILE *fp, X509_REQ *req) +int i2d_X509_REQ_fp(FILE *fp, const X509_REQ *req) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_REQ), fp, req); } @@ -195,23 +276,29 @@ int i2d_X509_REQ_fp(FILE *fp, X509_REQ *req) X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req) { - return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_REQ), bp, req); + OSSL_LIB_CTX *libctx = NULL; + const char *propq = NULL; + + if (req != NULL && *req != NULL) { + libctx = (*req)->libctx; + propq = (*req)->propq; + } + + return ASN1_item_d2i_bio_ex(ASN1_ITEM_rptr(X509_REQ), bp, req, libctx, propq); } -int i2d_X509_REQ_bio(BIO *bp, X509_REQ *req) +int i2d_X509_REQ_bio(BIO *bp, const X509_REQ *req) { return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_REQ), bp, req); } -#ifndef OPENSSL_NO_RSA - -# ifndef OPENSSL_NO_STDIO +#ifndef OPENSSL_NO_STDIO RSA *d2i_RSAPrivateKey_fp(FILE *fp, RSA **rsa) { return ASN1_item_d2i_fp(ASN1_ITEM_rptr(RSAPrivateKey), fp, rsa); } -int i2d_RSAPrivateKey_fp(FILE *fp, RSA *rsa) +int i2d_RSAPrivateKey_fp(FILE *fp, const RSA *rsa) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(RSAPrivateKey), fp, rsa); } @@ -228,23 +315,23 @@ RSA *d2i_RSA_PUBKEY_fp(FILE *fp, RSA **rsa) (void **)rsa); } -int i2d_RSAPublicKey_fp(FILE *fp, RSA *rsa) +int i2d_RSAPublicKey_fp(FILE *fp, const RSA *rsa) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(RSAPublicKey), fp, rsa); } -int i2d_RSA_PUBKEY_fp(FILE *fp, RSA *rsa) +int i2d_RSA_PUBKEY_fp(FILE *fp, const RSA *rsa) { return ASN1_i2d_fp((I2D_OF(void))i2d_RSA_PUBKEY, fp, rsa); } -# endif +#endif RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa) { return ASN1_item_d2i_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa); } -int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa) +int i2d_RSAPrivateKey_bio(BIO *bp, const RSA *rsa) { return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa); } @@ -259,16 +346,15 @@ RSA *d2i_RSA_PUBKEY_bio(BIO *bp, RSA **rsa) return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSA_PUBKEY, bp, rsa); } -int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa) +int i2d_RSAPublicKey_bio(BIO *bp, const RSA *rsa) { return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPublicKey), bp, rsa); } -int i2d_RSA_PUBKEY_bio(BIO *bp, RSA *rsa) +int i2d_RSA_PUBKEY_bio(BIO *bp, const RSA *rsa) { return ASN1_i2d_bio_of(RSA, i2d_RSA_PUBKEY, bp, rsa); } -#endif #ifndef OPENSSL_NO_DSA # ifndef OPENSSL_NO_STDIO @@ -277,9 +363,9 @@ DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa) return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSAPrivateKey, fp, dsa); } -int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa) +int i2d_DSAPrivateKey_fp(FILE *fp, const DSA *dsa) { - return ASN1_i2d_fp_of_const(DSA, i2d_DSAPrivateKey, fp, dsa); + return ASN1_i2d_fp_of(DSA, i2d_DSAPrivateKey, fp, dsa); } DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa) @@ -287,7 +373,7 @@ DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa) return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSA_PUBKEY, fp, dsa); } -int i2d_DSA_PUBKEY_fp(FILE *fp, DSA *dsa) +int i2d_DSA_PUBKEY_fp(FILE *fp, const DSA *dsa) { return ASN1_i2d_fp_of(DSA, i2d_DSA_PUBKEY, fp, dsa); } @@ -298,9 +384,9 @@ DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa) return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSAPrivateKey, bp, dsa); } -int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa) +int i2d_DSAPrivateKey_bio(BIO *bp, const DSA *dsa) { - return ASN1_i2d_bio_of_const(DSA, i2d_DSAPrivateKey, bp, dsa); + return ASN1_i2d_bio_of(DSA, i2d_DSAPrivateKey, bp, dsa); } DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa) @@ -308,7 +394,7 @@ DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa) return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSA_PUBKEY, bp, dsa); } -int i2d_DSA_PUBKEY_bio(BIO *bp, DSA *dsa) +int i2d_DSA_PUBKEY_bio(BIO *bp, const DSA *dsa) { return ASN1_i2d_bio_of(DSA, i2d_DSA_PUBKEY, bp, dsa); } @@ -322,7 +408,7 @@ EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey) return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, fp, eckey); } -int i2d_EC_PUBKEY_fp(FILE *fp, EC_KEY *eckey) +int i2d_EC_PUBKEY_fp(FILE *fp, const EC_KEY *eckey) { return ASN1_i2d_fp_of(EC_KEY, i2d_EC_PUBKEY, fp, eckey); } @@ -332,7 +418,7 @@ EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey) return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, fp, eckey); } -int i2d_ECPrivateKey_fp(FILE *fp, EC_KEY *eckey) +int i2d_ECPrivateKey_fp(FILE *fp, const EC_KEY *eckey) { return ASN1_i2d_fp_of(EC_KEY, i2d_ECPrivateKey, fp, eckey); } @@ -342,7 +428,7 @@ EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey) return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, bp, eckey); } -int i2d_EC_PUBKEY_bio(BIO *bp, EC_KEY *ecdsa) +int i2d_EC_PUBKEY_bio(BIO *bp, const EC_KEY *ecdsa) { return ASN1_i2d_bio_of(EC_KEY, i2d_EC_PUBKEY, bp, ecdsa); } @@ -352,7 +438,7 @@ EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey) return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, bp, eckey); } -int i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey) +int i2d_ECPrivateKey_bio(BIO *bp, const EC_KEY *eckey) { return ASN1_i2d_bio_of(EC_KEY, i2d_ECPrivateKey, bp, eckey); } @@ -361,63 +447,160 @@ int i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey) int X509_pubkey_digest(const X509 *data, const EVP_MD *type, unsigned char *md, unsigned int *len) { - ASN1_BIT_STRING *key; - key = X509_get0_pubkey_bitstr(data); - if (!key) + ASN1_BIT_STRING *key = X509_get0_pubkey_bitstr(data); + + if (key == NULL) return 0; return EVP_Digest(key->data, key->length, md, len, type, NULL); } -int X509_digest(const X509 *data, const EVP_MD *type, unsigned char *md, +int X509_digest(const X509 *cert, const EVP_MD *md, unsigned char *data, unsigned int *len) { - if (type == EVP_sha1() && (data->ex_flags & EXFLAG_SET) != 0 - && (data->ex_flags & EXFLAG_NO_FINGERPRINT) == 0) { + if (EVP_MD_is_a(md, SN_sha1) && (cert->ex_flags & EXFLAG_SET) != 0 + && (cert->ex_flags & EXFLAG_NO_FINGERPRINT) == 0) { /* Asking for SHA1 and we already computed it. */ if (len != NULL) - *len = sizeof(data->sha1_hash); - memcpy(md, data->sha1_hash, sizeof(data->sha1_hash)); + *len = sizeof(cert->sha1_hash); + memcpy(data, cert->sha1_hash, sizeof(cert->sha1_hash)); return 1; } - return (ASN1_item_digest - (ASN1_ITEM_rptr(X509), type, (char *)data, md, len)); + return ossl_asn1_item_digest_ex(ASN1_ITEM_rptr(X509), md, (char *)cert, + data, len, cert->libctx, cert->propq); +} + +/* calculate cert digest using the same hash algorithm as in its signature */ +ASN1_OCTET_STRING *X509_digest_sig(const X509 *cert, + EVP_MD **md_used, int *md_is_fallback) +{ + unsigned int len; + unsigned char hash[EVP_MAX_MD_SIZE]; + int mdnid, pknid; + EVP_MD *md = NULL; + const char *md_name; + ASN1_OCTET_STRING *new; + + if (md_used != NULL) + *md_used = NULL; + if (md_is_fallback != NULL) + *md_is_fallback = 0; + + if (cert == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if (!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &mdnid, &pknid)) { + ERR_raise(ERR_LIB_X509, X509_R_UNKNOWN_SIGID_ALGS); + return NULL; + } + + if (mdnid == NID_undef) { + if (pknid == EVP_PKEY_RSA_PSS) { + RSA_PSS_PARAMS *pss = ossl_rsa_pss_decode(&cert->sig_alg); + const EVP_MD *mgf1md, *mmd = NULL; + int saltlen, trailerfield; + + if (pss == NULL + || !ossl_rsa_pss_get_param_unverified(pss, &mmd, &mgf1md, + &saltlen, + &trailerfield) + || mmd == NULL) { + RSA_PSS_PARAMS_free(pss); + ERR_raise(ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM); + return NULL; + } + RSA_PSS_PARAMS_free(pss); + /* Fetch explicitly and do not fallback */ + if ((md = EVP_MD_fetch(cert->libctx, EVP_MD_get0_name(mmd), + cert->propq)) == NULL) + /* Error code from fetch is sufficient */ + return NULL; + } else if (pknid != NID_undef) { + /* A known algorithm, but without a digest */ + switch (pknid) { + case NID_ED25519: /* Follow CMS default given in RFC8419 */ + md_name = "SHA512"; + break; + case NID_ED448: /* Follow CMS default given in RFC8419 */ + md_name = "SHAKE256"; + break; + default: /* Fall back to SHA-256 */ + md_name = "SHA256"; + break; + } + if ((md = EVP_MD_fetch(cert->libctx, md_name, + cert->propq)) == NULL) + return NULL; + if (md_is_fallback != NULL) + *md_is_fallback = 1; + } else { + /* A completely unknown algorithm */ + ERR_raise(ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM); + return NULL; + } + } else if ((md = EVP_MD_fetch(cert->libctx, OBJ_nid2sn(mdnid), + cert->propq)) == NULL + && (md = (EVP_MD *)EVP_get_digestbynid(mdnid)) == NULL) { + ERR_raise(ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM); + return NULL; + } + if (!X509_digest(cert, md, hash, &len) + || (new = ASN1_OCTET_STRING_new()) == NULL) + goto err; + if (ASN1_OCTET_STRING_set(new, hash, len)) { + if (md_used != NULL) + *md_used = md; + else + EVP_MD_free(md); + return new; + } + ASN1_OCTET_STRING_free(new); + err: + EVP_MD_free(md); + return NULL; } int X509_CRL_digest(const X509_CRL *data, const EVP_MD *type, unsigned char *md, unsigned int *len) { - if (type == EVP_sha1() && (data->flags & EXFLAG_SET) != 0 - && (data->flags & EXFLAG_INVALID) == 0) { + if (type == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (EVP_MD_is_a(type, SN_sha1) + && (data->flags & EXFLAG_SET) != 0 + && (data->flags & EXFLAG_NO_FINGERPRINT) == 0) { /* Asking for SHA1; always computed in CRL d2i. */ if (len != NULL) *len = sizeof(data->sha1_hash); memcpy(md, data->sha1_hash, sizeof(data->sha1_hash)); return 1; } - return (ASN1_item_digest - (ASN1_ITEM_rptr(X509_CRL), type, (char *)data, md, len)); + return ossl_asn1_item_digest_ex(ASN1_ITEM_rptr(X509_CRL), type, (char *)data, + md, len, data->libctx, data->propq); } int X509_REQ_digest(const X509_REQ *data, const EVP_MD *type, unsigned char *md, unsigned int *len) { - return (ASN1_item_digest - (ASN1_ITEM_rptr(X509_REQ), type, (char *)data, md, len)); + return ossl_asn1_item_digest_ex(ASN1_ITEM_rptr(X509_REQ), type, (char *)data, + md, len, data->libctx, data->propq); } int X509_NAME_digest(const X509_NAME *data, const EVP_MD *type, unsigned char *md, unsigned int *len) { - return (ASN1_item_digest - (ASN1_ITEM_rptr(X509_NAME), type, (char *)data, md, len)); + return ASN1_item_digest(ASN1_ITEM_rptr(X509_NAME), type, (char *)data, + md, len); } int PKCS7_ISSUER_AND_SERIAL_digest(PKCS7_ISSUER_AND_SERIAL *data, const EVP_MD *type, unsigned char *md, unsigned int *len) { - return (ASN1_item_digest(ASN1_ITEM_rptr(PKCS7_ISSUER_AND_SERIAL), type, - (char *)data, md, len)); + return ASN1_item_digest(ASN1_ITEM_rptr(PKCS7_ISSUER_AND_SERIAL), type, + (char *)data, md, len); } #ifndef OPENSSL_NO_STDIO @@ -426,7 +609,7 @@ X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8) return ASN1_d2i_fp_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, fp, p8); } -int i2d_PKCS8_fp(FILE *fp, X509_SIG *p8) +int i2d_PKCS8_fp(FILE *fp, const X509_SIG *p8) { return ASN1_i2d_fp_of(X509_SIG, i2d_X509_SIG, fp, p8); } @@ -437,12 +620,36 @@ X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8) return ASN1_d2i_bio_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, bp, p8); } -int i2d_PKCS8_bio(BIO *bp, X509_SIG *p8) +int i2d_PKCS8_bio(BIO *bp, const X509_SIG *p8) { return ASN1_i2d_bio_of(X509_SIG, i2d_X509_SIG, bp, p8); } #ifndef OPENSSL_NO_STDIO +X509_PUBKEY *d2i_X509_PUBKEY_fp(FILE *fp, X509_PUBKEY **xpk) +{ + return ASN1_d2i_fp_of(X509_PUBKEY, X509_PUBKEY_new, d2i_X509_PUBKEY, + fp, xpk); +} + +int i2d_X509_PUBKEY_fp(FILE *fp, const X509_PUBKEY *xpk) +{ + return ASN1_i2d_fp_of(X509_PUBKEY, i2d_X509_PUBKEY, fp, xpk); +} +#endif + +X509_PUBKEY *d2i_X509_PUBKEY_bio(BIO *bp, X509_PUBKEY **xpk) +{ + return ASN1_d2i_bio_of(X509_PUBKEY, X509_PUBKEY_new, d2i_X509_PUBKEY, + bp, xpk); +} + +int i2d_X509_PUBKEY_bio(BIO *bp, const X509_PUBKEY *xpk) +{ + return ASN1_i2d_bio_of(X509_PUBKEY, i2d_X509_PUBKEY, bp, xpk); +} + +#ifndef OPENSSL_NO_STDIO PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO **p8inf) { @@ -450,25 +657,26 @@ PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, d2i_PKCS8_PRIV_KEY_INFO, fp, p8inf); } -int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO *p8inf) +int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, const PKCS8_PRIV_KEY_INFO *p8inf) { return ASN1_i2d_fp_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, fp, p8inf); } -int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key) +int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, const EVP_PKEY *key) { PKCS8_PRIV_KEY_INFO *p8inf; int ret; + p8inf = EVP_PKEY2PKCS8(key); - if (!p8inf) + if (p8inf == NULL) return 0; ret = i2d_PKCS8_PRIV_KEY_INFO_fp(fp, p8inf); PKCS8_PRIV_KEY_INFO_free(p8inf); return ret; } -int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey) +int i2d_PrivateKey_fp(FILE *fp, const EVP_PKEY *pkey) { return ASN1_i2d_fp_of(EVP_PKEY, i2d_PrivateKey, fp, pkey); } @@ -478,7 +686,23 @@ EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a) return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, fp, a); } -int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey) +EVP_PKEY *d2i_PrivateKey_ex_fp(FILE *fp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, + const char *propq) +{ + BIO *b; + void *ret; + + if ((b = BIO_new(BIO_s_file())) == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); + return NULL; + } + BIO_set_fp(b, fp, BIO_NOCLOSE); + ret = d2i_PrivateKey_ex_bio(b, a, libctx, propq); + BIO_free(b); + return ret; +} + +int i2d_PUBKEY_fp(FILE *fp, const EVP_PKEY *pkey) { return ASN1_i2d_fp_of(EVP_PKEY, i2d_PUBKEY, fp, pkey); } @@ -497,25 +721,26 @@ PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, d2i_PKCS8_PRIV_KEY_INFO, bp, p8inf); } -int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, PKCS8_PRIV_KEY_INFO *p8inf) +int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, const PKCS8_PRIV_KEY_INFO *p8inf) { return ASN1_i2d_bio_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, bp, p8inf); } -int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key) +int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, const EVP_PKEY *key) { PKCS8_PRIV_KEY_INFO *p8inf; int ret; + p8inf = EVP_PKEY2PKCS8(key); - if (!p8inf) + if (p8inf == NULL) return 0; ret = i2d_PKCS8_PRIV_KEY_INFO_bio(bp, p8inf); PKCS8_PRIV_KEY_INFO_free(p8inf); return ret; } -int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey) +int i2d_PrivateKey_bio(BIO *bp, const EVP_PKEY *pkey) { return ASN1_i2d_bio_of(EVP_PKEY, i2d_PrivateKey, bp, pkey); } @@ -525,7 +750,26 @@ EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a) return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, bp, a); } -int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey) +EVP_PKEY *d2i_PrivateKey_ex_bio(BIO *bp, EVP_PKEY **a, OSSL_LIB_CTX *libctx, + const char *propq) +{ + BUF_MEM *b = NULL; + const unsigned char *p; + void *ret = NULL; + int len; + + len = asn1_d2i_read_bio(bp, &b); + if (len < 0) + goto err; + + p = (unsigned char *)b->data; + ret = d2i_AutoPrivateKey_ex(a, &p, len, libctx, propq); + err: + BUF_MEM_free(b); + return ret; +} + +int i2d_PUBKEY_bio(BIO *bp, const EVP_PKEY *pkey) { return ASN1_i2d_bio_of(EVP_PKEY, i2d_PUBKEY, bp, pkey); } diff --git a/crypto/x509/x_attrib.c b/crypto/x509/x_attrib.c index 7342c4f6bcb5..5c7e622d1a0b 100644 --- a/crypto/x509/x_attrib.c +++ b/crypto/x509/x_attrib.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 diff --git a/crypto/x509/x_crl.c b/crypto/x509/x_crl.c index df0041c0108c..fd98ad6926cf 100644 --- a/crypto/x509/x_crl.c +++ b/crypto/x509/x_crl.c @@ -1,7 +1,7 @@ /* * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -27,8 +27,8 @@ ASN1_SEQUENCE(X509_REVOKED) = { static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r); static int def_crl_lookup(X509_CRL *crl, - X509_REVOKED **ret, ASN1_INTEGER *serial, - X509_NAME *issuer); + X509_REVOKED **ret, const ASN1_INTEGER *serial, + const X509_NAME *issuer); static X509_CRL_METHOD int_crl_meth = { 0, @@ -151,7 +151,7 @@ static int crl_set_issuers(X509_CRL *crl) /* * The X509_CRL structure needs a bit of customisation. Cache some extensions - * and hash of the whole CRL. + * and hash of the whole CRL or set EXFLAG_NO_FINGERPRINT if this fails. */ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg) @@ -189,7 +189,7 @@ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, case ASN1_OP_D2I_POST: if (!X509_CRL_digest(crl, EVP_sha1(), crl->sha1_hash, NULL)) - crl->flags |= EXFLAG_INVALID; + crl->flags |= EXFLAG_NO_FINGERPRINT; crl->idp = X509_CRL_get_ext_d2i(crl, NID_issuing_distribution_point, &i, NULL); @@ -268,6 +268,15 @@ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, ASN1_INTEGER_free(crl->crl_number); ASN1_INTEGER_free(crl->base_crl_number); sk_GENERAL_NAMES_pop_free(crl->issuers, GENERAL_NAMES_free); + OPENSSL_free(crl->propq); + break; + case ASN1_OP_DUP_POST: + { + X509_CRL *old = exarg; + + if (!ossl_x509_crl_set0_libctx(crl, old->libctx, old->propq)) + return 0; + } break; } return 1; @@ -335,6 +344,18 @@ static int X509_REVOKED_cmp(const X509_REVOKED *const *a, (ASN1_STRING *)&(*b)->serialNumber)); } +X509_CRL *X509_CRL_new_ex(OSSL_LIB_CTX *libctx, const char *propq) +{ + X509_CRL *crl = NULL; + + crl = (X509_CRL *)ASN1_item_new((X509_CRL_it())); + if (!ossl_x509_crl_set0_libctx(crl, libctx, propq)) { + X509_CRL_free(crl); + crl = NULL; + } + return crl; +} + int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev) { X509_CRL_INFO *inf; @@ -343,7 +364,7 @@ int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev) if (inf->revoked == NULL) inf->revoked = sk_X509_REVOKED_new(X509_REVOKED_cmp); if (inf->revoked == NULL || !sk_X509_REVOKED_push(inf->revoked, rev)) { - ASN1err(ASN1_F_X509_CRL_ADD0_REVOKED, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); return 0; } inf->enc.modified = 1; @@ -358,7 +379,7 @@ int X509_CRL_verify(X509_CRL *crl, EVP_PKEY *r) } int X509_CRL_get0_by_serial(X509_CRL *crl, - X509_REVOKED **ret, ASN1_INTEGER *serial) + X509_REVOKED **ret, const ASN1_INTEGER *serial) { if (crl->meth->crl_lookup) return crl->meth->crl_lookup(crl, ret, serial, NULL); @@ -369,18 +390,19 @@ int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x) { if (crl->meth->crl_lookup) return crl->meth->crl_lookup(crl, ret, - X509_get_serialNumber(x), + X509_get0_serialNumber(x), X509_get_issuer_name(x)); return 0; } static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r) { - return (ASN1_item_verify(ASN1_ITEM_rptr(X509_CRL_INFO), - &crl->sig_alg, &crl->signature, &crl->crl, r)); + return ASN1_item_verify_ex(ASN1_ITEM_rptr(X509_CRL_INFO), + &crl->sig_alg, &crl->signature, &crl->crl, NULL, + r, crl->libctx, crl->propq); } -static int crl_revoked_issuer_match(X509_CRL *crl, X509_NAME *nm, +static int crl_revoked_issuer_match(X509_CRL *crl, const X509_NAME *nm, X509_REVOKED *rev) { int i; @@ -408,8 +430,8 @@ static int crl_revoked_issuer_match(X509_CRL *crl, X509_NAME *nm, } static int def_crl_lookup(X509_CRL *crl, - X509_REVOKED **ret, ASN1_INTEGER *serial, - X509_NAME *issuer) + X509_REVOKED **ret, const ASN1_INTEGER *serial, + const X509_NAME *issuer) { X509_REVOKED rtmp, *rev; int idx, num; @@ -422,7 +444,8 @@ static int def_crl_lookup(X509_CRL *crl, * under a lock to avoid race condition. */ if (!sk_X509_REVOKED_is_sorted(crl->crl.revoked)) { - CRYPTO_THREAD_write_lock(crl->lock); + if (!CRYPTO_THREAD_write_lock(crl->lock)) + return 0; sk_X509_REVOKED_sort(crl->crl.revoked); CRYPTO_THREAD_unlock(crl->lock); } @@ -458,15 +481,15 @@ X509_CRL_METHOD *X509_CRL_METHOD_new(int (*crl_init) (X509_CRL *crl), int (*crl_free) (X509_CRL *crl), int (*crl_lookup) (X509_CRL *crl, X509_REVOKED **ret, - ASN1_INTEGER *ser, - X509_NAME *issuer), + const ASN1_INTEGER *ser, + const X509_NAME *issuer), int (*crl_verify) (X509_CRL *crl, EVP_PKEY *pk)) { X509_CRL_METHOD *m = OPENSSL_malloc(sizeof(*m)); if (m == NULL) { - X509err(X509_F_X509_CRL_METHOD_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return NULL; } m->crl_init = crl_init; @@ -493,3 +516,19 @@ void *X509_CRL_get_meth_data(X509_CRL *crl) { return crl->meth_data; } + +int ossl_x509_crl_set0_libctx(X509_CRL *x, OSSL_LIB_CTX *libctx, + const char *propq) +{ + if (x != NULL) { + x->libctx = libctx; + OPENSSL_free(x->propq); + x->propq = NULL; + if (propq != NULL) { + x->propq = OPENSSL_strdup(propq); + if (x->propq == NULL) + return 0; + } + } + return 1; +} diff --git a/crypto/x509/x_exten.c b/crypto/x509/x_exten.c index bd7518ef12bf..4e63b50caa62 100644 --- a/crypto/x509/x_exten.c +++ b/crypto/x509/x_exten.c @@ -1,7 +1,7 @@ /* * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 diff --git a/crypto/x509/x_name.c b/crypto/x509/x_name.c index dc4a494fb543..eded80246df9 100644 --- a/crypto/x509/x_name.c +++ b/crypto/x509/x_name.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -28,7 +28,7 @@ static int x509_name_ex_d2i(ASN1_VALUE **val, const ASN1_ITEM *it, int tag, int aclass, char opt, ASN1_TLC *ctx); -static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out, +static int x509_name_ex_i2d(const ASN1_VALUE **val, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass); static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it); static void x509_name_ex_free(ASN1_VALUE **val, const ASN1_ITEM *it); @@ -36,10 +36,10 @@ static void x509_name_ex_free(ASN1_VALUE **val, const ASN1_ITEM *it); static int x509_name_encode(X509_NAME *a); static int x509_name_canon(X509_NAME *a); static int asn1_string_canon(ASN1_STRING *out, const ASN1_STRING *in); -static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) * intname, +static int i2d_name_canon(const STACK_OF(STACK_OF_X509_NAME_ENTRY) * intname, unsigned char **in); -static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval, +static int x509_name_ex_print(BIO *out, const ASN1_VALUE **pval, int indent, const char *fname, const ASN1_PCTX *pctx); @@ -102,7 +102,7 @@ static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it) return 1; memerr: - ASN1err(ASN1_F_X509_NAME_EX_NEW, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); if (ret) { sk_X509_NAME_ENTRY_free(ret->entries); OPENSSL_free(ret); @@ -114,7 +114,7 @@ static void x509_name_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it) { X509_NAME *a; - if (!pval || !*pval) + if (pval == NULL || *pval == NULL) return; a = (X509_NAME *)*pval; @@ -156,6 +156,7 @@ static int x509_name_ex_d2i(ASN1_VALUE **val, int i, j, ret; STACK_OF(X509_NAME_ENTRY) *entries; X509_NAME_ENTRY *entry; + if (len > X509_NAME_MAX) len = X509_NAME_MAX; q = p; @@ -185,7 +186,7 @@ static int x509_name_ex_d2i(ASN1_VALUE **val, entry->set = i; if (!sk_X509_NAME_ENTRY_push(nm.x->entries, entry)) goto err; - sk_X509_NAME_ENTRY_set(entries, j, NULL); + (void)sk_X509_NAME_ENTRY_set(entries, j, NULL); } } ret = x509_name_canon(nm.x); @@ -203,15 +204,16 @@ static int x509_name_ex_d2i(ASN1_VALUE **val, X509_NAME_free(nm.x); sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, local_sk_X509_NAME_ENTRY_pop_free); - ASN1err(ASN1_F_X509_NAME_EX_D2I, ERR_R_NESTED_ASN1_ERROR); + ERR_raise(ERR_LIB_ASN1, ERR_R_NESTED_ASN1_ERROR); return 0; } -static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out, +static int x509_name_ex_i2d(const ASN1_VALUE **val, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass) { int ret; X509_NAME *a = (X509_NAME *)*val; + if (a->modified) { ret = x509_name_encode(a); if (ret < 0) @@ -232,7 +234,7 @@ static int x509_name_encode(X509_NAME *a) { union { STACK_OF(STACK_OF_X509_NAME_ENTRY) *s; - ASN1_VALUE *a; + const ASN1_VALUE *a; } intname = { NULL }; @@ -241,6 +243,7 @@ static int x509_name_encode(X509_NAME *a) STACK_OF(X509_NAME_ENTRY) *entries = NULL; X509_NAME_ENTRY *entry; int i, set = -1; + intname.s = sk_STACK_OF_X509_NAME_ENTRY_new_null(); if (!intname.s) goto memerr; @@ -273,11 +276,11 @@ static int x509_name_encode(X509_NAME *a) memerr: sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname.s, local_sk_X509_NAME_ENTRY_free); - ASN1err(ASN1_F_X509_NAME_ENCODE, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); return -1; } -static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval, +static int x509_name_ex_print(BIO *out, const ASN1_VALUE **pval, int indent, const char *fname, const ASN1_PCTX *pctx) { @@ -295,6 +298,7 @@ static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval, * comparison of Name structures can be rapidly performed by just using * memcmp() of the canonical encoding. By omitting the leading SEQUENCE name * constraints of type dirName can also be checked with a simple memcmp(). + * NOTE: For empty X509_NAME (NULL-DN), canon_enclen == 0 && canon_enc == NULL */ static int x509_name_canon(X509_NAME *a) @@ -314,7 +318,7 @@ static int x509_name_canon(X509_NAME *a) } intname = sk_STACK_OF_X509_NAME_ENTRY_new_null(); if (intname == NULL) { - X509err(X509_F_X509_NAME_CANON, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) { @@ -325,25 +329,25 @@ static int x509_name_canon(X509_NAME *a) goto err; if (!sk_STACK_OF_X509_NAME_ENTRY_push(intname, entries)) { sk_X509_NAME_ENTRY_free(entries); - X509err(X509_F_X509_NAME_CANON, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } set = entry->set; } tmpentry = X509_NAME_ENTRY_new(); if (tmpentry == NULL) { - X509err(X509_F_X509_NAME_CANON, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } tmpentry->object = OBJ_dup(entry->object); if (tmpentry->object == NULL) { - X509err(X509_F_X509_NAME_CANON, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } if (!asn1_string_canon(tmpentry->value, entry->value)) goto err; if (!sk_X509_NAME_ENTRY_push(entries, tmpentry)) { - X509err(X509_F_X509_NAME_CANON, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } tmpentry = NULL; @@ -357,7 +361,7 @@ static int x509_name_canon(X509_NAME *a) p = OPENSSL_malloc(a->canon_enclen); if (p == NULL) { - X509err(X509_F_X509_NAME_CANON, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); goto err; } @@ -460,11 +464,11 @@ static int asn1_string_canon(ASN1_STRING *out, const ASN1_STRING *in) } -static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) * _intname, +static int i2d_name_canon(const STACK_OF(STACK_OF_X509_NAME_ENTRY) * _intname, unsigned char **in) { int i, len, ltmp; - ASN1_VALUE *v; + const ASN1_VALUE *v; STACK_OF(ASN1_VALUE) *intname = (STACK_OF(ASN1_VALUE) *)_intname; len = 0; @@ -472,21 +476,23 @@ static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) * _intname, v = sk_ASN1_VALUE_value(intname, i); ltmp = ASN1_item_ex_i2d(&v, in, ASN1_ITEM_rptr(X509_NAME_ENTRIES), -1, -1); - if (ltmp < 0) - return ltmp; + if (ltmp < 0 || len > INT_MAX - ltmp) + return -1; len += ltmp; } return len; } -int X509_NAME_set(X509_NAME **xn, X509_NAME *name) +int X509_NAME_set(X509_NAME **xn, const X509_NAME *name) { + X509_NAME *name_copy; + if (*xn == name) return *xn != NULL; - if ((name = X509_NAME_dup(name)) == NULL) + if ((name_copy = X509_NAME_dup(name)) == NULL) return 0; X509_NAME_free(*xn); - *xn = name; + *xn = name_copy; return 1; } @@ -496,9 +502,9 @@ int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase) int i; b = X509_NAME_oneline(name, NULL, 0); - if (!b) + if (b == NULL) return 0; - if (!*b) { + if (*b == '\0') { OPENSSL_free(b); return 1; } @@ -528,12 +534,12 @@ int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase) OPENSSL_free(b); return 1; err: - X509err(X509_F_X509_NAME_PRINT, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); OPENSSL_free(b); return 0; } -int X509_NAME_get0_der(X509_NAME *nm, const unsigned char **pder, +int X509_NAME_get0_der(const X509_NAME *nm, const unsigned char **pder, size_t *pderlen) { /* Make sure encoding is valid */ diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c index 9be7e9286571..b290075c8589 100644 --- a/crypto/x509/x_pubkey.c +++ b/crypto/x509/x_pubkey.c @@ -1,92 +1,386 @@ /* - * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 */ +/* + * DSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + #include <stdio.h> #include "internal/cryptlib.h" #include <openssl/asn1t.h> #include <openssl/x509.h> +#include <openssl/engine.h> #include "crypto/asn1.h" #include "crypto/evp.h" #include "crypto/x509.h" #include <openssl/rsa.h> #include <openssl/dsa.h> +#include <openssl/decoder.h> +#include <openssl/encoder.h> +#include "internal/provider.h" +#include "internal/sizes.h" struct X509_pubkey_st { X509_ALGOR *algor; ASN1_BIT_STRING *public_key; + EVP_PKEY *pkey; + + /* extra data for the callback, used by d2i_PUBKEY_ex */ + OSSL_LIB_CTX *libctx; + char *propq; + + /* Flag to force legacy keys */ + unsigned int flag_force_legacy : 1; }; -static int x509_pubkey_decode(EVP_PKEY **pk, X509_PUBKEY *key); +static int x509_pubkey_decode(EVP_PKEY **pk, const X509_PUBKEY *key); -/* Minor tweak to operation: free up EVP_PKEY */ -static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, - void *exarg) +static int x509_pubkey_set0_libctx(X509_PUBKEY *x, OSSL_LIB_CTX *libctx, + const char *propq) { - if (operation == ASN1_OP_FREE_POST) { - X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; - EVP_PKEY_free(pubkey->pkey); - } else if (operation == ASN1_OP_D2I_POST) { - /* Attempt to decode public key and cache in pubkey structure. */ - X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; - EVP_PKEY_free(pubkey->pkey); - pubkey->pkey = NULL; - /* - * Opportunistically decode the key but remove any non fatal errors - * from the queue. Subsequent explicit attempts to decode/use the key - * will return an appropriate error. - */ - ERR_set_mark(); - if (x509_pubkey_decode(&pubkey->pkey, pubkey) == -1) - return 0; - ERR_pop_to_mark(); + if (x != NULL) { + x->libctx = libctx; + OPENSSL_free(x->propq); + x->propq = NULL; + if (propq != NULL) { + x->propq = OPENSSL_strdup(propq); + if (x->propq == NULL) + return 0; + } } return 1; } -ASN1_SEQUENCE_cb(X509_PUBKEY, pubkey_cb) = { +ASN1_SEQUENCE(X509_PUBKEY_INTERNAL) = { ASN1_SIMPLE(X509_PUBKEY, algor, X509_ALGOR), ASN1_SIMPLE(X509_PUBKEY, public_key, ASN1_BIT_STRING) -} ASN1_SEQUENCE_END_cb(X509_PUBKEY, X509_PUBKEY) +} static_ASN1_SEQUENCE_END_name(X509_PUBKEY, X509_PUBKEY_INTERNAL) + +X509_PUBKEY *ossl_d2i_X509_PUBKEY_INTERNAL(const unsigned char **pp, + long len, OSSL_LIB_CTX *libctx) +{ + X509_PUBKEY *xpub = OPENSSL_zalloc(sizeof(*xpub)); + + if (xpub == NULL) + return NULL; + return (X509_PUBKEY *)ASN1_item_d2i_ex((ASN1_VALUE **)&xpub, pp, len, + ASN1_ITEM_rptr(X509_PUBKEY_INTERNAL), + libctx, NULL); +} + +void ossl_X509_PUBKEY_INTERNAL_free(X509_PUBKEY *xpub) +{ + ASN1_item_free((ASN1_VALUE *)xpub, ASN1_ITEM_rptr(X509_PUBKEY_INTERNAL)); +} + +static void x509_pubkey_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it) +{ + X509_PUBKEY *pubkey; + + if (pval != NULL && (pubkey = (X509_PUBKEY *)*pval) != NULL) { + X509_ALGOR_free(pubkey->algor); + ASN1_BIT_STRING_free(pubkey->public_key); + EVP_PKEY_free(pubkey->pkey); + OPENSSL_free(pubkey->propq); + OPENSSL_free(pubkey); + *pval = NULL; + } +} + +static int x509_pubkey_ex_populate(ASN1_VALUE **pval, const ASN1_ITEM *it) +{ + X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; + + return (pubkey->algor != NULL + || (pubkey->algor = X509_ALGOR_new()) != NULL) + && (pubkey->public_key != NULL + || (pubkey->public_key = ASN1_BIT_STRING_new()) != NULL); +} + + +static int x509_pubkey_ex_new_ex(ASN1_VALUE **pval, const ASN1_ITEM *it, + OSSL_LIB_CTX *libctx, const char *propq) +{ + X509_PUBKEY *ret; + + if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL + || !x509_pubkey_ex_populate((ASN1_VALUE **)&ret, NULL) + || !x509_pubkey_set0_libctx(ret, libctx, propq)) { + x509_pubkey_ex_free((ASN1_VALUE **)&ret, NULL); + ret = NULL; + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + } else { + *pval = (ASN1_VALUE *)ret; + } + + return ret != NULL; +} + +static int x509_pubkey_ex_d2i_ex(ASN1_VALUE **pval, + const unsigned char **in, long len, + const ASN1_ITEM *it, int tag, int aclass, + char opt, ASN1_TLC *ctx, OSSL_LIB_CTX *libctx, + const char *propq) +{ + const unsigned char *in_saved = *in; + size_t publen; + X509_PUBKEY *pubkey; + int ret; + OSSL_DECODER_CTX *dctx = NULL; + unsigned char *tmpbuf = NULL; + + if (*pval == NULL && !x509_pubkey_ex_new_ex(pval, it, libctx, propq)) + return 0; + if (!x509_pubkey_ex_populate(pval, NULL)) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* This ensures that |*in| advances properly no matter what */ + if ((ret = ASN1_item_ex_d2i(pval, in, len, + ASN1_ITEM_rptr(X509_PUBKEY_INTERNAL), + tag, aclass, opt, ctx)) <= 0) + return ret; + + publen = *in - in_saved; + if (!ossl_assert(publen > 0)) { + ERR_raise(ERR_LIB_ASN1, ERR_R_INTERNAL_ERROR); + return 0; + } + + pubkey = (X509_PUBKEY *)*pval; + EVP_PKEY_free(pubkey->pkey); + pubkey->pkey = NULL; + + /* + * Opportunistically decode the key but remove any non fatal errors + * from the queue. Subsequent explicit attempts to decode/use the key + * will return an appropriate error. + */ + ERR_set_mark(); + /* + * Try to decode with legacy method first. This ensures that engines + * aren't overriden by providers. + */ + if ((ret = x509_pubkey_decode(&pubkey->pkey, pubkey)) == -1) { + /* -1 indicates a fatal error, like malloc failure */ + ERR_clear_last_mark(); + goto end; + } + + /* Try to decode it into an EVP_PKEY with OSSL_DECODER */ + if (ret <= 0 && !pubkey->flag_force_legacy) { + const unsigned char *p; + char txtoidname[OSSL_MAX_NAME_SIZE]; + size_t slen = publen; + + /* + * The decoders don't know how to handle anything other than Universal + * class so we modify the data accordingly. + */ + if (aclass != V_ASN1_UNIVERSAL) { + tmpbuf = OPENSSL_memdup(in_saved, publen); + if (tmpbuf == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return 0; + } + in_saved = tmpbuf; + *tmpbuf = V_ASN1_CONSTRUCTED | V_ASN1_SEQUENCE; + } + p = in_saved; + + if (OBJ_obj2txt(txtoidname, sizeof(txtoidname), + pubkey->algor->algorithm, 0) <= 0) { + ERR_clear_last_mark(); + goto end; + } + if ((dctx = + OSSL_DECODER_CTX_new_for_pkey(&pubkey->pkey, + "DER", "SubjectPublicKeyInfo", + txtoidname, EVP_PKEY_PUBLIC_KEY, + pubkey->libctx, + pubkey->propq)) != NULL) + /* + * As said higher up, we're being opportunistic. In other words, + * we don't care if we fail. + */ + if (OSSL_DECODER_from_data(dctx, &p, &slen)) { + if (slen != 0) { + /* + * If we successfully decoded then we *must* consume all the + * bytes. + */ + ERR_clear_last_mark(); + ERR_raise(ERR_LIB_ASN1, EVP_R_DECODE_ERROR); + goto end; + } + } + } + + ERR_pop_to_mark(); + ret = 1; + end: + OSSL_DECODER_CTX_free(dctx); + OPENSSL_free(tmpbuf); + return ret; +} + +static int x509_pubkey_ex_i2d(const ASN1_VALUE **pval, unsigned char **out, + const ASN1_ITEM *it, int tag, int aclass) +{ + return ASN1_item_ex_i2d(pval, out, ASN1_ITEM_rptr(X509_PUBKEY_INTERNAL), + tag, aclass); +} + +static int x509_pubkey_ex_print(BIO *out, const ASN1_VALUE **pval, int indent, + const char *fname, const ASN1_PCTX *pctx) +{ + return ASN1_item_print(out, *pval, indent, + ASN1_ITEM_rptr(X509_PUBKEY_INTERNAL), pctx); +} + +static const ASN1_EXTERN_FUNCS x509_pubkey_ff = { + NULL, + NULL, + x509_pubkey_ex_free, + 0, /* Default clear behaviour is OK */ + NULL, + x509_pubkey_ex_i2d, + x509_pubkey_ex_print, + x509_pubkey_ex_new_ex, + x509_pubkey_ex_d2i_ex, +}; + +IMPLEMENT_EXTERN_ASN1(X509_PUBKEY, V_ASN1_SEQUENCE, x509_pubkey_ff) IMPLEMENT_ASN1_FUNCTIONS(X509_PUBKEY) +X509_PUBKEY *X509_PUBKEY_new_ex(OSSL_LIB_CTX *libctx, const char *propq) +{ + X509_PUBKEY *pubkey = NULL; + + pubkey = (X509_PUBKEY *)ASN1_item_new_ex(X509_PUBKEY_it(), libctx, propq); + if (!x509_pubkey_set0_libctx(pubkey, libctx, propq)) { + X509_PUBKEY_free(pubkey); + pubkey = NULL; + } + return pubkey; +} + +/* + * X509_PUBKEY_dup() must be implemented manually, because there is no + * support for it in ASN1_EXTERN_FUNCS. + */ +X509_PUBKEY *X509_PUBKEY_dup(const X509_PUBKEY *a) +{ + X509_PUBKEY *pubkey = OPENSSL_zalloc(sizeof(*pubkey)); + + if (pubkey == NULL + || !x509_pubkey_set0_libctx(pubkey, a->libctx, a->propq) + || (pubkey->algor = X509_ALGOR_dup(a->algor)) == NULL + || (pubkey->public_key = ASN1_BIT_STRING_new()) == NULL + || !ASN1_BIT_STRING_set(pubkey->public_key, + a->public_key->data, + a->public_key->length)) { + x509_pubkey_ex_free((ASN1_VALUE **)&pubkey, + ASN1_ITEM_rptr(X509_PUBKEY_INTERNAL)); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + return NULL; + } + + if (a->pkey != NULL) { + ERR_set_mark(); + pubkey->pkey = EVP_PKEY_dup(a->pkey); + if (pubkey->pkey == NULL) { + pubkey->flag_force_legacy = 1; + if (x509_pubkey_decode(&pubkey->pkey, pubkey) <= 0) { + x509_pubkey_ex_free((ASN1_VALUE **)&pubkey, + ASN1_ITEM_rptr(X509_PUBKEY_INTERNAL)); + ERR_clear_last_mark(); + return NULL; + } + } + ERR_pop_to_mark(); + } + return pubkey; +} + int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) { X509_PUBKEY *pk = NULL; - if (x == NULL) + if (x == NULL || pkey == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); return 0; + } - if ((pk = X509_PUBKEY_new()) == NULL) - goto error; - - if (pkey->ameth) { - if (pkey->ameth->pub_encode) { + if (pkey->ameth != NULL) { + if ((pk = X509_PUBKEY_new()) == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + goto error; + } + if (pkey->ameth->pub_encode != NULL) { if (!pkey->ameth->pub_encode(pk, pkey)) { - X509err(X509_F_X509_PUBKEY_SET, - X509_R_PUBLIC_KEY_ENCODE_ERROR); + ERR_raise(ERR_LIB_X509, X509_R_PUBLIC_KEY_ENCODE_ERROR); goto error; } } else { - X509err(X509_F_X509_PUBKEY_SET, X509_R_METHOD_NOT_SUPPORTED); + ERR_raise(ERR_LIB_X509, X509_R_METHOD_NOT_SUPPORTED); goto error; } - } else { - X509err(X509_F_X509_PUBKEY_SET, X509_R_UNSUPPORTED_ALGORITHM); + } else if (evp_pkey_is_provided(pkey)) { + unsigned char *der = NULL; + size_t derlen = 0; + OSSL_ENCODER_CTX *ectx = + OSSL_ENCODER_CTX_new_for_pkey(pkey, EVP_PKEY_PUBLIC_KEY, + "DER", "SubjectPublicKeyInfo", + NULL); + + if (OSSL_ENCODER_to_data(ectx, &der, &derlen)) { + const unsigned char *pder = der; + + pk = d2i_X509_PUBKEY(NULL, &pder, (long)derlen); + } + + OSSL_ENCODER_CTX_free(ectx); + OPENSSL_free(der); + } + + if (pk == NULL) { + ERR_raise(ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM); goto error; } X509_PUBKEY_free(*x); + if (!EVP_PKEY_up_ref(pkey)) { + ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR); + goto error; + } *x = pk; + + /* + * pk->pkey is NULL when using the legacy routine, but is non-NULL when + * going through the encoder, and for all intents and purposes, it's + * a perfect copy of the public key portions of |pkey|, just not the same + * instance. If that's all there was to pkey then we could simply return + * early, right here. However, some application might very well depend on + * the passed |pkey| being used and none other, so we spend a few more + * cycles throwing away the newly created |pk->pkey| and replace it with + * |pkey|. + */ + if (pk->pkey != NULL) + EVP_PKEY_free(pk->pkey); + pk->pkey = pkey; - EVP_PKEY_up_ref(pkey); return 1; error: @@ -98,20 +392,36 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) * Attempt to decode a public key. * Returns 1 on success, 0 for a decode failure and -1 for a fatal * error e.g. malloc failure. + * + * This function is #legacy. */ +static int x509_pubkey_decode(EVP_PKEY **ppkey, const X509_PUBKEY *key) +{ + EVP_PKEY *pkey; + int nid; + nid = OBJ_obj2nid(key->algor->algorithm); + if (!key->flag_force_legacy) { +#ifndef OPENSSL_NO_ENGINE + ENGINE *e = NULL; -static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) -{ - EVP_PKEY *pkey = EVP_PKEY_new(); + e = ENGINE_get_pkey_meth_engine(nid); + if (e == NULL) + return 0; + ENGINE_finish(e); +#else + return 0; +#endif + } + pkey = EVP_PKEY_new(); if (pkey == NULL) { - X509err(X509_F_X509_PUBKEY_DECODE, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return -1; } - if (!EVP_PKEY_set_type(pkey, OBJ_obj2nid(key->algor->algorithm))) { - X509err(X509_F_X509_PUBKEY_DECODE, X509_R_UNSUPPORTED_ALGORITHM); + if (!EVP_PKEY_set_type(pkey, nid)) { + ERR_raise(ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM); goto error; } @@ -121,12 +431,10 @@ static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) * future we could have different return codes for decode * errors and fatal errors such as malloc failure. */ - if (!pkey->ameth->pub_decode(pkey, key)) { - X509err(X509_F_X509_PUBKEY_DECODE, X509_R_PUBLIC_KEY_DECODE_ERROR); + if (!pkey->ameth->pub_decode(pkey, key)) goto error; - } } else { - X509err(X509_F_X509_PUBKEY_DECODE, X509_R_METHOD_NOT_SUPPORTED); + ERR_raise(ERR_LIB_X509, X509_R_METHOD_NOT_SUPPORTED); goto error; } @@ -138,110 +446,181 @@ static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) return 0; } -EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) +EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key) { - EVP_PKEY *ret = NULL; - - if (key == NULL || key->public_key == NULL) + if (key == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER); return NULL; + } - if (key->pkey != NULL) - return key->pkey; - - /* - * When the key ASN.1 is initially parsed an attempt is made to - * decode the public key and cache the EVP_PKEY structure. If this - * operation fails the cached value will be NULL. Parsing continues - * to allow parsing of unknown key types or unsupported forms. - * We repeat the decode operation so the appropriate errors are left - * in the queue. - */ - x509_pubkey_decode(&ret, key); - /* If decode doesn't fail something bad happened */ - if (ret != NULL) { - X509err(X509_F_X509_PUBKEY_GET0, ERR_R_INTERNAL_ERROR); - EVP_PKEY_free(ret); + if (key->pkey == NULL) { + /* We failed to decode the key when we loaded it, or it was never set */ + ERR_raise(ERR_LIB_EVP, EVP_R_DECODE_ERROR); + return NULL; } - return NULL; + return key->pkey; } -EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) +EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key) { EVP_PKEY *ret = X509_PUBKEY_get0(key); if (ret != NULL && !EVP_PKEY_up_ref(ret)) { - X509err(X509_F_X509_PUBKEY_GET, ERR_R_INTERNAL_ERROR); + ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR); ret = NULL; } return ret; } /* - * Now two pseudo ASN1 routines that take an EVP_PKEY structure and encode or - * decode as X509_PUBKEY + * Now three pseudo ASN1 routines that take an EVP_PKEY structure and encode + * or decode as X509_PUBKEY */ - -EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length) +static EVP_PKEY *d2i_PUBKEY_int(EVP_PKEY **a, + const unsigned char **pp, long length, + OSSL_LIB_CTX *libctx, const char *propq, + unsigned int force_legacy, + X509_PUBKEY * + (*d2i_x509_pubkey)(X509_PUBKEY **a, + const unsigned char **in, + long len)) { - X509_PUBKEY *xpk; - EVP_PKEY *pktmp; + X509_PUBKEY *xpk, *xpk2 = NULL, **pxpk = NULL; + EVP_PKEY *pktmp = NULL; const unsigned char *q; + q = *pp; - xpk = d2i_X509_PUBKEY(NULL, &q, length); - if (!xpk) - return NULL; + + /* + * If libctx or propq are non-NULL, we take advantage of the reuse + * feature. It's not generally recommended, but is safe enough for + * newly created structures. + */ + if (libctx != NULL || propq != NULL || force_legacy) { + xpk2 = OPENSSL_zalloc(sizeof(*xpk2)); + if (xpk2 == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + return NULL; + } + if (!x509_pubkey_set0_libctx(xpk2, libctx, propq)) + goto end; + xpk2->flag_force_legacy = !!force_legacy; + pxpk = &xpk2; + } + xpk = d2i_x509_pubkey(pxpk, &q, length); + if (xpk == NULL) + goto end; pktmp = X509_PUBKEY_get(xpk); X509_PUBKEY_free(xpk); - if (!pktmp) - return NULL; + xpk2 = NULL; /* We know that xpk == xpk2 */ + if (pktmp == NULL) + goto end; *pp = q; - if (a) { + if (a != NULL) { EVP_PKEY_free(*a); *a = pktmp; } + end: + X509_PUBKEY_free(xpk2); return pktmp; } -int i2d_PUBKEY(EVP_PKEY *a, unsigned char **pp) +/* For the algorithm specific d2i functions further down */ +EVP_PKEY *ossl_d2i_PUBKEY_legacy(EVP_PKEY **a, const unsigned char **pp, + long length) { - X509_PUBKEY *xpk = NULL; - int ret; - if (!a) + return d2i_PUBKEY_int(a, pp, length, NULL, NULL, 1, d2i_X509_PUBKEY); +} + +EVP_PKEY *d2i_PUBKEY_ex(EVP_PKEY **a, const unsigned char **pp, long length, + OSSL_LIB_CTX *libctx, const char *propq) +{ + return d2i_PUBKEY_int(a, pp, length, libctx, propq, 0, d2i_X509_PUBKEY); +} + +EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length) +{ + return d2i_PUBKEY_ex(a, pp, length, NULL, NULL); +} + +int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp) +{ + int ret = -1; + + if (a == NULL) return 0; - if (!X509_PUBKEY_set(&xpk, a)) - return -1; - ret = i2d_X509_PUBKEY(xpk, pp); - X509_PUBKEY_free(xpk); + if (a->ameth != NULL) { + X509_PUBKEY *xpk = NULL; + + if ((xpk = X509_PUBKEY_new()) == NULL) + return -1; + + /* pub_encode() only encode parameters, not the key itself */ + if (a->ameth->pub_encode != NULL && a->ameth->pub_encode(xpk, a)) { + xpk->pkey = (EVP_PKEY *)a; + ret = i2d_X509_PUBKEY(xpk, pp); + xpk->pkey = NULL; + } + X509_PUBKEY_free(xpk); + } else if (a->keymgmt != NULL) { + OSSL_ENCODER_CTX *ctx = + OSSL_ENCODER_CTX_new_for_pkey(a, EVP_PKEY_PUBLIC_KEY, + "DER", "SubjectPublicKeyInfo", + NULL); + BIO *out = BIO_new(BIO_s_mem()); + BUF_MEM *buf = NULL; + + if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0 + && out != NULL + && OSSL_ENCODER_to_bio(ctx, out) + && BIO_get_mem_ptr(out, &buf) > 0) { + ret = buf->length; + + if (pp != NULL) { + if (*pp == NULL) { + *pp = (unsigned char *)buf->data; + buf->length = 0; + buf->data = NULL; + } else { + memcpy(*pp, buf->data, ret); + *pp += ret; + } + } + } + BIO_free(out); + OSSL_ENCODER_CTX_free(ctx); + } + return ret; } /* * The following are equivalents but which return RSA and DSA keys */ -#ifndef OPENSSL_NO_RSA RSA *d2i_RSA_PUBKEY(RSA **a, const unsigned char **pp, long length) { EVP_PKEY *pkey; - RSA *key; + RSA *key = NULL; const unsigned char *q; + q = *pp; - pkey = d2i_PUBKEY(NULL, &q, length); - if (!pkey) + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) return NULL; key = EVP_PKEY_get1_RSA(pkey); EVP_PKEY_free(pkey); - if (!key) + if (key == NULL) return NULL; *pp = q; - if (a) { + if (a != NULL) { RSA_free(*a); *a = key; } return key; } -int i2d_RSA_PUBKEY(RSA *a, unsigned char **pp) +int i2d_RSA_PUBKEY(const RSA *a, unsigned char **pp) { EVP_PKEY *pktmp; int ret; @@ -249,11 +628,95 @@ int i2d_RSA_PUBKEY(RSA *a, unsigned char **pp) return 0; pktmp = EVP_PKEY_new(); if (pktmp == NULL) { - ASN1err(ASN1_F_I2D_RSA_PUBKEY, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); return -1; } - EVP_PKEY_set1_RSA(pktmp, a); + (void)EVP_PKEY_assign_RSA(pktmp, (RSA *)a); ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; + EVP_PKEY_free(pktmp); + return ret; +} + +#ifndef OPENSSL_NO_DH +DH *ossl_d2i_DH_PUBKEY(DH **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + DH *key = NULL; + const unsigned char *q; + + q = *pp; + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) + return NULL; + if (EVP_PKEY_get_id(pkey) == EVP_PKEY_DH) + key = EVP_PKEY_get1_DH(pkey); + EVP_PKEY_free(pkey); + if (key == NULL) + return NULL; + *pp = q; + if (a != NULL) { + DH_free(*a); + *a = key; + } + return key; +} + +int ossl_i2d_DH_PUBKEY(const DH *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return 0; + pktmp = EVP_PKEY_new(); + if (pktmp == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return -1; + } + (void)EVP_PKEY_assign_DH(pktmp, (DH *)a); + ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; + EVP_PKEY_free(pktmp); + return ret; +} + +DH *ossl_d2i_DHx_PUBKEY(DH **a, const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + DH *key = NULL; + const unsigned char *q; + + q = *pp; + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) + return NULL; + if (EVP_PKEY_get_id(pkey) == EVP_PKEY_DHX) + key = EVP_PKEY_get1_DH(pkey); + EVP_PKEY_free(pkey); + if (key == NULL) + return NULL; + *pp = q; + if (a != NULL) { + DH_free(*a); + *a = key; + } + return key; +} + +int ossl_i2d_DHx_PUBKEY(const DH *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + if (!a) + return 0; + pktmp = EVP_PKEY_new(); + if (pktmp == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return -1; + } + (void)EVP_PKEY_assign(pktmp, EVP_PKEY_DHX, (DH *)a); + ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; EVP_PKEY_free(pktmp); return ret; } @@ -263,25 +726,50 @@ int i2d_RSA_PUBKEY(RSA *a, unsigned char **pp) DSA *d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp, long length) { EVP_PKEY *pkey; - DSA *key; + DSA *key = NULL; const unsigned char *q; + q = *pp; - pkey = d2i_PUBKEY(NULL, &q, length); - if (!pkey) + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) return NULL; key = EVP_PKEY_get1_DSA(pkey); EVP_PKEY_free(pkey); - if (!key) + if (key == NULL) return NULL; *pp = q; - if (a) { + if (a != NULL) { + DSA_free(*a); + *a = key; + } + return key; +} + +/* Called from decoders; disallows provided DSA keys without parameters. */ +DSA *ossl_d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp, long length) +{ + DSA *key = NULL; + const unsigned char *data; + const BIGNUM *p, *q, *g; + + data = *pp; + key = d2i_DSA_PUBKEY(NULL, &data, length); + if (key == NULL) + return NULL; + DSA_get0_pqg(key, &p, &q, &g); + if (p == NULL || q == NULL || g == NULL) { + DSA_free(key); + return NULL; + } + *pp = data; + if (a != NULL) { DSA_free(*a); *a = key; } return key; } -int i2d_DSA_PUBKEY(DSA *a, unsigned char **pp) +int i2d_DSA_PUBKEY(const DSA *a, unsigned char **pp) { EVP_PKEY *pktmp; int ret; @@ -289,11 +777,12 @@ int i2d_DSA_PUBKEY(DSA *a, unsigned char **pp) return 0; pktmp = EVP_PKEY_new(); if (pktmp == NULL) { - ASN1err(ASN1_F_I2D_DSA_PUBKEY, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); return -1; } - EVP_PKEY_set1_DSA(pktmp, a); + (void)EVP_PKEY_assign_DSA(pktmp, (DSA *)a); ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; EVP_PKEY_free(pktmp); return ret; } @@ -303,39 +792,213 @@ int i2d_DSA_PUBKEY(DSA *a, unsigned char **pp) EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp, long length) { EVP_PKEY *pkey; - EC_KEY *key; + EC_KEY *key = NULL; const unsigned char *q; + int type; + q = *pp; - pkey = d2i_PUBKEY(NULL, &q, length); - if (!pkey) + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) return NULL; - key = EVP_PKEY_get1_EC_KEY(pkey); + type = EVP_PKEY_get_id(pkey); + if (type == EVP_PKEY_EC || type == EVP_PKEY_SM2) + key = EVP_PKEY_get1_EC_KEY(pkey); EVP_PKEY_free(pkey); - if (!key) + if (key == NULL) return NULL; *pp = q; - if (a) { + if (a != NULL) { EC_KEY_free(*a); *a = key; } return key; } -int i2d_EC_PUBKEY(EC_KEY *a, unsigned char **pp) +int i2d_EC_PUBKEY(const EC_KEY *a, unsigned char **pp) { EVP_PKEY *pktmp; int ret; - if (!a) + + if (a == NULL) return 0; if ((pktmp = EVP_PKEY_new()) == NULL) { - ASN1err(ASN1_F_I2D_EC_PUBKEY, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); return -1; } - EVP_PKEY_set1_EC_KEY(pktmp, a); + (void)EVP_PKEY_assign_EC_KEY(pktmp, (EC_KEY *)a); ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; EVP_PKEY_free(pktmp); return ret; } + +ECX_KEY *ossl_d2i_ED25519_PUBKEY(ECX_KEY **a, + const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + ECX_KEY *key = NULL; + const unsigned char *q; + + q = *pp; + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) + return NULL; + key = ossl_evp_pkey_get1_ED25519(pkey); + EVP_PKEY_free(pkey); + if (key == NULL) + return NULL; + *pp = q; + if (a != NULL) { + ossl_ecx_key_free(*a); + *a = key; + } + return key; +} + +int ossl_i2d_ED25519_PUBKEY(const ECX_KEY *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + + if (a == NULL) + return 0; + if ((pktmp = EVP_PKEY_new()) == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return -1; + } + (void)EVP_PKEY_assign(pktmp, EVP_PKEY_ED25519, (ECX_KEY *)a); + ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; + EVP_PKEY_free(pktmp); + return ret; +} + +ECX_KEY *ossl_d2i_ED448_PUBKEY(ECX_KEY **a, + const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + ECX_KEY *key = NULL; + const unsigned char *q; + + q = *pp; + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) + return NULL; + if (EVP_PKEY_get_id(pkey) == EVP_PKEY_ED448) + key = ossl_evp_pkey_get1_ED448(pkey); + EVP_PKEY_free(pkey); + if (key == NULL) + return NULL; + *pp = q; + if (a != NULL) { + ossl_ecx_key_free(*a); + *a = key; + } + return key; +} + +int ossl_i2d_ED448_PUBKEY(const ECX_KEY *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + + if (a == NULL) + return 0; + if ((pktmp = EVP_PKEY_new()) == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return -1; + } + (void)EVP_PKEY_assign(pktmp, EVP_PKEY_ED448, (ECX_KEY *)a); + ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; + EVP_PKEY_free(pktmp); + return ret; +} + +ECX_KEY *ossl_d2i_X25519_PUBKEY(ECX_KEY **a, + const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + ECX_KEY *key = NULL; + const unsigned char *q; + + q = *pp; + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) + return NULL; + if (EVP_PKEY_get_id(pkey) == EVP_PKEY_X25519) + key = ossl_evp_pkey_get1_X25519(pkey); + EVP_PKEY_free(pkey); + if (key == NULL) + return NULL; + *pp = q; + if (a != NULL) { + ossl_ecx_key_free(*a); + *a = key; + } + return key; +} + +int ossl_i2d_X25519_PUBKEY(const ECX_KEY *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + + if (a == NULL) + return 0; + if ((pktmp = EVP_PKEY_new()) == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return -1; + } + (void)EVP_PKEY_assign(pktmp, EVP_PKEY_X25519, (ECX_KEY *)a); + ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; + EVP_PKEY_free(pktmp); + return ret; +} + +ECX_KEY *ossl_d2i_X448_PUBKEY(ECX_KEY **a, + const unsigned char **pp, long length) +{ + EVP_PKEY *pkey; + ECX_KEY *key = NULL; + const unsigned char *q; + + q = *pp; + pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length); + if (pkey == NULL) + return NULL; + if (EVP_PKEY_get_id(pkey) == EVP_PKEY_X448) + key = ossl_evp_pkey_get1_X448(pkey); + EVP_PKEY_free(pkey); + if (key == NULL) + return NULL; + *pp = q; + if (a != NULL) { + ossl_ecx_key_free(*a); + *a = key; + } + return key; +} + +int ossl_i2d_X448_PUBKEY(const ECX_KEY *a, unsigned char **pp) +{ + EVP_PKEY *pktmp; + int ret; + + if (a == NULL) + return 0; + if ((pktmp = EVP_PKEY_new()) == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + return -1; + } + (void)EVP_PKEY_assign(pktmp, EVP_PKEY_X448, (ECX_KEY *)a); + ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; + EVP_PKEY_free(pktmp); + return ret; +} + #endif int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *aobj, @@ -357,7 +1020,7 @@ int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *aobj, int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg, const unsigned char **pk, int *ppklen, - X509_ALGOR **pa, X509_PUBKEY *pub) + X509_ALGOR **pa, const X509_PUBKEY *pub) { if (ppkalg) *ppkalg = pub->algor->algorithm; @@ -376,3 +1039,34 @@ ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x) return NULL; return x->cert_info.key->public_key; } + +/* Returns 1 for equal, 0, for non-equal, < 0 on error */ +int X509_PUBKEY_eq(const X509_PUBKEY *a, const X509_PUBKEY *b) +{ + X509_ALGOR *algA, *algB; + EVP_PKEY *pA, *pB; + + if (a == b) + return 1; + if (a == NULL || b == NULL) + return 0; + if (!X509_PUBKEY_get0_param(NULL, NULL, NULL, &algA, a) || algA == NULL + || !X509_PUBKEY_get0_param(NULL, NULL, NULL, &algB, b) || algB == NULL) + return -2; + if (X509_ALGOR_cmp(algA, algB) != 0) + return 0; + if ((pA = X509_PUBKEY_get0(a)) == NULL + || (pB = X509_PUBKEY_get0(b)) == NULL) + return -2; + return EVP_PKEY_eq(pA, pB); +} + +int ossl_x509_PUBKEY_get0_libctx(OSSL_LIB_CTX **plibctx, const char **ppropq, + const X509_PUBKEY *key) +{ + if (plibctx) + *plibctx = key->libctx; + if (ppropq) + *ppropq = key->propq; + return 1; +} diff --git a/crypto/x509/x_req.c b/crypto/x509/x_req.c index d2b02f6dae86..293d4be71335 100644 --- a/crypto/x509/x_req.c +++ b/crypto/x509/x_req.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -45,6 +45,67 @@ static int rinf_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, return 1; } +static int req_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, + void *exarg) +{ + X509_REQ *ret = (X509_REQ *)*pval; + + switch (operation) { + case ASN1_OP_D2I_PRE: + ASN1_OCTET_STRING_free(ret->distinguishing_id); + /* fall thru */ + case ASN1_OP_NEW_POST: + ret->distinguishing_id = NULL; + break; + + case ASN1_OP_FREE_POST: + ASN1_OCTET_STRING_free(ret->distinguishing_id); + OPENSSL_free(ret->propq); + break; + case ASN1_OP_DUP_POST: + { + X509_REQ *old = exarg; + + if (!ossl_x509_req_set0_libctx(ret, old->libctx, old->propq)) + return 0; + if (old->req_info.pubkey != NULL) { + EVP_PKEY *pkey = X509_PUBKEY_get0(old->req_info.pubkey); + + if (pkey != NULL) { + pkey = EVP_PKEY_dup(pkey); + if (pkey == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); + return 0; + } + if (!X509_PUBKEY_set(&ret->req_info.pubkey, pkey)) { + EVP_PKEY_free(pkey); + ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR); + return 0; + } + EVP_PKEY_free(pkey); + } + } + } + break; + case ASN1_OP_GET0_LIBCTX: + { + OSSL_LIB_CTX **libctx = exarg; + + *libctx = ret->libctx; + } + break; + case ASN1_OP_GET0_PROPQ: + { + const char **propq = exarg; + + *propq = ret->propq; + } + break; + } + + return 1; +} + ASN1_SEQUENCE_enc(X509_REQ_INFO, enc, rinf_cb) = { ASN1_SIMPLE(X509_REQ_INFO, version, ASN1_INTEGER), ASN1_SIMPLE(X509_REQ_INFO, subject, X509_NAME), @@ -57,7 +118,7 @@ ASN1_SEQUENCE_enc(X509_REQ_INFO, enc, rinf_cb) = { IMPLEMENT_ASN1_FUNCTIONS(X509_REQ_INFO) -ASN1_SEQUENCE_ref(X509_REQ, 0) = { +ASN1_SEQUENCE_ref(X509_REQ, req_cb) = { ASN1_EMBED(X509_REQ, req_info, X509_REQ_INFO), ASN1_EMBED(X509_REQ, sig_alg, X509_ALGOR), ASN1_SIMPLE(X509_REQ, signature, ASN1_BIT_STRING) @@ -66,3 +127,47 @@ ASN1_SEQUENCE_ref(X509_REQ, 0) = { IMPLEMENT_ASN1_FUNCTIONS(X509_REQ) IMPLEMENT_ASN1_DUP_FUNCTION(X509_REQ) + +void X509_REQ_set0_distinguishing_id(X509_REQ *x, ASN1_OCTET_STRING *d_id) +{ + ASN1_OCTET_STRING_free(x->distinguishing_id); + x->distinguishing_id = d_id; +} + +ASN1_OCTET_STRING *X509_REQ_get0_distinguishing_id(X509_REQ *x) +{ + return x->distinguishing_id; +} + +/* + * This should only be used if the X509_REQ object was embedded inside another + * asn1 object and it needs a libctx to operate. + * Use X509_REQ_new_ex() instead if possible. + */ +int ossl_x509_req_set0_libctx(X509_REQ *x, OSSL_LIB_CTX *libctx, + const char *propq) +{ + if (x != NULL) { + x->libctx = libctx; + OPENSSL_free(x->propq); + x->propq = NULL; + if (propq != NULL) { + x->propq = OPENSSL_strdup(propq); + if (x->propq == NULL) + return 0; + } + } + return 1; +} + +X509_REQ *X509_REQ_new_ex(OSSL_LIB_CTX *libctx, const char *propq) +{ + X509_REQ *req = NULL; + + req = (X509_REQ *)ASN1_item_new((X509_REQ_it())); + if (!ossl_x509_req_set0_libctx(req, libctx, propq)) { + X509_REQ_free(req); + req = NULL; + } + return req; +} diff --git a/crypto/x509/x_x509.c b/crypto/x509/x_x509.c index 7aa8b77ae73a..010578b19a31 100644 --- a/crypto/x509/x_x509.c +++ b/crypto/x509/x_x509.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -31,7 +31,7 @@ ASN1_SEQUENCE_enc(X509_CINF, enc, 0) = { IMPLEMENT_ASN1_FUNCTIONS(X509_CINF) /* X509 top level structure needs a bit of customisation */ -extern void policy_cache_free(X509_POLICY_CACHE *cache); +extern void ossl_policy_cache_free(X509_POLICY_CACHE *cache); static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg) @@ -46,13 +46,14 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, ASN1_OCTET_STRING_free(ret->skid); AUTHORITY_KEYID_free(ret->akid); CRL_DIST_POINTS_free(ret->crldp); - policy_cache_free(ret->policy_cache); + ossl_policy_cache_free(ret->policy_cache); GENERAL_NAMES_free(ret->altname); NAME_CONSTRAINTS_free(ret->nc); #ifndef OPENSSL_NO_RFC3779 sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free); ASIdentifiers_free(ret->rfc3779_asid); #endif + ASN1_OCTET_STRING_free(ret->distinguishing_id); /* fall thru */ @@ -73,6 +74,7 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, ret->rfc3779_addr = NULL; ret->rfc3779_asid = NULL; #endif + ret->distinguishing_id = NULL; ret->aux = NULL; ret->crldp = NULL; if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data)) @@ -85,19 +87,46 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, ASN1_OCTET_STRING_free(ret->skid); AUTHORITY_KEYID_free(ret->akid); CRL_DIST_POINTS_free(ret->crldp); - policy_cache_free(ret->policy_cache); + ossl_policy_cache_free(ret->policy_cache); GENERAL_NAMES_free(ret->altname); NAME_CONSTRAINTS_free(ret->nc); #ifndef OPENSSL_NO_RFC3779 sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free); ASIdentifiers_free(ret->rfc3779_asid); #endif + ASN1_OCTET_STRING_free(ret->distinguishing_id); + OPENSSL_free(ret->propq); break; + case ASN1_OP_DUP_POST: + { + X509 *old = exarg; + + if (!ossl_x509_set0_libctx(ret, old->libctx, old->propq)) + return 0; + } + break; + case ASN1_OP_GET0_LIBCTX: + { + OSSL_LIB_CTX **libctx = exarg; + + *libctx = ret->libctx; + } + break; + + case ASN1_OP_GET0_PROPQ: + { + const char **propq = exarg; + + *propq = ret->propq; + } + break; + + default: + break; } return 1; - } ASN1_SEQUENCE_ref(X509, x509_cb) = { @@ -107,15 +136,46 @@ ASN1_SEQUENCE_ref(X509, x509_cb) = { } ASN1_SEQUENCE_END_ref(X509, X509) IMPLEMENT_ASN1_FUNCTIONS(X509) - IMPLEMENT_ASN1_DUP_FUNCTION(X509) +/* + * This should only be used if the X509 object was embedded inside another + * asn1 object and it needs a libctx to operate. + * Use X509_new_ex() instead if possible. + */ +int ossl_x509_set0_libctx(X509 *x, OSSL_LIB_CTX *libctx, const char *propq) +{ + if (x != NULL) { + x->libctx = libctx; + OPENSSL_free(x->propq); + x->propq = NULL; + if (propq != NULL) { + x->propq = OPENSSL_strdup(propq); + if (x->propq == NULL) + return 0; + } + } + return 1; +} + +X509 *X509_new_ex(OSSL_LIB_CTX *libctx, const char *propq) +{ + X509 *cert = NULL; + + cert = (X509 *)ASN1_item_new_ex(X509_it(), libctx, propq); + if (!ossl_x509_set0_libctx(cert, libctx, propq)) { + X509_free(cert); + cert = NULL; + } + return cert; +} + int X509_set_ex_data(X509 *r, int idx, void *arg) { return CRYPTO_set_ex_data(&r->ex_data, idx, arg); } -void *X509_get_ex_data(X509 *r, int idx) +void *X509_get_ex_data(const X509 *r, int idx) { return CRYPTO_get_ex_data(&r->ex_data, idx); } @@ -163,7 +223,7 @@ X509 *d2i_X509_AUX(X509 **a, const unsigned char **pp, long length) * error path, but that depends on similar hygiene in lower-level functions. * Here we avoid compounding the problem. */ -static int i2d_x509_aux_internal(X509 *a, unsigned char **pp) +static int i2d_x509_aux_internal(const X509 *a, unsigned char **pp) { int length, tmplen; unsigned char *start = pp != NULL ? *pp : NULL; @@ -197,7 +257,7 @@ static int i2d_x509_aux_internal(X509 *a, unsigned char **pp) * the allocation, nor can we allow i2d_X509_CERT_AUX() to increment the * allocated buffer. */ -int i2d_X509_AUX(X509 *a, unsigned char **pp) +int i2d_X509_AUX(const X509 *a, unsigned char **pp) { int length; unsigned char *tmp; @@ -213,7 +273,7 @@ int i2d_X509_AUX(X509 *a, unsigned char **pp) /* Allocate requisite combined storage */ *pp = tmp = OPENSSL_malloc(length); if (tmp == NULL) { - X509err(X509_F_I2D_X509_AUX, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE); return -1; } @@ -245,3 +305,14 @@ int X509_get_signature_nid(const X509 *x) { return OBJ_obj2nid(x->sig_alg.algorithm); } + +void X509_set0_distinguishing_id(X509 *x, ASN1_OCTET_STRING *d_id) +{ + ASN1_OCTET_STRING_free(x->distinguishing_id); + x->distinguishing_id = d_id; +} + +ASN1_OCTET_STRING *X509_get0_distinguishing_id(X509 *x) +{ + return x->distinguishing_id; +} diff --git a/crypto/x509/x_x509a.c b/crypto/x509/x_x509a.c index c5175faef7af..f7953c269e7c 100644 --- a/crypto/x509/x_x509a.c +++ b/crypto/x509/x_x509a.c @@ -1,7 +1,7 @@ /* - * Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1999-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * 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 @@ -125,6 +125,8 @@ int X509_add1_reject_object(X509 *x, const ASN1_OBJECT *obj) { X509_CERT_AUX *aux; ASN1_OBJECT *objtmp; + int res = 0; + if ((objtmp = OBJ_dup(obj)) == NULL) return 0; if ((aux = aux_get(x)) == NULL) @@ -132,10 +134,13 @@ int X509_add1_reject_object(X509 *x, const ASN1_OBJECT *obj) if (aux->reject == NULL && (aux->reject = sk_ASN1_OBJECT_new_null()) == NULL) goto err; - return sk_ASN1_OBJECT_push(aux->reject, objtmp); + if (sk_ASN1_OBJECT_push(aux->reject, objtmp) > 0) + res = 1; + err: - ASN1_OBJECT_free(objtmp); - return 0; + if (!res) + ASN1_OBJECT_free(objtmp); + return res; } void X509_trust_clear(X509 *x) |