From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- third_party/heimdal/kadmin/mod.c | 853 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 853 insertions(+) create mode 100644 third_party/heimdal/kadmin/mod.c (limited to 'third_party/heimdal/kadmin/mod.c') diff --git a/third_party/heimdal/kadmin/mod.c b/third_party/heimdal/kadmin/mod.c new file mode 100644 index 0000000..2d4bd5d --- /dev/null +++ b/third_party/heimdal/kadmin/mod.c @@ -0,0 +1,853 @@ +/* + * Copyright (c) 1997 - 2006 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 "kadmin_locl.h" +#include "kadmin-commands.h" + +void +add_tl(kadm5_principal_ent_rec *princ, int type, krb5_data *data) +{ + krb5_tl_data *tl, **ptl; + + tl = ecalloc(1, sizeof(*tl)); + tl->tl_data_next = NULL; + tl->tl_data_type = type; + tl->tl_data_length = data->length; + tl->tl_data_contents = data->data; + + if (tl->tl_data_length < 0 || data->length != (size_t)tl->tl_data_length) + errx(1, "TL data overflow"); + + princ->n_tl_data++; + ptl = &princ->tl_data; + while (*ptl != NULL) + ptl = &(*ptl)->tl_data_next; + *ptl = tl; + + return; +} + +/* + * Find a TL data of type KRB5_TL_EXTENSION that has an extension of type + * `etype' in it. + */ +krb5_tl_data * +get_tl(kadm5_principal_ent_rec *princ, int type) +{ + krb5_tl_data *tl = princ->tl_data; + + while (tl && tl->tl_data_type != type) + tl = tl->tl_data_next; + return tl; +} + +static void +add_constrained_delegation(krb5_context contextp, + kadm5_principal_ent_rec *princ, + struct getarg_strings *strings) +{ + krb5_error_code ret; + HDB_extension ext; + krb5_data buf; + size_t size = 0; + + memset(&ext, 0, sizeof(ext)); + ext.mandatory = FALSE; + ext.data.element = choice_HDB_extension_data_allowed_to_delegate_to; + + if (strings->num_strings == 1 && strings->strings[0][0] == '\0') { + ext.data.u.allowed_to_delegate_to.val = NULL; + ext.data.u.allowed_to_delegate_to.len = 0; + } else { + krb5_principal p; + int i; + + ext.data.u.allowed_to_delegate_to.val = + calloc(strings->num_strings, + sizeof(ext.data.u.allowed_to_delegate_to.val[0])); + ext.data.u.allowed_to_delegate_to.len = strings->num_strings; + + for (i = 0; i < strings->num_strings; i++) { + ret = krb5_parse_name(contextp, strings->strings[i], &p); + if (ret) + abort(); + ret = copy_Principal(p, &ext.data.u.allowed_to_delegate_to.val[i]); + if (ret) + abort(); + krb5_free_principal(contextp, p); + } + } + + ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, + &ext, &size, ret); + free_HDB_extension(&ext); + if (ret) + abort(); + if (buf.length != size) + abort(); + + add_tl(princ, KRB5_TL_EXTENSION, &buf); +} + +static void +add_aliases(krb5_context contextp, kadm5_principal_ent_rec *princ, + struct getarg_strings *strings) +{ + krb5_error_code ret = 0; + HDB_extension ext; + krb5_data buf; + krb5_principal p; + size_t size = 0; + int i; + + memset(&ext, 0, sizeof(ext)); + ext.mandatory = FALSE; + ext.data.element = choice_HDB_extension_data_aliases; + ext.data.u.aliases.case_insensitive = 0; + + if (strings->num_strings == 1 && strings->strings[0][0] == '\0') { + ext.data.u.aliases.aliases.val = NULL; + ext.data.u.aliases.aliases.len = 0; + } else { + ext.data.u.aliases.aliases.val = + calloc(strings->num_strings, + sizeof(ext.data.u.aliases.aliases.val[0])); + ext.data.u.aliases.aliases.len = strings->num_strings; + + for (i = 0; ret == 0 && i < strings->num_strings; i++) { + ret = krb5_parse_name(contextp, strings->strings[i], &p); + if (ret) + krb5_err(contextp, 1, ret, "Could not parse alias %s", + strings->strings[i]); + if (ret == 0) + ret = copy_Principal(p, &ext.data.u.aliases.aliases.val[i]); + if (ret) + krb5_err(contextp, 1, ret, "Could not copy parsed alias %s", + strings->strings[i]); + krb5_free_principal(contextp, p); + } + } + + ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, + &ext, &size, ret); + free_HDB_extension(&ext); + if (ret) + abort(); + if (buf.length != size) + abort(); + + add_tl(princ, KRB5_TL_EXTENSION, &buf); +} + +static void +add_pkinit_acl(krb5_context contextp, kadm5_principal_ent_rec *princ, + struct getarg_strings *strings) +{ + krb5_error_code ret; + HDB_extension ext; + krb5_data buf; + size_t size = 0; + int i; + + memset(&ext, 0, sizeof(ext)); + ext.mandatory = FALSE; + ext.data.element = choice_HDB_extension_data_pkinit_acl; + ext.data.u.aliases.case_insensitive = 0; + + if (strings->num_strings == 1 && strings->strings[0][0] == '\0') { + ext.data.u.pkinit_acl.val = NULL; + ext.data.u.pkinit_acl.len = 0; + } else { + ext.data.u.pkinit_acl.val = + calloc(strings->num_strings, + sizeof(ext.data.u.pkinit_acl.val[0])); + ext.data.u.pkinit_acl.len = strings->num_strings; + + for (i = 0; i < strings->num_strings; i++) { + ext.data.u.pkinit_acl.val[i].subject = estrdup(strings->strings[i]); + } + } + + ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, + &ext, &size, ret); + free_HDB_extension(&ext); + if (ret) + abort(); + if (buf.length != size) + abort(); + + add_tl(princ, KRB5_TL_EXTENSION, &buf); +} + +static krb5_error_code +add_etypes(krb5_context contextp, + kadm5_principal_ent_rec *princ, + struct getarg_strings *strings) +{ + krb5_error_code ret = 0; + HDB_EncTypeList etypes; + krb5_data buf; + size_t i, size; + + etypes.len = strings->num_strings; + if ((etypes.val = calloc(strings->num_strings, + sizeof(etypes.val[0]))) == NULL) + krb5_err(contextp, 1, ret, "Out of memory"); + + for (i = 0; i < strings->num_strings; i++) { + krb5_enctype etype; + + ret = krb5_string_to_enctype(contextp, strings->strings[i], &etype); + if (ret) { + krb5_warn(contextp, ret, "Could not parse enctype %s", + strings->strings[i]); + free(etypes.val); + return ret; + } + etypes.val[i] = etype; + } + + if (ret == 0) { + ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length, + &etypes, &size, ret); + } + if (ret || buf.length != size) + abort(); + add_tl(princ, KRB5_TL_ETYPES, &buf); + free(etypes.val); + return 0; +} + +static void +add_kvno_diff(krb5_context contextp, kadm5_principal_ent_rec *princ, + int is_svc_diff, krb5_kvno kvno_diff) +{ + krb5_error_code ret; + HDB_extension ext; + krb5_data buf; + size_t size = 0; + + if (kvno_diff < 0) + return; + if (kvno_diff > 2048) + kvno_diff = 2048; + + ext.mandatory = 0; + if (is_svc_diff) { + ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc; + ext.data.u.hist_kvno_diff_svc = (unsigned int)kvno_diff; + } else { + ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt; + ext.data.u.hist_kvno_diff_clnt = (unsigned int)kvno_diff; + } + ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, + &ext, &size, ret); + if (ret) + abort(); + if (buf.length != size) + abort(); + + add_tl(princ, KRB5_TL_EXTENSION, &buf); +} + +static void +add_krb5_config(kadm5_principal_ent_rec *princ, const char *fname) +{ + HDB_extension ext; + krb5_data buf; + size_t size; + int ret; + + memset(&ext, 0, sizeof(ext)); + ext.mandatory = FALSE; + ext.data.element = choice_HDB_extension_data_krb5_config; + + if ((ret = rk_undumpdata(fname, + &ext.data.u.krb5_config.data, + &ext.data.u.krb5_config.length))) { + krb5_warn(context, ret, "Could not read %s", fname); + return; + } + + ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, + &ext, &size, ret); + free_HDB_extension(&ext); + if (ret) + abort(); + if (buf.length != size) + abort(); + add_tl(princ, KRB5_TL_EXTENSION, &buf); +} + +struct mod_data { + struct modify_namespace_key_rotation_options *opt_ns_kr; + struct modify_namespace_options *opt_ns; + struct modify_options *opt; + void *kadm_handle; +}; + +static int +do_mod_entry(krb5_principal principal, void *data) +{ + krb5_error_code ret; + kadm5_principal_ent_rec princ; + int mask = 0; + struct mod_data *m = data; + struct modify_options *e = m->opt; + + memset (&princ, 0, sizeof(princ)); + ret = kadm5_get_principal(m->kadm_handle, principal, &princ, + KADM5_PRINCIPAL | KADM5_ATTRIBUTES | + KADM5_MAX_LIFE | KADM5_MAX_RLIFE | + KADM5_PRINC_EXPIRE_TIME | + KADM5_PW_EXPIRATION); + if(ret) + return ret; + + if(e->max_ticket_life_string || + e->max_renewable_life_string || + e->expiration_time_string || + e->pw_expiration_time_string || + e->attributes_string || + e->policy_string || + e->kvno_integer != -1 || + e->service_enctypes_strings.num_strings || + e->constrained_delegation_strings.num_strings || + e->alias_strings.num_strings || + e->pkinit_acl_strings.num_strings || + e->krb5_config_file_string || + e->hist_kvno_diff_clnt_integer != -1 || + e->hist_kvno_diff_svc_integer != -1) { + ret = set_entry(context, &princ, &mask, + e->max_ticket_life_string, + e->max_renewable_life_string, + e->expiration_time_string, + e->pw_expiration_time_string, + e->attributes_string, + e->policy_string); + if(e->kvno_integer != -1) { + princ.kvno = e->kvno_integer; + mask |= KADM5_KVNO; + } + if (e->constrained_delegation_strings.num_strings) { + add_constrained_delegation(context, &princ, + &e->constrained_delegation_strings); + mask |= KADM5_TL_DATA; + } + if (e->alias_strings.num_strings) { + add_aliases(context, &princ, &e->alias_strings); + mask |= KADM5_TL_DATA; + } + if (e->pkinit_acl_strings.num_strings) { + add_pkinit_acl(context, &princ, &e->pkinit_acl_strings); + mask |= KADM5_TL_DATA; + } + if (e->service_enctypes_strings.num_strings) { + ret = add_etypes(context, &princ, &e->service_enctypes_strings); + mask |= KADM5_TL_DATA; + } + if (e->hist_kvno_diff_clnt_integer != -1) { + add_kvno_diff(context, &princ, 0, e->hist_kvno_diff_clnt_integer); + mask |= KADM5_TL_DATA; + } + if (e->hist_kvno_diff_svc_integer != -1) { + add_kvno_diff(context, &princ, 1, e->hist_kvno_diff_svc_integer); + mask |= KADM5_TL_DATA; + } + if (e->krb5_config_file_string) { + add_krb5_config(&princ, e->krb5_config_file_string); + mask |= KADM5_TL_DATA; + } + } else + ret = edit_entry(&princ, &mask, NULL, 0); + if(ret == 0) { + ret = kadm5_modify_principal(m->kadm_handle, &princ, mask); + if(ret) + krb5_warn(context, ret, "kadm5_modify_principal"); + } + + kadm5_free_principal_ent(m->kadm_handle, &princ); + return ret; +} + +int +mod_entry(struct modify_options *opt, int argc, char **argv) +{ + krb5_error_code ret = 0; + struct mod_data data; + int i; + + data.kadm_handle = NULL; + data.opt_ns_kr = NULL; + data.opt_ns = NULL; + data.opt = opt; + + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + for (i = 0; ret == 0 && i < argc; i++) + ret = foreach_principal(argv[i], do_mod_entry, "mod", &data); + if (data.kadm_handle) + kadm5_destroy(data.kadm_handle); + return ret != 0; +} + +static int +do_mod_ns_entry(krb5_principal principal, void *data) +{ + krb5_error_code ret; + kadm5_principal_ent_rec princ; + int mask = 0; + struct mod_data *m = data; + struct modify_namespace_options *e = m->opt_ns; + + memset (&princ, 0, sizeof(princ)); + ret = kadm5_get_principal(m->kadm_handle, principal, &princ, + KADM5_PRINCIPAL | KADM5_ATTRIBUTES | + KADM5_MAX_LIFE | KADM5_MAX_RLIFE | + KADM5_PRINC_EXPIRE_TIME | + KADM5_PW_EXPIRATION); + if(ret) + return ret; + + if(e->max_ticket_life_string || + e->max_renewable_life_string || + e->attributes_string || + e->enctypes_strings.num_strings || + e->krb5_config_file_string) { + ret = set_entry(context, &princ, &mask, e->max_ticket_life_string, + e->max_renewable_life_string, NULL, NULL, + e->attributes_string, NULL); + if (e->enctypes_strings.num_strings) { + ret = add_etypes(context, &princ, &e->enctypes_strings); + mask |= KADM5_TL_DATA; + } + if (e->krb5_config_file_string) { + add_krb5_config(&princ, e->krb5_config_file_string); + mask |= KADM5_TL_DATA; + } + } else + ret = edit_entry(&princ, &mask, NULL, 0); + if(ret == 0) { + ret = kadm5_modify_principal(m->kadm_handle, &princ, mask); + if(ret) + krb5_warn(context, ret, "kadm5_modify_principal"); + } + + kadm5_free_principal_ent(m->kadm_handle, &princ); + return ret; +} + +int +modify_namespace(struct modify_namespace_options *opt, int argc, char **argv) +{ + krb5_error_code ret = 0; + struct mod_data data; + int i; + + data.kadm_handle = NULL; + data.opt_ns_kr = NULL; + data.opt_ns = opt; + data.opt = NULL; + + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + for (i = 0; ret == 0 && i < argc; i++) + ret = foreach_principal(argv[i], do_mod_ns_entry, "mod_ns", &data); + if (data.kadm_handle) + kadm5_destroy(data.kadm_handle); + return ret != 0; +} + +#if 0 +struct modify_namespace_key_rotation_options { + int force_flag; + int keep_base_key_flag; + char* revoke_old_string; + char* new_key_rotation_epoch_string; + char* new_key_rotation_period_string; +}; +#endif + +static int +princ2kstuple(kadm5_principal_ent_rec *princ, + unsigned int kvno, + krb5_key_salt_tuple **kstuple, + size_t *nkstuple) +{ + krb5_error_code ret = 0; + HDB_EncTypeList etypes; + krb5_tl_data *tl; + size_t i; + + *kstuple = 0; + *nkstuple = 0; + etypes.len = 0; + etypes.val = 0; + for (tl = princ->tl_data; tl; tl = tl->tl_data_next) { + if (tl->tl_data_type != KRB5_TL_ETYPES || tl->tl_data_length < 0) + continue; + ret = decode_HDB_EncTypeList(tl->tl_data_contents, tl->tl_data_length, + &etypes, NULL); + if (ret) + break; + *nkstuple = etypes.len; + *kstuple = ecalloc(etypes.len, sizeof(kstuple[0][0])); + for (i = 0; i < etypes.len; i++) { + (*kstuple)[i].ks_enctype = etypes.val[i]; + (*kstuple)[i].ks_salttype = 0; + } + return 0; + } + if (princ->n_key_data > 0) { + *kstuple = ecalloc(1, sizeof(kstuple[0][0])); + *nkstuple = 1; + for (i = 0; i < princ->n_key_data; i++) { + if (princ->key_data->key_data_kvno == kvno) { + (*kstuple)[0].ks_enctype = princ->key_data->key_data_type[0]; + (*kstuple)[0].ks_salttype = princ->key_data->key_data_type[1]; + return 0; + } + } + } + krb5_warnx(context, "Could not determine what enctypes to generate " + "keys for; recreate namespace?"); + return EINVAL; +} + +static int +randkey_kr(kadm5_principal_ent_rec *princ, + unsigned int old_kvno, + unsigned int kvno) +{ + krb5_key_salt_tuple *kstuple = 0; + krb5_error_code ret = 0; + size_t nkstuple = 0; + + /* + * We might be using kadm5clnt, so we'll use kadm5_randkey_principal_3(), + * which will generate new keys on the server side. This allows a race, + * but it will be detected by the key rotation update checks in lib/kadm5 + * and lib/hdb. + */ + ret = princ2kstuple(princ, old_kvno, &kstuple, &nkstuple); + if (ret == 0) + ret = kadm5_randkey_principal_3(kadm_handle, princ->principal, 1, + nkstuple, kstuple, NULL, NULL); + free(kstuple); + return ret; +} + +static int +do_mod_ns_kr(krb5_principal principal, void *data) +{ + krb5_error_code ret; + kadm5_principal_ent_rec princ; + struct modify_namespace_key_rotation_options *e = data; + HDB_Ext_KeyRotation existing; + HDB_Ext_KeyRotation new_kr; + HDB_extension ext; + KeyRotation new_krs[3]; + krb5_tl_data *tl; + krb5_data d; + time_t now = time(NULL); + size_t size; + int freeit = 0; + + d.data = 0; + d.length = 0; + new_kr.len = 0; + new_kr.val = new_krs; + ext.mandatory = 0; + ext.data.element = choice_HDB_extension_data_key_rotation; + ext.data.u.key_rotation.len = 0; + ext.data.u.key_rotation.val = 0; + existing.len = 0; + existing.val = 0; + memset(&new_krs, 0, sizeof(new_krs)); + memset(&princ, 0, sizeof(princ)); + + if (e->force_flag || e->revoke_old_string) { + krb5_warnx(context, "--force and --revoke-old not implemented yet"); + return ENOTSUP; + } + + ret = kadm5_get_principal(kadm_handle, principal, &princ, + KADM5_PRINCIPAL | KADM5_KVNO | + KADM5_KEY_DATA | KADM5_TL_DATA); + if (ret == 0) { + freeit = 1; + for (tl = princ.tl_data; tl; tl = tl->tl_data_next) { + if (tl->tl_data_type != KRB5_TL_KRB5_CONFIG) + continue; + ret = decode_HDB_Ext_KeyRotation(tl->tl_data_contents, + tl->tl_data_length, &existing, NULL); + if (ret) { + krb5_warn(context, ret, "unable to decode existing key " + "rotation schedule"); + kadm5_free_principal_ent(kadm_handle, &princ); + return ret; + } + } + if (!existing.len) { + krb5_warnx(context, "no key rotation schedule; " + "re-create namespace?"); + kadm5_free_principal_ent(kadm_handle, &princ); + return EINVAL; + } + } + + if (ret) { + krb5_warn(context, ret, "No such namespace"); + kadm5_free_principal_ent(kadm_handle, &princ); + return ret; + } + + if (existing.len > 1) + new_kr.val[1] = existing.val[0]; + if (existing.len > 2) + new_kr.val[2] = existing.val[1]; + new_kr.val[0].flags = existing.val[0].flags; + new_kr.val[0].base_kvno = princ.kvno + 2; /* XXX Compute better */ + new_kr.val[0].base_key_kvno = existing.val[0].base_key_kvno + 1; + if (e->new_key_rotation_epoch_string) { + if ((ret = str2time_t(e->new_key_rotation_epoch_string, + &new_kr.val[0].epoch))) + krb5_warn(context, ret, "Invalid epoch specification: %s", + e->new_key_rotation_epoch_string); + } else { + new_kr.val[0].epoch = existing.val[0].epoch + + existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno); + } + if (ret == 0 && e->new_key_rotation_period_string) { + time_t t; + + if ((ret = str2time_t(e->new_key_rotation_period_string, &t))) + krb5_warn(context, ret, "Invalid period specification: %s", + e->new_key_rotation_period_string); + else + new_kr.val[0].period = t; + } else { + new_kr.val[0].period = existing.val[0].period + + existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno); + } + if (new_kr.val[0].epoch < now) { + krb5_warnx(context, "New epoch cannot be in the past"); + ret = EINVAL; + } + if (new_kr.val[0].epoch < 30) { + krb5_warnx(context, "New period cannot be less than 30s"); + ret = EINVAL; + } + if (ret == 0) + ret = randkey_kr(&princ, princ.kvno, new_kr.val[0].base_key_kvno); + ext.data.u.key_rotation = new_kr; + if (ret == 0) + ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length, + &ext, &size, ret); + if (ret == 0) + add_tl(&princ, KRB5_TL_EXTENSION, &d); + if (ret == 0) { + ret = kadm5_modify_principal(kadm_handle, &princ, + KADM5_PRINCIPAL | KADM5_TL_DATA); + if (ret) + krb5_warn(context, ret, "Could not update namespace"); + } + + krb5_data_free(&d); + free_HDB_Ext_KeyRotation(&existing); + if (freeit) + kadm5_free_principal_ent(kadm_handle, &princ); + return ret; +} + +int +modify_ns_kr(struct modify_namespace_key_rotation_options *opt, + int argc, + char **argv) +{ + krb5_error_code ret = 0; + struct mod_data data; + int i; + + data.kadm_handle = NULL; + data.opt_ns_kr = opt; + data.opt_ns = NULL; + data.opt = NULL; + + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + for (i = 0; ret == 0 && i < argc; i++) + ret = foreach_principal(argv[i], do_mod_ns_kr, "mod_ns", opt); + if (data.kadm_handle) + kadm5_destroy(data.kadm_handle); + return ret != 0; +} + +#define princ_realm(P) ((P)->realm) +#define princ_num_comp(P) ((P)->name.name_string.len) +#define princ_ncomp(P, N) ((P)->name.name_string.val[(N)]) + +static int +princ_cmp(const void *a, const void *b) +{ + krb5_const_principal pa = a; + krb5_const_principal pb = b; + size_t i; + int r; + + r = strcmp(princ_realm(pa), princ_realm(pb)); + if (r == 0) + r = princ_num_comp(pa) - princ_num_comp(pb); + for (i = 0; r == 0 && i < princ_num_comp(pa); i++) + r = strcmp(princ_ncomp(pa, i), princ_ncomp(pb, i)); + return r; +} + +/* Sort and remove dups */ +static void +uniq(HDB_Ext_Aliases *a) +{ + size_t i = 0; + + qsort(a->aliases.val, a->aliases.len, sizeof(a->aliases.val[0]), + princ_cmp); + + /* While there are at least two principals left to look at... */ + while (i + 1 < a->aliases.len) { + if (princ_cmp(&a->aliases.val[i], &a->aliases.val[i + 1])) { + /* ...if they are different, increment i and loop */ + i++; + continue; + } + /* ...else drop the one on the right and loop w/o incrementing i */ + free_Principal(&a->aliases.val[i + 1]); + if (i + 2 < a->aliases.len) + memmove(&a->aliases.val[i + 1], + &a->aliases.val[i + 2], + sizeof(a->aliases.val[i + 1]) * (a->aliases.len - (i + 2))); + a->aliases.len--; + } +} + +int +add_alias(void *opt, int argc, char **argv) +{ + kadm5_principal_ent_rec princ; + krb5_error_code ret; + krb5_principal p = NULL; + HDB_Ext_Aliases *a; + HDB_extension ext; + krb5_tl_data *tl = NULL; + krb5_data d; + size_t i; + + memset(&princ, 0, sizeof(princ)); + krb5_data_zero(&d); + + if (argc < 2) { + krb5_warnx(context, "Principal not given"); + return 1; + } + ret = krb5_parse_name(context, argv[0], &p); + if (ret) { + krb5_warn(context, ret, "Invalid principal: %s", argv[0]); + return 1; + } + + ret = kadm5_get_principal(kadm_handle, p, &princ, + KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA); + if (ret) { + krb5_warn(context, ret, "Principal not found %s", argv[0]); + return 1; + } + krb5_free_principal(context, p); + p = NULL; + + a = &ext.data.u.aliases; + a->case_insensitive = 0; + a->aliases.len = 0; + a->aliases.val = 0; + if ((tl = get_tl(&princ, KRB5_TL_ALIASES))) { + ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, tl->tl_data_length, + a, NULL); + if (ret) { + kadm5_free_principal_ent(kadm_handle, &princ); + krb5_warn(context, ret, "Principal has invalid aliases extension " + "contents: %s", argv[0]); + return 1; + } + } + + argv++; + argc--; + + a->aliases.val = realloc(a->aliases.val, + sizeof(a->aliases.val[0]) * (a->aliases.len + argc)); + if (a->aliases.val == NULL) + krb5_err(context, 1, errno, "Out of memory"); + for (i = 0; ret == 0 && i < argc; i++) { + ret = krb5_parse_name(context, argv[i], &p); + if (ret) { + krb5_warn(context, ret, "krb5_parse_name"); + break; + } + ret = copy_Principal(p, &a->aliases.val[a->aliases.len]); + krb5_free_principal(context, p); + if (ret == 0) + a->aliases.len++; + } + uniq(a); + + ext.data.element = choice_HDB_extension_data_aliases; + ext.mandatory = 0; + if (ret == 0) + ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length, &ext, &i, ret); + free_HDB_extension(&ext); + if (ret == 0) { + int16_t len = d.length; + + if (len < 0 || d.length != (size_t)len) { + krb5_warnx(context, "Too many aliases; does not fit in 32767 bytes"); + ret = EOVERFLOW; + } + } + if (ret == 0) { + add_tl(&princ, KRB5_TL_EXTENSION, &d); + krb5_data_zero(&d); + ret = kadm5_modify_principal(kadm_handle, &princ, + KADM5_PRINCIPAL | KADM5_TL_DATA); + if (ret) + krb5_warn(context, ret, "kadm5_modify_principal"); + } + + kadm5_free_principal_ent(kadm_handle, &princ); + krb5_data_free(&d); + return ret == 0 ? 0 : 1; +} -- cgit v1.2.3