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.
This commit is contained in:
Nicolas Williams
2019-01-01 17:25:06 -06:00
committed by Nico Williams
parent 7b76d6719f
commit d8394c65b7
18 changed files with 574 additions and 142 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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"

View File

@@ -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

View File

@@ -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 ,

View File

@@ -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]);