kadmin: Add add_alias, del_alias

This commit is contained in:
Nicolas Williams
2021-10-06 21:59:43 -05:00
parent decd8f4102
commit ba98690a0a
7 changed files with 378 additions and 5 deletions

View File

@@ -97,3 +97,117 @@ del_namespace(void *opt, int argc, char **argv)
}
return ret != 0;
}
int
del_alias(void *opt, int argc, char **argv)
{
krb5_error_code ret;
size_t i;
if (argc < 1) {
krb5_warnx(context, "No aliases given");
return 1;
}
for (; argc; argc--, argv++) {
kadm5_principal_ent_rec princ;
krb5_principal p;
HDB_Ext_Aliases *a;
HDB_extension ext;
krb5_tl_data *tl;
krb5_data d;
if ((ret = krb5_parse_name(context, argv[0], &p))) {
krb5_warn(context, ret, "Invalid principal: %s", argv[0]);
return 1;
}
memset(&princ, 0, sizeof(princ));
ret = kadm5_get_principal(kadm_handle, p, &princ,
KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA);
if (ret) {
krb5_warn(context, ret, "Principal alias not found %s", argv[0]);
continue;
}
if (krb5_principal_compare(context, p, princ.principal)) {
krb5_warn(context, ret, "Not deleting principal %s because it is "
"not an alias; use 'delete' to delete the principal",
argv[0]);
continue;
}
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)) == NULL) {
krb5_warnx(context, "kadm5_get_principal() found principal %s but "
"not its aliases", argv[0]);
kadm5_free_principal_ent(kadm_handle, &princ);
krb5_free_principal(context, p);
return 1;
}
ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, tl->tl_data_length,
a, NULL);
if (ret) {
krb5_warn(context, ret, "Principal alias list could not be decoded");
kadm5_free_principal_ent(kadm_handle, &princ);
krb5_free_principal(context, p);
return 1;
}
/*
* Remove alias, but also, don't assume it appears only once in aliases
* list.
*/
i = 0;
while (i < a->aliases.len) {
if (!krb5_principal_compare(context, p, &a->aliases.val[i])) {
i++;
continue;
}
free_Principal(&a->aliases.val[i]);
if (i + 1 < a->aliases.len)
memmove(&a->aliases.val[i],
&a->aliases.val[i + 1],
sizeof(a->aliases.val[i]) * (a->aliases.len - (i + 1)));
if (a->aliases.len)
a->aliases.len--;
continue;
}
krb5_data_zero(&d);
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_Ext_Aliases(a);
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;
} else {
add_tl(&princ, KRB5_TL_EXTENSION, &d);
krb5_data_zero(&d);
}
}
if (ret == 0) {
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_free_principal(context, p);
krb5_data_free(&d);
p = NULL;
}
return ret == 0 ? 0 : 1;
}

View File

@@ -609,6 +609,20 @@ command = {
max_args = "1"
help = "Modifies some attributes of the specified principal."
}
command = {
name = "add_alias"
function = "add_alias"
argument = "principal"
min_args = "2"
help = "Add one or more aliases to the given principal."
}
command = {
name = "del_alias"
function = "del_alias"
argument = "principal"
min_args = "1"
help = "Delete one or more aliases without deleting their canonical principals."
}
command = {
name = "prune"
argument = "principal"

View File

@@ -140,6 +140,37 @@ The only policy supported by Heimdal servers is
.Ql default .
.Ed
.Pp
.Nm add_alias
.Ar principal
.Ar alias...
.Bd -ragged -offset indent
Adds one or more aliases to the given principal.
.Pp
When a client requests a service ticket for a service principal
name that is an alias of a principal in a different realm, the
TGS will return a referral to that realm.
This compares favorably to using
.Ar [domain_realm]
entries in the KDC's
.Ar krb5.conf ,
but may be managed via the
.Nm kadmin
command and its
.Nm add_alias
and
.Nm del_alias
sub-commands rather than having to edit the KDC's configuration
file and having to restart the KDC.
.Pp
However, there is currently no way to alias namespaces via HDB
entry aliases.
To issue referrals for entire namespaces use the
.Ar [domain_realm]
section of the KDC's
.Ar krb5.conf
file.
.Ed
.Pp
.Nm add_namespace
.Ar Fl Fl key-rotation-epoch= Ns Ar time
.Ar Fl Fl key-rotation-period= Ns Ar time
@@ -202,6 +233,19 @@ supported.
.Ar principal...
.Bd -ragged -offset indent
Removes a principal.
It is an error to delete an alias.
To remove a principal's alias or aliases, use the
.Nm del_alias
command.
To remove a principal given an alias, first
.Nm get
the principal to get its canonical name and then delete that.
.Ed
.Pp
.Nm del_alias
.Ar alias...
.Bd -ragged -offset indent
Deletes the given aliases, but not their canonical principals.
.Ed
.Pp
.Nm del_enctype
@@ -320,6 +364,7 @@ and
.Op Fl Fl pw-expiration-time= Ns Ar time
.Op Fl Fl kvno= Ns Ar number
.Op Fl Fl policy= Ns Ar policy-name
.Op Fl Fl alias= Ns Ar alias-name
.Op Fl C Ar path | Fl Fl krb5-config-file= Ns Ar path
.Ar principal...
.Bd -ragged -offset indent
@@ -327,6 +372,23 @@ Modifies certain attributes of a principal. If run without command
line options, you will be prompted. With command line options, it will
only change the ones specified.
.Pp
The
.Fl Fl alias= Ns Ar alias-name
option may be given multiple times, which will set the complete
list of aliases for the principal.
Use the
.Nm add_alias
command instead to add an alias without having to list all
existing aliases to keep.
.Pp
The
.Fl Fl alias=
option without a value allows the user to set an empty list of
aliases.
Use the
.Nm del_alias
command to delete one or more aliases.
.Pp
The only policy supported by Heimdal is
.Ql default .
.Pp

View File

@@ -161,4 +161,7 @@ handle_mit(krb5_context, void *, size_t, int, int);
void
add_tl(kadm5_principal_ent_rec *, int, krb5_data *);
krb5_tl_data *
get_tl(kadm5_principal_ent_rec *, int);
#endif /* __ADMIN_LOCL_H__ */

View File

@@ -45,6 +45,9 @@ add_tl(kadm5_principal_ent_rec *princ, int type, krb5_data *data)
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)
@@ -54,6 +57,20 @@ add_tl(kadm5_principal_ent_rec *princ, int type, krb5_data *data)
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,
@@ -656,3 +673,145 @@ modify_ns_kr(struct modify_namespace_key_rotation_options *opt,
return ret != 0;
return 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;
}

View File

@@ -635,8 +635,8 @@ foreach_principal(const char *exp_str,
}
ret = (*func)(princ_ent, data);
if(ret) {
krb5_clear_error_message(context);
krb5_warn(context, ret, "%s %s", funcname, princs[i]);
krb5_clear_error_message(context);
if (saved_ret == 0)
saved_ret = ret;
}

View File

@@ -70,7 +70,14 @@ ${kadmin} modify --alias=foo-alias1@${R} --alias=foo-alias2@${R} foo@${R} || exi
echo "Adding bar"
${kadmin} add -p foo --use-defaults bar@${R} || exit 1
${kadmin} modify --alias=bar-alias1@${R} bar@${R} || exit 1
${kadmin} add_alias bar@${R} bar-alias1@${R} bar-alias2@${R} || exit 1
${kadmin} add_alias bar@${R} bar-alias4@${R} bar-alias3@${R} || exit 1
${kadmin} get -o principal bar@${R} | grep "Principal:.bar@${R}" >/dev/null || exit 1
${kadmin} get -o principal bar-alias1@${R} | grep "Principal:.bar@${R}" >/dev/null || exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias1@${R}" >/dev/null || exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias2@${R}" >/dev/null || exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias3@${R}" >/dev/null || exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias4@${R}" >/dev/null || exit 1
echo "Baz does not exists"
@@ -85,11 +92,19 @@ ${kadmin} rename bar${R} foo${R} 2>/dev/null && exit 1
${kadmin} rename baz${R} foo-alias1${R} 2>/dev/null && exit 1
${kadmin} rename baz${R} foo${R} 2>/dev/null && exit 1
echo "Delete alias"
echo "Delete alias (must fail)"
${kadmin} delete foo-alias1${R} 2>/dev/null && exit 1
${kadmin} delete bar-alias1${R} 2>/dev/null && exit 1
${kadmin} delete baz-alias1${R} 2>/dev/null && exit 1
echo "Delete aliases with del_alias (must succeed)"
${kadmin} del_alias bar-alias2@${R} bar-alias3@${R} bar-alias4@${R} || exit 1
${kadmin} get -o principal bar@${R} | grep "Principal:.bar@${R}" >/dev/null || exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias1@${R}" >/dev/null || exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias2@${R}" >/dev/null && exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias3@${R}" >/dev/null && exit 1
${kadmin} get -o aliases bar@${R} | grep "Aliases:.*bar-alias4@${R}" >/dev/null && exit 1
echo "Delete"
${kadmin} delete bar@${R} || exit 1
${kadmin} delete bar@${R} 2>/dev/null && exit 1
@@ -99,6 +114,13 @@ echo "Add alias to deleted name"
${kadmin} modify --alias=bar-alias1@${R} foo@${R} || exit 1
${kadmin} modify --alias=bar@${R} foo@${R} || exit 1
${kadmin} modify --alias=bar@${R} --alias=baz@${R} foo@${R} || exit 1
${kadmin} get -o principal foo@${R} | grep "Principal:.foo@${R}" >/dev/null || exit 1
${kadmin} get -o principal bar@${R} | grep "Principal:.foo@${R}" >/dev/null || exit 1
${kadmin} get -o principal baz@${R} | grep "Principal:.foo@${R}" >/dev/null || exit 1
${kadmin} get -o aliases foo@${R} |grep "Aliases:.*bar@${R}" >/dev/null || exit 1
${kadmin} get -o aliases foo@${R} |grep "Aliases:.*baz@${R}" >/dev/null || exit 1
${kadmin} get -o aliases foo@${R} |grep "Aliases:.*bar-alias1@${R}" >/dev/null && exit 1
${kadmin} get bar-alias1@${R} 2>/dev/null && exit 1
echo "Rename over self alias key"
${kadmin} rename foo@${R} foo-alias1@${R} 2>/dev/null && exit 1
@@ -109,5 +131,4 @@ ${kadmin} modify --alias=foo foo-alias1@${R} || exit 1
echo "Doing database check"
${kadmin} check ${R} || exit 1
exit $ec
exit 0