From d8394c65b763e944e410046fe75f3c548a3e2f18 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Tue, 1 Jan 2019 17:25:06 -0600 Subject: [PATCH] Add new kadmin/ktutil --keep* and --enctypes opts - Add --keepold/keepallold/pruneall options to various kadmin/ktutil commands. Default behavior to "prune old keys". - When setting keys for a service, we need to specify enctypes for it: - Always use kadm5_randkey_principal_3() instead of the older kadm5_randkey_principal(). - Add krb5_string_to_keysalts2(), like MIT's krb5_string_to_keysalts(), but with a context, and simpler. - Add --enctypes options to various kadmin/ktutil commands. - Add [libdefaults] supported_enctypes param with enctype[:salttype] list. - Add [realms] realm supported_enctypes param with enctype[:salttype] list. Default to aes128-cts-hmac-sha1-96:normal. --- admin/change.c | 74 +++++++++++++---- admin/get.c | 123 +++++++++++++++++----------- admin/ktutil-commands.in | 54 +++++++++++- admin/ktutil.1 | 4 + kadmin/ank.c | 82 ++++++++++++------- kadmin/cpw.c | 24 +++++- kadmin/ext.c | 47 +++++++++-- kadmin/get.c | 8 +- kadmin/kadmin-commands.in | 47 ++++++++++- kadmin/kadmin.1 | 60 +++++++++++++- kadmin/kadmind.8 | 18 +++- kadmin/server.c | 79 ++++++++++-------- lib/kadm5/randkey_s.c | 8 ++ lib/kadm5/version-script-client.map | 1 + lib/krb5/libkrb5-exports.def.in | 1 + lib/krb5/salt.c | 63 ++++++++++++++ lib/krb5/version-script.map | 1 + tests/kdc/check-kadmin.in | 22 ++++- 18 files changed, 574 insertions(+), 142 deletions(-) diff --git a/admin/change.c b/admin/change.c index c390441f2..fef5f96e4 100644 --- a/admin/change.c +++ b/admin/change.c @@ -36,17 +36,23 @@ RCSID("$Id$"); static krb5_error_code -change_entry (krb5_keytab keytab, - krb5_principal principal, krb5_kvno kvno, - const char *realm, const char *admin_server, int server_port) +change_entry(krb5_keytab keytab, + krb5_principal principal, + krb5_kvno kvno, + int keep, + size_t nkstuple, + krb5_key_salt_tuple *kstuple, + const char *realm, + const char *admin_server, + int server_port) { krb5_error_code ret; kadm5_config_params conf; void *kadm_handle; char *client_name; krb5_keyblock *keys; + size_t i; int num_keys; - int i; ret = krb5_unparse_name (context, principal, &client_name); if (ret) { @@ -96,14 +102,15 @@ change_entry (krb5_keytab keytab, free (client_name); return ret; } - ret = kadm5_randkey_principal (kadm_handle, principal, &keys, &num_keys); - kadm5_destroy (kadm_handle); + ret = kadm5_randkey_principal_3(kadm_handle, principal, keep, nkstuple, + kstuple, &keys, &num_keys); + kadm5_destroy(kadm_handle); if (ret) { - krb5_warn(context, ret, "kadm5_randkey_principal: %s:", client_name); + krb5_warn(context, ret, "kadm5_randkey_principal_3: %s:", client_name); free (client_name); return ret; } - free (client_name); + free(client_name); for (i = 0; i < num_keys; ++i) { krb5_keytab_entry new_entry; @@ -131,16 +138,51 @@ struct change_set { }; int -kt_change (struct change_options *opt, int argc, char **argv) +kt_change(struct change_options *opt, int argc, char **argv) { krb5_error_code ret; krb5_keytab keytab; krb5_kt_cursor cursor; krb5_keytab_entry entry; - int i, j, max; + krb5_key_salt_tuple *kstuple = NULL; + const char *enctype; + size_t i, j, max, nkstuple; + int keep = 0; struct change_set *changeset; int errors = 0; + i = 0; + + if (opt->keepold_flag) { + keep = 1; + i++; + } + if (opt->keepallold_flag) { + keep = 2; + i++; + } + if (opt->pruneall_flag) { + keep = 0; + i++; + } + if (i > 1) { + fprintf(stderr, "use only one of --keepold, --keepallold, or --pruneall\n"); + return EINVAL; + } + + enctype = opt->enctype_string; + if (enctype == NULL || enctype[0] == '\0') + enctype = krb5_config_get_string(context, NULL, "libdefaults", + "supported_enctypes", NULL); + if (enctype == NULL || enctype[0] == '\0') + enctype = "aes128-cts-hmac-sha1-96"; + ret = krb5_string_to_keysalts2(context, enctype, &nkstuple, &kstuple); + if (ret) { + fprintf(stderr, "enctype(s) unknown\n"); + return ret; + } + + /* XXX Parameterize keytab name */ if((keytab = ktutil_open_keytab()) == NULL) return 1; @@ -232,11 +274,12 @@ kt_change (struct change_options *opt, int argc, char **argv) free(client_name); } } - ret = change_entry (keytab, - changeset[i].principal, changeset[i].kvno, - opt->realm_string, - opt->admin_server_string, - opt->server_port_integer); + ret = change_entry(keytab, + changeset[i].principal, changeset[i].kvno, + keep, nkstuple, kstuple, + opt->realm_string, + opt->admin_server_string, + opt->server_port_integer); if (ret != 0) errors = 1; } @@ -247,6 +290,7 @@ kt_change (struct change_options *opt, int argc, char **argv) free (changeset); out: + free(kstuple); krb5_kt_close(context, keytab); return errors; } diff --git a/admin/get.c b/admin/get.c index df294324b..e16a46731 100644 --- a/admin/get.c +++ b/admin/get.c @@ -82,45 +82,84 @@ open_kadmin_connection(char *principal, return kadm_handle; } +static int +parse_enctypes(struct get_options *opt, + size_t *nks, + krb5_key_salt_tuple **ks) +{ + const char *str; + char *s = NULL; + char *tmp; + size_t i; + int ret; + + *nks = 0; + *ks = NULL; + if (opt->enctypes_strings.num_strings == 0) { + str = krb5_config_get_string(context, NULL, "libdefaults", + "supported_enctypes", NULL); + if (str == NULL) + str = "aes128-cts-hmac-sha1-96"; + return krb5_string_to_keysalts2(context, str, nks, ks); + } + + for (i = 0; i < opt->enctypes_strings.num_strings; i++) { + if (asprintf(&tmp, "%s%s%s", i ? s : "", i ? "," : "", + opt->enctypes_strings.strings[i]) == -1) { + free(s); + return krb5_enomem(context); + } + s = tmp; + } + ret = krb5_string_to_keysalts2(context, s, nks, ks); + free(s); + return ret; +} + int kt_get(struct get_options *opt, int argc, char **argv) { krb5_error_code ret = 0; krb5_keytab keytab; void *kadm_handle = NULL; - krb5_enctype *etypes = NULL; - size_t netypes = 0; + krb5_key_salt_tuple *ks = NULL; + size_t nks; size_t i; - int a, j; + int a, j, keep; unsigned int failed = 0; - if((keytab = ktutil_open_keytab()) == NULL) + i = 0; + keep = 0; + if (opt->keepallold_flag) { + keep = 2; + i++; + } + if (opt->keepold_flag) { + keep = 1; + i++; + } + if (opt->pruneall_flag) { + keep = 0; + i++; + } + if (i > 1) { + fprintf(stderr, "use only one of --keepold, --keepallold, or --pruneall\n"); + return EINVAL; + } + + if ((ret = parse_enctypes(opt, &nks, &ks))) { + fprintf(stderr, "invalid enctype(s)\n"); + return ret; + } + + if((keytab = ktutil_open_keytab()) == NULL) { + free(ks); return 1; + } if(opt->realm_string) krb5_set_default_realm(context, opt->realm_string); - if (opt->enctypes_strings.num_strings != 0) { - - etypes = malloc (opt->enctypes_strings.num_strings * sizeof(*etypes)); - if (etypes == NULL) { - krb5_warnx(context, "malloc failed"); - goto out; - } - netypes = opt->enctypes_strings.num_strings; - for(i = 0; i < netypes; i++) { - ret = krb5_string_to_enctype(context, - opt->enctypes_strings.strings[i], - &etypes[i]); - if(ret) { - krb5_warnx(context, "unrecognized enctype: %s", - opt->enctypes_strings.strings[i]); - goto out; - } - } - } - - for(a = 0; a < argc; a++){ krb5_principal princ_ent; kadm5_principal_ent_rec princ; @@ -167,7 +206,8 @@ kt_get(struct get_options *opt, int argc, char **argv) failed++; continue; } - ret = kadm5_randkey_principal(kadm_handle, princ_ent, &keys, &n_keys); + ret = kadm5_randkey_principal_3(kadm_handle, princ_ent, keep, nks, ks, + &keys, &n_keys); if (ret) { krb5_warn(context, ret, "kadm5_randkey_principal(%s)", argv[a]); krb5_free_principal(context, princ_ent); @@ -203,37 +243,22 @@ kt_get(struct get_options *opt, int argc, char **argv) continue; } for(j = 0; j < n_keys; j++) { - int do_add = TRUE; - - if (netypes) { - size_t k; - - do_add = FALSE; - for (k = 0; k < netypes; ++k) - if (keys[j].keytype == etypes[k]) { - do_add = TRUE; - break; - } - } - if (do_add) { - entry.principal = princ_ent; - entry.vno = princ.kvno; - entry.keyblock = keys[j]; - entry.timestamp = time (NULL); - ret = krb5_kt_add_entry(context, keytab, &entry); - if (ret) - krb5_warn(context, ret, "krb5_kt_add_entry"); - } + entry.principal = princ_ent; + entry.vno = princ.kvno; + entry.keyblock = keys[j]; + entry.timestamp = time (NULL); + ret = krb5_kt_add_entry(context, keytab, &entry); + if (ret) + krb5_warn(context, ret, "krb5_kt_add_entry"); krb5_free_keyblock_contents(context, &keys[j]); } kadm5_free_principal_ent(kadm_handle, &princ); krb5_free_principal(context, princ_ent); } - out: - free(etypes); if (kadm_handle) kadm5_destroy(kadm_handle); krb5_kt_close(context, keytab); + free(ks); return ret != 0 || failed > 0; } diff --git a/admin/ktutil-commands.in b/admin/ktutil-commands.in index 8bae7ab00..2b771e931 100644 --- a/admin/ktutil-commands.in +++ b/admin/ktutil-commands.in @@ -54,7 +54,7 @@ command = { short = "e" type = "string" argument = "enctype" - help = "encryption type" + help = "encryption type(s)" } option = { long = "password" @@ -75,6 +75,21 @@ command = { type = "flag" help = "generate random key" } + option = { + long = "keepold" + type = "flag" + help = "keep old keys/password needed to decrypt extant tickets (default)" + } + option = { + long = "keepallold" + type = "flag" + help = "keep all old keys/password" + } + option = { + long = "pruneall" + type = "flag" + help = "delete all old keys" + } option = { long = "hex" short = "H" @@ -94,6 +109,28 @@ command = { argument = "realm" help = "realm to use" } + option = { + long = "enctype" + short = "e" + type = "string" + argument = "enctype" + help = "encryption type(s)" + } + option = { + long = "keepold" + type = "flag" + help = "keep old keys/password needed to decrypt extant tickets (default)" + } + option = { + long = "keepallold" + type = "flag" + help = "keep all old keys/password" + } + option = { + long = "pruneall" + type = "flag" + help = "delete all old keys" + } option = { long = "admin-server" short = "a" @@ -136,6 +173,21 @@ command = { help = "encryption types to use" argument = "enctype" } + option = { + long = "keepold" + type = "flag" + help = "keep old keys/password needed to decrypt extant tickets (default)" + } + option = { + long = "keepallold" + type = "flag" + help = "keep all old keys/password" + } + option = { + long = "pruneall" + type = "flag" + help = "delete all old keys" + } option = { long = "realm" short = "r" diff --git a/admin/ktutil.1 b/admin/ktutil.1 index a90541991..125b5e8f0 100644 --- a/admin/ktutil.1 +++ b/admin/ktutil.1 @@ -62,6 +62,7 @@ can be one of the following: .Bl -tag -width srvconvert .It add Oo Fl p Ar principal Oc Oo Fl Fl principal= Ns Ar principal Oc \ Oo Fl V Ar kvno Oc Oo Fl Fl kvno= Ns Ar kvno Oc Oo Fl e Ar enctype Oc \ +Oo Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall Oc \ Oo Fl Fl enctype= Ns Ar enctype Oc Oo Fl w Ar password Oc \ Oo Fl Fl password= Ns Ar password Oc Oo Fl r Oc Oo Fl Fl random Oc \ Oo Fl s Oc Oo Fl Fl no-salt Oc Oo Fl H Oc Op Fl Fl hex @@ -72,6 +73,8 @@ the keytab, you should consider the .Ar get command, which talks to the kadmin server. .It change Oo Fl r Ar realm Oc Oo Fl Fl realm= Ns Ar realm Oc \ +Oo Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall Oc \ +Oo Fl Fl enctype= Ns Ar enctype Oc \ Oo Fl Fl a Ar host Oc Oo Fl Fl admin-server= Ns Ar host Oc \ Oo Fl Fl s Ar port Oc Op Fl Fl server-port= Ns Ar port Update one or several keys to new versions. By default, use the admin @@ -86,6 +89,7 @@ to .Ar keytab-dest . .It get Oo Fl p Ar admin principal Oc \ Oo Fl Fl principal= Ns Ar admin principal Oc Oo Fl e Ar enctype Oc \ +Oo Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall Oc \ Oo Fl Fl enctypes= Ns Ar enctype Oc Oo Fl r Ar realm Oc \ Oo Fl Fl realm= Ns Ar realm Oc Oo Fl a Ar admin server Oc \ Oo Fl Fl admin-server= Ns Ar admin server Oc Oo Fl s Ar server port Oc \ diff --git a/kadmin/ank.c b/kadmin/ank.c index 648e1ef1d..ee36a77ba 100644 --- a/kadmin/ank.c +++ b/kadmin/ank.c @@ -63,18 +63,20 @@ get_default (kadm5_server_context *contextp, */ static krb5_error_code -add_one_principal (const char *name, - int rand_key, - int rand_password, - int use_defaults, - char *password, - char *policy, - krb5_key_data *key_data, - const char *max_ticket_life, - const char *max_renewable_life, - const char *attributes, - const char *expiration, - const char *pw_expiration) +add_one_principal(const char *name, + int rand_key, + int rand_password, + int use_defaults, + char *password, + char *policy, + size_t nkstuple, + krb5_key_salt_tuple *kstuple, + krb5_key_data *key_data, + const char *max_ticket_life, + const char *max_renewable_life, + const char *attributes, + const char *expiration, + const char *pw_expiration) { krb5_error_code ret; kadm5_principal_ent_rec princ, defrec; @@ -157,11 +159,11 @@ add_one_principal (const char *name, } /* Save requested password expiry before it's clobbered */ pw_expire = princ.pw_expiration; - if(rand_key) { + if (rand_key) { krb5_keyblock *new_keys; int n_keys, i; - ret = kadm5_randkey_principal(kadm_handle, princ_ent, - &new_keys, &n_keys); + ret = kadm5_randkey_principal_3(kadm_handle, princ_ent, 0, + nkstuple, kstuple, &new_keys, &n_keys); if(ret){ krb5_warn(context, ret, "kadm5_randkey_principal"); n_keys = 0; @@ -231,10 +233,12 @@ int add_new_key(struct add_options *opt, int argc, char **argv) { krb5_error_code ret = 0; - int i; - int num; + krb5_key_salt_tuple *kstuple = NULL; krb5_key_data key_data[3]; krb5_key_data *kdp = NULL; + const char *enctypes; + size_t i, nkstuple; + int num; num = 0; if (opt->random_key_flag) @@ -252,30 +256,46 @@ add_new_key(struct add_options *opt, int argc, char **argv) return 1; } + enctypes = opt->enctypes_string; + if (enctypes == NULL || enctypes[0] == '\0') + enctypes = krb5_config_get_string(context, NULL, "libdefaults", + "supported_enctypes", NULL); + if (enctypes == NULL || enctypes[0] == '\0') + enctypes = "aes128-cts-hmac-sha1-96"; + ret = krb5_string_to_keysalts2(context, enctypes, &nkstuple, &kstuple); + if (ret) { + fprintf(stderr, "enctype(s) unknown\n"); + return ret; + } + + if (opt->key_string) { const char *error; if (parse_des_key (opt->key_string, key_data, &error)) { - fprintf (stderr, "failed parsing key \"%s\": %s\n", - opt->key_string, error); + fprintf(stderr, "failed parsing key \"%s\": %s\n", + opt->key_string, error); + free(kstuple); return 1; } kdp = key_data; } for(i = 0; i < argc; i++) { - ret = add_one_principal (argv[i], - opt->random_key_flag, - opt->random_password_flag, - opt->use_defaults_flag, - opt->password_string, - opt->policy_string, - kdp, - opt->max_ticket_life_string, - opt->max_renewable_life_string, - opt->attributes_string, - opt->expiration_time_string, - opt->pw_expiration_time_string); + ret = add_one_principal(argv[i], + opt->random_key_flag, + opt->random_password_flag, + opt->use_defaults_flag, + opt->password_string, + opt->policy_string, + nkstuple, + kstuple, + kdp, + opt->max_ticket_life_string, + opt->max_renewable_life_string, + opt->attributes_string, + opt->expiration_time_string, + opt->pw_expiration_time_string); if (ret) { krb5_warn (context, ret, "adding %s", argv[i]); break; diff --git a/kadmin/cpw.c b/kadmin/cpw.c index 8a4a0b497..041f24255 100644 --- a/kadmin/cpw.c +++ b/kadmin/cpw.c @@ -148,12 +148,34 @@ cpw_entry(struct passwd_options *opt, int argc, char **argv) int num; krb5_key_data key_data[3]; - data.keepold = opt->keepold_flag; data.random_key = opt->random_key_flag; data.random_password = opt->random_password_flag; data.password = opt->password_string; data.key_data = NULL; + /* + * --keepold is the the default, and it should mean "prune all old keys not + * needed to decrypt extant tickets". + */ + num = 0; + data.keepold = 0; + if (opt->keepold_flag) { + data.keepold = 1; + num++; + } + if (opt->keepallold_flag) { + data.keepold = 2; + num++; + } + if (opt->pruneall_flag) { + data.keepold = 0; + num++; + } + if (num > 1) { + fprintf(stderr, "use only one of --keepold, --keepallold, and --pruneall\n"); + return 1; + } + num = 0; if (data.random_key) ++num; diff --git a/kadmin/ext.c b/kadmin/ext.c index 32e3a12f6..5e2bd79ab 100644 --- a/kadmin/ext.c +++ b/kadmin/ext.c @@ -36,7 +36,10 @@ struct ext_keytab_data { krb5_keytab keytab; + int keep; int random_key_flag; + size_t nkstuple; + krb5_key_salt_tuple *kstuple; }; static int @@ -109,7 +112,8 @@ do_ext_keytab(krb5_principal principal, void *data) n_k++; } } else if (e->random_key_flag) { - ret = kadm5_randkey_principal(kadm_handle, principal, &k, &n_k); + ret = kadm5_randkey_principal_3(kadm_handle, principal, e->keep, + e->nkstuple, e->kstuple, &k, &n_k); if (ret) goto out; @@ -151,8 +155,30 @@ int ext_keytab(struct ext_keytab_options *opt, int argc, char **argv) { krb5_error_code ret; - int i; struct ext_keytab_data data; + const char *enctypes; + size_t i; + + data.random_key_flag = opt->random_key_flag; + data.keep = 0; + i = 0; + if (opt->keepallold_flag) { + data.keep = 2; + i++; + } + if (opt->keepold_flag) { + data.keep = 1; + i++; + } + if (opt->pruneall_flag) { + data.keep = 1; + i++; + } + if (i > 1) { + fprintf(stderr, + "use only one of --keepold, --keepallold, or --pruneall\n"); + return EINVAL; + } if (opt->keytab_string == NULL) ret = krb5_kt_default(context, &data.keytab); @@ -163,8 +189,19 @@ ext_keytab(struct ext_keytab_options *opt, int argc, char **argv) krb5_warn(context, ret, "krb5_kt_resolve"); return 1; } - - data.random_key_flag = opt->random_key_flag; + enctypes = opt->enctypes_string; + if (enctypes == NULL || enctypes[0] == '\0') + enctypes = krb5_config_get_string(context, NULL, "libdefaults", + "supported_enctypes", NULL); + if (enctypes == NULL || enctypes[0] == '\0') + enctypes = "aes128-cts-hmac-sha1-96"; + ret = krb5_string_to_keysalts2(context, enctypes, &data.nkstuple, + &data.kstuple); + if (ret) { + fprintf(stderr, "enctype(s) unknown\n"); + krb5_kt_close(context, data.keytab); + return ret; + } for(i = 0; i < argc; i++) { ret = foreach_principal(argv[i], do_ext_keytab, "ext", &data); @@ -173,6 +210,6 @@ ext_keytab(struct ext_keytab_options *opt, int argc, char **argv) } krb5_kt_close(context, data.keytab); - + free(data.kstuple); return ret != 0; } diff --git a/kadmin/get.c b/kadmin/get.c index 802b65dc5..9d9678043 100644 --- a/kadmin/get.c +++ b/kadmin/get.c @@ -534,9 +534,15 @@ get_entry(struct get_options *opt, int argc, char **argv) int list_princs(struct list_options *opt, int argc, char **argv) { + struct get_options get_opt; + if(sizeof(struct get_options) != sizeof(struct list_options)) { krb5_warnx(context, "programmer error: sizeof(struct get_options) != sizeof(struct list_options)"); return 0; } - return getit((struct get_options*)opt, "list", argc, argv); + get_opt.long_flag = opt->long_flag; + get_opt.short_flag = opt->short_flag; + get_opt.terse_flag = opt->terse_flag; + get_opt.column_info_string = opt->column_info_string; + return getit(&get_opt, "list", argc, argv); } diff --git a/kadmin/kadmin-commands.in b/kadmin/kadmin-commands.in index 2966b05d2..5bc897e0b 100644 --- a/kadmin/kadmin-commands.in +++ b/kadmin/kadmin-commands.in @@ -139,6 +139,12 @@ command = { type = "flag" help = "set random password" } + option = { + long = "enctypes" + short = "e" + type = "string" + help = "encryption type(s)" + } option = { long = "password" short = "p" @@ -225,6 +231,12 @@ command = { type = "flag" help = "set random password" } + option = { + long = "enctypes" + short = "e" + type = "string" + help = "encryption type(s)" + } option = { long = "password" short = "p" @@ -239,7 +251,17 @@ command = { option = { long = "keepold" type = "flag" - help = "keep old keys/password" + help = "keep old keys/password needed to decrypt extant tickets (default)" + } + option = { + long = "keepallold" + type = "flag" + help = "keep all old keys/password" + } + option = { + long = "pruneall" + type = "flag" + help = "delete all old keys" } argument = "principal..." min_args = "1" @@ -286,6 +308,27 @@ command = { type = "flag" help = "set random key" } + option = { + long = "enctypes" + short = "e" + type = "string" + help = "encryption type(s)" + } + option = { + long = "keepold" + type = "flag" + help = "keep old keys/password needed to decrypt extant tickets (default)" + } + option = { + long = "keepallold" + type = "flag" + help = "keep all old keys/password" + } + option = { + long = "pruneall" + type = "flag" + help = "delete all old keys" + } argument = "principal..." min_args = "1" help = "Extracts the keys of all principals matching the expressions, and stores them in a keytab." @@ -294,7 +337,7 @@ command = { name = "get" name = "get_entry" function = "get_entry" - /* XXX sync options with "list" */ + /* Options added to list should be added here; not the reverse */ option = { long = "long" short = "l" diff --git a/kadmin/kadmin.1 b/kadmin/kadmin.1 index 9390594a6..1f8650eea 100644 --- a/kadmin/kadmin.1 +++ b/kadmin/kadmin.1 @@ -102,6 +102,7 @@ Commands include: .Pp .Nm add .Op Fl r | Fl Fl random-key +.Op Fl Fl enctypes= Ns Ar string .Op Fl Fl random-password .Op Fl p Ar string \*(Ba Fl Fl password= Ns Ar string .Op Fl Fl key= Ns Ar string @@ -115,6 +116,24 @@ Commands include: .Bd -ragged -offset indent Adds a new principal to the database. The options not passed on the command line will be promped for. +If enctypes to use are not given, then the +.Ar [libdefaults] supported_enctypes +configuration parameter will be used on the client side to select +enctypes, defaulting to +.Ar aes128-cts-hmac-sha1-96. +For compatibility with MIT, the enctypes string is a space- or +comma-separated list of enctype:salttype. +If +.Fl Fl keepold +is given, then old keys needed to decrypt extant tickets are +kept, and all other old keys are deleted. +If +.Fl Fl keepallold +is given then all old keys are kept. If +.Fl Fl pruneall is given then all old keys are removed. +The +.Fl Fl keepold +behavior is the default if none of these are given. The only policy supported by Heimdal servers is .Ql default . .Ed @@ -157,6 +176,8 @@ principals, those are not consulted here. .Pp .Nm ext_keytab .Oo Fl k Ar string \*(Ba Xo +.Op Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall +.Op Fl Fl enctypes= Ns Ar string .Fl Fl keytab= Ns Ar string .Xc .Oc @@ -165,6 +186,24 @@ principals, those are not consulted here. Creates a keytab with the keys of the specified principals. Requires get-keys rights, otherwise the principal's keys are changed and saved in the keytab. +If enctypes to use are not given, then the +.Ar [libdefaults] supported_enctypes +configuration parameter will be used on the client side to select +enctypes, defaulting to +.Ar aes128-cts-hmac-sha1-96. +For compatibility with MIT, the enctypes string is a space- or +comma-separated list of enctype:salttype. +If +.Fl Fl keepold +is given, then old keys needed to decrypt extant tickets are +kept, and all other old keys are deleted. +If +.Fl Fl keepallold +is given then all old keys are kept. If +.Fl Fl pruneall is given then all old keys are removed. +The +.Fl Fl keepold +behavior is the default if none of these are given. .Ed .Pp .Nm get @@ -250,7 +289,8 @@ kadmin -l modify -a -disallow-proxiable user .Ed .Pp .Nm passwd -.Op Fl Fl keepold +.Op Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall +.Op Fl Fl enctypes= Ns Ar string .Op Fl r | Fl Fl random-key .Op Fl Fl random-password .Oo Fl p Ar string \*(Ba Xo @@ -261,6 +301,24 @@ kadmin -l modify -a -disallow-proxiable user .Ar principal... .Bd -ragged -offset indent Changes the password of an existing principal. +If enctypes to use are not given, then the +.Ar [libdefaults] supported_enctypes +configuration parameter will be used on the client side to select +enctypes, defaulting to +.Ar aes128-cts-hmac-sha1-96. +For compatibility with MIT, the enctypes string is a space- or +comma-separated list of enctype:salttype. +If +.Fl Fl keepold +is given, then old keys needed to decrypt extant tickets are +kept, and all other old keys are deleted. +If +.Fl Fl keepallold +is given then all old keys are kept. If +.Fl Fl pruneall is given then all old keys are removed. +The +.Fl Fl keepold +behavior is the default if none of these are given. .Ed .Pp .Nm password-quality diff --git a/kadmin/kadmind.8 b/kadmin/kadmind.8 index f66615932..acda0c5dc 100644 --- a/kadmin/kadmind.8 +++ b/kadmin/kadmind.8 @@ -155,7 +155,23 @@ keytabs). joe/admin@EXAMPLE.COM all mallory/admin@EXAMPLE.COM add,get-keys host/*@EXAMPLE.COM .Ed -.\".Sh DIAGNOSTICS +.Sh CONFIGURATION FILE +kadmind uses the following configuration parameters from the +.Ar [kadmin] +section of +.Ar krb5.conf: +.Bl -tag -width Ds -offset indent +.It password_lifetime +.El +.Pp +kadmind uses the following configuration parameters from the per-realm entries +in the +.Ar [realms] +section of +.Ar krb5.conf: +.Bl -tag -width Ds -offset indent +.It supported_enctypes +.El .Sh SEE ALSO .Xr kpasswd 1 , .Xr kadmin 1 , diff --git a/kadmin/server.c b/kadmin/server.c index 75d91dba3..c5b1b7c36 100644 --- a/kadmin/server.c +++ b/kadmin/server.c @@ -448,9 +448,11 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, break; } case kadm_randkey:{ + size_t i; + op = "RANDKEY"; ret = krb5_ret_principal(sp, &princ); - if(ret) + if (ret) goto fail; krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); @@ -483,39 +485,49 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, } ret = krb5_ret_int32(sp, &n_ks_tuple); - if (ret != 0 && ret != HEIM_ERR_EOF) { + if (ret == HEIM_ERR_EOF) { + const char *enctypes; + + enctypes = krb5_config_get_string(context, NULL, "realms", + krb5_principal_get_realm(context, + princ), + "supported_enctypes", NULL); + if (enctypes == NULL || enctypes[0] == '\0') + enctypes = "aes128-cts-hmac-sha1-96"; + ret = krb5_string_to_keysalts2(context, enctypes, &n_ks_tuple, + &ks_tuple); + } + if (ret != 0) { krb5_free_principal(contextp->context, princ); goto fail; - } else if (ret == 0) { - size_t i; - - if (n_ks_tuple < 0) { - ret = EOVERFLOW; - krb5_free_principal(contextp->context, princ); - goto fail; - } - - if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) { - ret = errno; - krb5_free_principal(contextp->context, princ); - goto fail; - } - - for (i = 0; i < n_ks_tuple; i++) { - ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype); - if (ret != 0) { - krb5_free_principal(contextp->context, princ); - free(ks_tuple); - goto fail; - } - ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype); - if (ret != 0) { - krb5_free_principal(contextp->context, princ); - free(ks_tuple); - goto fail; - } - } } + + if (n_ks_tuple < 0) { + ret = EOVERFLOW; + krb5_free_principal(contextp->context, princ); + goto fail; + } + + if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) { + ret = errno; + krb5_free_principal(contextp->context, princ); + goto fail; + } + + for (i = 0; i < n_ks_tuple; i++) { + ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype); + if (ret != 0) { + krb5_free_principal(contextp->context, princ); + free(ks_tuple); + goto fail; + } + ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype); + if (ret != 0) { + krb5_free_principal(contextp->context, princ); + free(ks_tuple); + goto fail; + } + } ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold, n_ks_tuple, ks_tuple, &new_keys, &n_keys); @@ -525,10 +537,9 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); - if(ret == 0){ - int i; + if (ret == 0){ krb5_store_int32(sp, n_keys); - for(i = 0; i < n_keys; i++){ + for (i = 0; i < n_keys; i++){ if (ret == 0) ret = krb5_store_keyblock(sp, new_keys[i]); krb5_free_keyblock_contents(contextp->context, &new_keys[i]); diff --git a/lib/kadm5/randkey_s.c b/lib/kadm5/randkey_s.c index 83fce48d1..cb96f638d 100644 --- a/lib/kadm5/randkey_s.c +++ b/lib/kadm5/randkey_s.c @@ -103,6 +103,14 @@ kadm5_s_randkey_principal(void *server_handle, if (keepold) { ret = hdb_add_current_keys_to_history(context->context, &ent.entry); + if (ret == 0 && keepold == 1) + ret = hdb_prune_keys_kvno(context, &ent.entry, 0); + if (ret) + goto out3; + } else { + /* Remove all key history */ + ret = hdb_clear_extension(context, &ent.entry, + choice_HDB_extension_data_hist_keys); if (ret) goto out3; } diff --git a/lib/kadm5/version-script-client.map b/lib/kadm5/version-script-client.map index c45d1e7f9..4d89fabf3 100644 --- a/lib/kadm5/version-script-client.map +++ b/lib/kadm5/version-script-client.map @@ -45,6 +45,7 @@ HEIMDAL_KADM5_CLIENT_1.0 { kadm5_init_with_skey_ctx; kadm5_modify_principal; kadm5_randkey_principal; + kadm5_randkey_principal_3; kadm5_rename_principal; kadm5_ret_key_data; kadm5_ret_principal_ent; diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index 46de64a1f..fdfb06189 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -653,6 +653,7 @@ EXPORTS krb5_string_to_key_derived krb5_string_to_key_salt krb5_string_to_key_salt_opaque + krb5_string_to_keysalts2 krb5_string_to_keytype krb5_string_to_salttype krb5_ticket_get_authorization_data_type diff --git a/lib/krb5/salt.c b/lib/krb5/salt.c index a3e850fcb..c8dbb520b 100644 --- a/lib/krb5/salt.c +++ b/lib/krb5/salt.c @@ -91,6 +91,69 @@ krb5_string_to_salttype (krb5_context context, return HEIM_ERR_SALTTYPE_NOSUPP; } +/* + * Like MIT's krb5_string_to_keysalts(), but simpler and with a context + * argument. + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_string_to_keysalts2(krb5_context context, const char *string, + size_t *nksaltp, krb5_key_salt_tuple **ksaltp) +{ + /* deleted: tupleseps, ksaltseps, dups */ + krb5_key_salt_tuple *tmp = NULL; + krb5_error_code ret = 0; + char *copy, *token, *stype_str; + char *lasts = NULL; + krb5_enctype etype; + krb5_salttype stype; + size_t i; + + *ksaltp = NULL; + *nksaltp = 0; + if ((copy = strdup(string)) == NULL) + return krb5_enomem(context); + for (token = strtok_r(copy, ", \t", &lasts), ret = 0; + token != NULL; + token = strtok_r(NULL, ", \t", &lasts)) { + if ((stype_str = strchr(token, ':')) != NULL) + *(stype_str++) = '\0'; + if ((ret = krb5_string_to_enctype(context, token, &etype))) + continue; + if (stype_str == NULL) + stype = KRB5_PW_SALT; + else if ((ret = krb5_string_to_salttype(context, etype, stype_str, &stype))) + continue; + for (i = 0; i < *nksaltp; i++) { + if ((*ksaltp)[i].ks_enctype == etype && + (*ksaltp)[i].ks_salttype == stype) + goto skip; + } + tmp = realloc(*ksaltp, ((*nksaltp) + 1) * sizeof(**ksaltp)); + if (tmp == NULL) { + ret = krb5_enomem(context); + break; + } + *ksaltp = tmp; + (*ksaltp)[*nksaltp].ks_enctype = etype; + (*ksaltp)[*nksaltp].ks_salttype = stype; + (*nksaltp)++; +skip: + (void)1; + } + free(copy); + if (ret == ENOMEM) { + free(*ksaltp); + *nksaltp = 0; + *ksaltp = NULL; + } else if (*nksaltp) { + return 0; + } else if (ret == 0) { + return KRB5_PROG_ETYPE_NOSUPP; + } + return ret; +} + + KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_pw_salt(krb5_context context, krb5_const_principal principal, diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index bdaa4284d..94df630a0 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -646,6 +646,7 @@ HEIMDAL_KRB5_2.0 { krb5_string_to_key_derived; krb5_string_to_key_salt; krb5_string_to_key_salt_opaque; + krb5_string_to_keysalts2; krb5_string_to_keytype; krb5_string_to_salttype; krb5_ticket_get_authorization_data_type; diff --git a/tests/kdc/check-kadmin.in b/tests/kdc/check-kadmin.in index 6f488fce9..3d3f41003 100644 --- a/tests/kdc/check-kadmin.in +++ b/tests/kdc/check-kadmin.in @@ -87,9 +87,12 @@ ${kadmin} -l add -p "$foopassword" --use-defaults fez@${R} || exit 1 ${kadmin} -l add -p "$foopassword" --use-defaults hasalias@${R} || exit 1 ${kadmin} -l add -p "$foopassword" --use-defaults pkinit@${R} || exit 1 ${kadmin} -l modify --pkinit-acl="CN=baz,DC=test,DC=h5l,DC=se" pkinit@${R} || exit 1 -${kadmin} -l add -p foo --use-defaults prune@${R} || exit 1 +${kadmin} -l add -p "$foopassword" --use-defaults prune@${R} || exit 1 ${kadmin} -l cpw --keepold --random-key prune@${R} || exit 1 ${kadmin} -l cpw --keepold --random-key prune@${R} || exit 1 +${kadmin} -l add -p "$foopassword" --use-defaults pruneall@${R} || exit 1 +${kadmin} -l cpw --pruneall --random-key pruneall@${R} || exit 1 +${kadmin} -l cpw --pruneall --random-key pruneall@${R} || exit 1 echo "$foopassword" > ${objdir}/foopassword @@ -382,6 +385,23 @@ cat kadmin.tmp | ${EGREP} Keytypes: | cut -d: -f2 | tr ' ' ' ' | ${EGREP} '^13$' > /dev/null || \ { echo "kadmin prune failed $?"; cat messages.log ; exit 1; } +#---------------------------------- +${kadmind} -d & +kadmpid=$! +sleep 1 + +echo "kadmin pruneall" +env KRB5CCNAME=${cache} \ +${kadmin} get pruneall@${R} \ + > kadmin.tmp 2>&1 || \ + { echo "kadmin failed $?"; cat messages.log ; exit 1; } +wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; } + +cat kadmin.tmp | ${EGREP} Keytypes: | cut -d: -f2 | tr ' ' ' +' | sed 's/^.*[[]\(.*\)[]].*$/\1/' | grep '[0-9]' | sort -nu | tr -d ' +' | ${EGREP} '^3$' > /dev/null || \ + { echo "kadmin pruneall failed $?"; cat messages.log ; exit 1; } + #---------------------------------- echo "killing kdc (${kdcpid} ${kadmpid})"