/* * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project. */ /* ==================================================================== * Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * licensing@OpenSSL.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ #include #include #include #include #include #include #include #include "internal.h" static void *v2i_subject_alt(const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const STACK_OF(CONF_VALUE) *nval); static void *v2i_issuer_alt(const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const STACK_OF(CONF_VALUE) *nval); static int copy_email(const X509V3_CTX *ctx, GENERAL_NAMES *gens, int move_p); static int copy_issuer(const X509V3_CTX *ctx, GENERAL_NAMES *gens); static int do_othername(GENERAL_NAME *gen, const char *value, const X509V3_CTX *ctx); static int do_dirname(GENERAL_NAME *gen, const char *value, const X509V3_CTX *ctx); static STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES_cb( const X509V3_EXT_METHOD *method, void *ext, STACK_OF(CONF_VALUE) *ret) { return i2v_GENERAL_NAMES(method, ext, ret); } const X509V3_EXT_METHOD v3_alt[] = { {NID_subject_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES), 0, 0, 0, 0, 0, 0, i2v_GENERAL_NAMES_cb, v2i_subject_alt, NULL, NULL, NULL}, {NID_issuer_alt_name, 0, ASN1_ITEM_ref(GENERAL_NAMES), 0, 0, 0, 0, 0, 0, i2v_GENERAL_NAMES_cb, v2i_issuer_alt, NULL, NULL, NULL}, {NID_certificate_issuer, 0, ASN1_ITEM_ref(GENERAL_NAMES), 0, 0, 0, 0, 0, 0, i2v_GENERAL_NAMES_cb, NULL, NULL, NULL, NULL}, }; STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(const X509V3_EXT_METHOD *method, const GENERAL_NAMES *gens, STACK_OF(CONF_VALUE) *ret) { int ret_was_null = ret == NULL; for (size_t i = 0; i < sk_GENERAL_NAME_num(gens); i++) { const GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i); STACK_OF(CONF_VALUE) *tmp = i2v_GENERAL_NAME(method, gen, ret); if (tmp == NULL) { if (ret_was_null) { sk_CONF_VALUE_pop_free(ret, X509V3_conf_free); } return NULL; } ret = tmp; } if (!ret) { return sk_CONF_VALUE_new_null(); } return ret; } STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(const X509V3_EXT_METHOD *method, const GENERAL_NAME *gen, STACK_OF(CONF_VALUE) *ret) { // Note the error-handling for this function relies on there being at most // one |X509V3_add_value| call. If there were two and the second failed, we // would need to sometimes free the first call's result. unsigned char *p; char oline[256], htmp[5]; int i; switch (gen->type) { case GEN_OTHERNAME: if (!X509V3_add_value("othername", "", &ret)) { return NULL; } break; case GEN_X400: if (!X509V3_add_value("X400Name", "", &ret)) { return NULL; } break; case GEN_EDIPARTY: if (!X509V3_add_value("EdiPartyName", "", &ret)) { return NULL; } break; case GEN_EMAIL: if (!x509V3_add_value_asn1_string("email", gen->d.ia5, &ret)) { return NULL; } break; case GEN_DNS: if (!x509V3_add_value_asn1_string("DNS", gen->d.ia5, &ret)) { return NULL; } break; case GEN_URI: if (!x509V3_add_value_asn1_string("URI", gen->d.ia5, &ret)) { return NULL; } break; case GEN_DIRNAME: if (X509_NAME_oneline(gen->d.dirn, oline, 256) == NULL || !X509V3_add_value("DirName", oline, &ret)) { return NULL; } break; case GEN_IPADD: p = gen->d.ip->data; if (gen->d.ip->length == 4) { snprintf(oline, sizeof(oline), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); } else if (gen->d.ip->length == 16) { oline[0] = 0; for (i = 0; i < 8; i++) { uint16_t v = ((uint16_t)p[0] << 8) | p[1]; snprintf(htmp, sizeof(htmp), "%X", v); p += 2; OPENSSL_strlcat(oline, htmp, sizeof(oline)); if (i != 7) { OPENSSL_strlcat(oline, ":", sizeof(oline)); } } } else { if (!X509V3_add_value("IP Address", "", &ret)) { return NULL; } break; } if (!X509V3_add_value("IP Address", oline, &ret)) { return NULL; } break; case GEN_RID: i2t_ASN1_OBJECT(oline, 256, gen->d.rid); if (!X509V3_add_value("Registered ID", oline, &ret)) { return NULL; } break; } return ret; } int GENERAL_NAME_print(BIO *out, const GENERAL_NAME *gen) { switch (gen->type) { case GEN_OTHERNAME: BIO_printf(out, "othername:"); break; case GEN_X400: BIO_printf(out, "X400Name:"); break; case GEN_EDIPARTY: // Maybe fix this: it is supported now BIO_printf(out, "EdiPartyName:"); 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: { const unsigned char *p = gen->d.ip->data; if (gen->d.ip->length == 4) { BIO_printf(out, "IP Address:%d.%d.%d.%d", p[0], p[1], p[2], p[3]); } else if (gen->d.ip->length == 16) { BIO_printf(out, "IP Address"); for (int i = 0; i < 8; i++) { uint16_t v = ((uint16_t)p[0] << 8) | p[1]; BIO_printf(out, ":%X", v); p += 2; } BIO_puts(out, "\n"); } else { BIO_printf(out, "IP Address:"); break; } break; } case GEN_RID: BIO_printf(out, "Registered ID"); i2a_ASN1_OBJECT(out, gen->d.rid); break; } return 1; } static void *v2i_issuer_alt(const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const STACK_OF(CONF_VALUE) *nval) { GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); if (gens == NULL) { return NULL; } for (size_t i = 0; i < sk_CONF_VALUE_num(nval); i++) { const CONF_VALUE *cnf = sk_CONF_VALUE_value(nval, i); if (x509v3_conf_name_matches(cnf->name, "issuer") && cnf->value && !strcmp(cnf->value, "copy")) { if (!copy_issuer(ctx, gens)) { goto err; } } else { GENERAL_NAME *gen = v2i_GENERAL_NAME(method, ctx, cnf); if (gen == NULL || !sk_GENERAL_NAME_push(gens, gen)) { GENERAL_NAME_free(gen); goto err; } } } 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(const X509V3_CTX *ctx, GENERAL_NAMES *gens) { if (ctx && (ctx->flags == X509V3_CTX_TEST)) { return 1; } if (!ctx || !ctx->issuer_cert) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_NO_ISSUER_DETAILS); return 0; } int i = X509_get_ext_by_NID(ctx->issuer_cert, NID_subject_alt_name, -1); if (i < 0) { return 1; } int ret = 0; GENERAL_NAMES *ialt = NULL; X509_EXTENSION *ext; if (!(ext = X509_get_ext(ctx->issuer_cert, i)) || !(ialt = X509V3_EXT_d2i(ext))) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_ISSUER_DECODE_ERROR); goto err; } for (size_t j = 0; j < sk_GENERAL_NAME_num(ialt); j++) { GENERAL_NAME *gen = sk_GENERAL_NAME_value(ialt, j); if (!sk_GENERAL_NAME_push(gens, gen)) { goto err; } // Ownership of |gen| has moved from |ialt| to |gens|. sk_GENERAL_NAME_set(ialt, j, NULL); } ret = 1; err: GENERAL_NAMES_free(ialt); return ret; } static void *v2i_subject_alt(const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const STACK_OF(CONF_VALUE) *nval) { GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); if (gens == NULL) { return NULL; } for (size_t i = 0; i < sk_CONF_VALUE_num(nval); i++) { const CONF_VALUE *cnf = sk_CONF_VALUE_value(nval, i); if (x509v3_conf_name_matches(cnf->name, "email") && cnf->value && !strcmp(cnf->value, "copy")) { if (!copy_email(ctx, gens, 0)) { goto err; } } else if (x509v3_conf_name_matches(cnf->name, "email") && cnf->value && !strcmp(cnf->value, "move")) { if (!copy_email(ctx, gens, 1)) { goto err; } } else { GENERAL_NAME *gen = v2i_GENERAL_NAME(method, ctx, cnf); if (gen == NULL || !sk_GENERAL_NAME_push(gens, gen)) { GENERAL_NAME_free(gen); goto err; } } } 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(const 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; if (ctx != NULL && ctx->flags == X509V3_CTX_TEST) { return 1; } if (!ctx || (!ctx->subject_cert && !ctx->subject_req)) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_NO_SUBJECT_DETAILS); goto err; } // Find the subject name if (ctx->subject_cert) { nm = X509_get_subject_name(ctx->subject_cert); } else { nm = X509_REQ_get_subject_name(ctx->subject_req); } // Now add any email address(es) to STACK i = -1; 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 || !(gen = GENERAL_NAME_new())) { goto err; } gen->d.ia5 = email; email = NULL; gen->type = GEN_EMAIL; if (!sk_GENERAL_NAME_push(gens, gen)) { 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, const X509V3_CTX *ctx, const STACK_OF(CONF_VALUE) *nval) { GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); if (gens == NULL) { return NULL; } for (size_t i = 0; i < sk_CONF_VALUE_num(nval); i++) { const CONF_VALUE *cnf = sk_CONF_VALUE_value(nval, i); GENERAL_NAME *gen = v2i_GENERAL_NAME(method, ctx, cnf); if (gen == NULL || !sk_GENERAL_NAME_push(gens, gen)) { GENERAL_NAME_free(gen); goto err; } } return gens; err: sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); return NULL; } GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const CONF_VALUE *cnf) { return v2i_GENERAL_NAME_ex(NULL, method, ctx, cnf, 0); } static GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out, const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, int gen_type, const char *value, int is_nc) { if (!value) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_MISSING_VALUE); return NULL; } GENERAL_NAME *gen = NULL; if (out) { gen = out; } else { gen = GENERAL_NAME_new(); if (gen == NULL) { return NULL; } } switch (gen_type) { case GEN_URI: case GEN_EMAIL: case GEN_DNS: { ASN1_IA5STRING *str = ASN1_IA5STRING_new(); if (str == NULL || !ASN1_STRING_set(str, value, strlen(value))) { ASN1_STRING_free(str); goto err; } gen->type = gen_type; gen->d.ia5 = str; break; } case GEN_RID: { ASN1_OBJECT *obj; if (!(obj = OBJ_txt2obj(value, 0))) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_BAD_OBJECT); ERR_add_error_data(2, "value=", value); goto err; } gen->type = GEN_RID; gen->d.rid = obj; break; } case GEN_IPADD: gen->type = GEN_IPADD; if (is_nc) { gen->d.ip = a2i_IPADDRESS_NC(value); } else { gen->d.ip = a2i_IPADDRESS(value); } if (gen->d.ip == NULL) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_BAD_IP_ADDRESS); ERR_add_error_data(2, "value=", value); goto err; } break; case GEN_DIRNAME: if (!do_dirname(gen, value, ctx)) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_DIRNAME_ERROR); goto err; } break; case GEN_OTHERNAME: if (!do_othername(gen, value, ctx)) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_OTHERNAME_ERROR); goto err; } break; default: OPENSSL_PUT_ERROR(X509V3, X509V3_R_UNSUPPORTED_TYPE); goto err; } 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, const X509V3_CTX *ctx, const CONF_VALUE *cnf, int is_nc) { const char *name = cnf->name; const char *value = cnf->value; if (!value) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_MISSING_VALUE); return NULL; } int type; if (x509v3_conf_name_matches(name, "email")) { type = GEN_EMAIL; } else if (x509v3_conf_name_matches(name, "URI")) { type = GEN_URI; } else if (x509v3_conf_name_matches(name, "DNS")) { type = GEN_DNS; } else if (x509v3_conf_name_matches(name, "RID")) { type = GEN_RID; } else if (x509v3_conf_name_matches(name, "IP")) { type = GEN_IPADD; } else if (x509v3_conf_name_matches(name, "dirName")) { type = GEN_DIRNAME; } else if (x509v3_conf_name_matches(name, "otherName")) { type = GEN_OTHERNAME; } else { OPENSSL_PUT_ERROR(X509V3, X509V3_R_UNSUPPORTED_OPTION); ERR_add_error_data(2, "name=", name); return NULL; } return a2i_GENERAL_NAME(out, method, ctx, type, value, is_nc); } static int do_othername(GENERAL_NAME *gen, const char *value, const X509V3_CTX *ctx) { const char *semicolon = strchr(value, ';'); if (semicolon == NULL) { return 0; } OTHERNAME *name = OTHERNAME_new(); if (name == NULL) { return 0; } char *objtmp = OPENSSL_strndup(value, semicolon - value); if (objtmp == NULL) { goto err; } ASN1_OBJECT_free(name->type_id); name->type_id = OBJ_txt2obj(objtmp, /*dont_search_names=*/0); OPENSSL_free(objtmp); if (name->type_id == NULL) { goto err; } ASN1_TYPE_free(name->value); name->value = ASN1_generate_v3(semicolon + 1, ctx); if (name->value == NULL) { goto err; } gen->type = GEN_OTHERNAME; gen->d.otherName = name; return 1; err: OTHERNAME_free(name); return 0; } static int do_dirname(GENERAL_NAME *gen, const char *value, const X509V3_CTX *ctx) { int ret = 0; X509_NAME *nm = X509_NAME_new(); if (nm == NULL) { goto err; } const STACK_OF(CONF_VALUE) *sk = X509V3_get_section(ctx, value); if (sk == NULL) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_SECTION_NOT_FOUND); ERR_add_error_data(2, "section=", value); goto err; } // FIXME: should allow other character types... if (!X509V3_NAME_from_section(nm, sk, MBSTRING_ASC)) { goto err; } gen->type = GEN_DIRNAME; gen->d.dirn = nm; ret = 1; err: if (!ret) { X509_NAME_free(nm); } return ret; }