krb5 commit: Add alias support
ghudson at mit.edu
ghudson at mit.edu
Thu Feb 27 13:17:40 EST 2025
https://github.com/krb5/krb5/commit/5d3fe31bf1dc48e8ee946bf65428611958cac329
commit 5d3fe31bf1dc48e8ee946bf65428611958cac329
Author: Greg Hudson <ghudson at mit.edu>
Date: Tue Jan 14 21:25:51 2025 -0500
Add alias support
Add a new kadmin command add_alias. Implement it for DB2 and LMDB by
writing stub principal entries with a tl-data entry giving the target
name. Add libkdb5 functions to create and interpret alias entries.
Handle these stub entries in krb5_db_get_principal(), iteratively
resolving aliases up to a depth of 10.
To allow kadm5_delete_principal() to work on aliases, remove the code
that fetches the entry prior to deletion; it was needed before commit
0780e46fc13dbafa177525164997cd204cc50b51 to decrement the policy
reference count, but now serves no purpose. Adjust kdb_delete_entry()
to translate KRB5_KDB_NOENTRY instead of ignoring it, as we still want
to return KADM5_UNK_PRINC when deleting a nonexistent principal name.
Modify the LDAP KDB module to work with alias entries. In
krb5_ldap_put_principal(), recognize stub alias entries and add an
alias to the object for the target principal. In
krb5_ldap_delete_principal(), don't delete the LDAP object when
deleting an alias name. In krb5_ldap_iterate(), generate stub entries
for each alias name in addition to the populated entry for the
canonical name. A small amount of refactoring was done as part of
this work: the LDAP-specific principal name parsing and unparsing
functions were simplified, and a helper function search_princ() was
added to find the LDAP object for a principal name.
In kdb5_util tabdump, add a dump type "alias" to display a list of
aliases in the database.
Based on work by Alexander Bokovoy.
doc/admin/admin_commands/kadmin_local.rst | 22 +-
doc/admin/admin_commands/kdb5_util.rst | 8 +
doc/admin/conf_ldap.rst | 7 +-
doc/admin/database.rst | 4 +
src/include/kdb.h | 28 +-
src/include/krb5/kadm5_auth_plugin.h | 11 +-
src/include/krb5/kadm5_hook_plugin.h | 8 +-
src/kadmin/cli/kadmin.c | 49 +++
src/kadmin/cli/kadmin.h | 2 +
src/kadmin/cli/kadmin_ct.ct | 3 +
src/kadmin/dbutil/tabdump.c | 38 ++
src/kadmin/server/auth.c | 8 +-
src/kadmin/server/auth.h | 2 +
src/kadmin/server/auth_acl.c | 14 +
src/kadmin/server/kadm_rpc_svc.c | 7 +
src/kadmin/server/server_stubs.c | 259 ++++++++-----
src/lib/kadm5/admin.h | 3 +
src/lib/kadm5/admin_xdr.h | 1 +
src/lib/kadm5/clnt/client_principal.c | 20 +
src/lib/kadm5/clnt/client_rpc.c | 8 +
src/lib/kadm5/clnt/libkadm5clnt_mit.exports | 2 +
src/lib/kadm5/kadm_err.et | 1 +
src/lib/kadm5/kadm_rpc.h | 12 +
src/lib/kadm5/kadm_rpc_xdr.c | 15 +
src/lib/kadm5/server_internal.h | 7 +
src/lib/kadm5/srv/kadm5_hook.c | 10 +-
src/lib/kadm5/srv/libkadm5srv_mit.exports | 2 +
src/lib/kadm5/srv/server_kdb.c | 4 +-
src/lib/kadm5/srv/svr_principal.c | 49 ++-
src/lib/kdb/kdb5.c | 112 +++++-
src/lib/kdb/libkdb5.exports | 2 +
src/lib/krb5/error_tables/kdb5_err.et | 1 +
src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c | 227 ++++++-----
src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h | 6 +-
src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c | 429 ++++++++++++---------
src/tests/Makefile.in | 1 +
src/tests/t_alias.py | 124 ++++++
src/tests/t_kadmin_acl.py | 102 ++++-
src/tests/t_kdb.py | 44 ++-
src/tests/t_tabdump.py | 6 +
40 files changed, 1238 insertions(+), 420 deletions(-)
diff --git a/doc/admin/admin_commands/kadmin_local.rst b/doc/admin/admin_commands/kadmin_local.rst
index 2435b3c36..b4edc7924 100644
--- a/doc/admin/admin_commands/kadmin_local.rst
+++ b/doc/admin/admin_commands/kadmin_local.rst
@@ -460,6 +460,24 @@ This command requires the **add** and **delete** privileges.
Alias: **renprinc**
+.. _add_alias:
+
+add_alias
+~~~~~~~~~
+
+ **add_alias** *alias_princ* *target_princ*
+
+Create an alias *alias_princ* pointing to *target_princ*. Aliases may
+be chained (that is, *target_princ* may itself be an alias) up to a
+depth of 10.
+
+This command requires the **add** privilege for *alias_princ* and the
+**modify** privilege for *target_princ*.
+
+(New in release 1.22.)
+
+Aliases: **alias**
+
.. _delete_principal:
delete_principal
@@ -467,8 +485,8 @@ delete_principal
**delete_principal** [**-force**] *principal*
-Deletes the specified *principal* from the database. This command
-prompts for deletion, unless the **-force** option is given.
+Deletes the specified *principal* or alias from the database. This
+command prompts for deletion, unless the **-force** option is given.
This command requires the **delete** privilege.
diff --git a/doc/admin/admin_commands/kdb5_util.rst b/doc/admin/admin_commands/kdb5_util.rst
index 444c58bcd..8147e9766 100644
--- a/doc/admin/admin_commands/kdb5_util.rst
+++ b/doc/admin/admin_commands/kdb5_util.rst
@@ -376,6 +376,14 @@ Options:
Dump types:
+**alias**
+ principal alias information
+
+ **aliasname**
+ the name of the alias
+ **targetname**
+ the target of the alias
+
**keydata**
principal encryption key information, including actual key data
(which is still encrypted in the master key)
diff --git a/doc/admin/conf_ldap.rst b/doc/admin/conf_ldap.rst
index 65542c1a4..908dfd1e7 100644
--- a/doc/admin/conf_ldap.rst
+++ b/doc/admin/conf_ldap.rst
@@ -112,9 +112,10 @@ Configuring Kerberos with OpenLDAP back-end
details.
With the LDAP back end it is possible to provide aliases for principal
-entries. Currently we provide no administrative utilities for
-creating aliases, so it must be done by direct manipulation of the
-LDAP entries.
+entries. Beginning in release 1.22, aliases can be added with the
+kadmin **add_alias** command, but it is also possible (in release 1.7
+or later) to provide aliases through direct manipulation of the LDAP
+entries.
An entry with aliases contains multiple values of the
*krbPrincipalName* attribute. Since LDAP attribute values are not
diff --git a/doc/admin/database.rst b/doc/admin/database.rst
index 2fd07242a..685ec272f 100644
--- a/doc/admin/database.rst
+++ b/doc/admin/database.rst
@@ -93,6 +93,10 @@ To view the attributes of a principal, use the kadmin`
To generate a listing of principals, use the kadmin
**list_principals** command.
+To give a principal additional names, use the kadmin **add_alias**
+command to create aliases to the principal (new in release 1.22).
+Aliases can be removed with the **delete_principal** command.
+
.. _policies:
diff --git a/src/include/kdb.h b/src/include/kdb.h
index d2252d505..7beee28df 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -263,6 +263,8 @@ typedef struct __krb5_key_salt_tuple {
* must use the get_strings and set_string RPCs. */
#define KRB5_TL_STRING_ATTRS 0x000b
+#define KRB5_TL_ALIAS_TARGET 0x000c
+
#define KRB5_TL_PAC_LOGON_INFO 0x0100 /* NDR encoded validation info */
#define KRB5_TL_SERVER_REFERRAL 0x0200 /* ASN.1 encoded ServerReferralInfo */
#define KRB5_TL_SVR_REFERRAL_DATA 0x0300 /* ASN.1 encoded PA-SVR-REFERRAL-DATA */
@@ -850,6 +852,17 @@ krb5_dbe_free_strings(krb5_context, krb5_string_attr *, int count);
void
krb5_dbe_free_string(krb5_context, char *);
+/* Set *out to a stub alias entry pointing to target. */
+krb5_error_code
+krb5_dbe_make_alias_entry(krb5_context context, krb5_const_principal alias,
+ krb5_const_principal target, krb5_db_entry **out);
+
+/* If entry contains a stub alias entry, set *target_out to the alias target.
+ * If not, set *target_out to NULL and return 0. */
+krb5_error_code
+krb5_dbe_read_alias(krb5_context context, krb5_db_entry *entry,
+ krb5_principal *target_out);
+
/*
* Register the KDB keytab type, allowing "KDB:" to be used as a keytab name.
* For this type to work, the context used for keytab operations must have an
@@ -1080,13 +1093,18 @@ typedef struct _kdb_vftabl {
* an entry are expected to contain correct values, regardless of whether
* they are specified in the mask, so it is acceptable for a module to
* ignore the mask and update the entire entry.
+ *
+ * If the module has its own representation of principal aliases, this
+ * method should recognize alias stub entries using krb5_dbe_read_alias()
+ * and should create the alias instead of storing the stub entry.
*/
krb5_error_code (*put_principal)(krb5_context kcontext,
krb5_db_entry *entry, char **db_args);
/*
- * Optional: Delete the entry for the principal search_for. If the
- * principal did not exist, return KRB5_KDB_NOENTRY.
+ * Optional: Delete search_for from the database. If the principal did not
+ * exist, return KRB5_KDB_NOENTRY. If search_for is an alias, delete the
+ * alias, not the entry for the canonical principal.
*/
krb5_error_code (*delete_principal)(krb5_context kcontext,
krb5_const_principal search_for);
@@ -1094,7 +1112,7 @@ typedef struct _kdb_vftabl {
/*
* Optional with default: Rename a principal. If the source principal does
* not exist, return KRB5_KDB_NOENTRY. If the target exists, return an
- * error.
+ * error. This method will not be called if source is an alias.
*
* NOTE: If the module chooses to implement a custom function for renaming
* a principal instead of using the default, then rename operations will
@@ -1109,6 +1127,10 @@ typedef struct _kdb_vftabl {
* arguments func_arg and the entry data. If match_entry is specified, the
* module may narrow the iteration to principal names matching that regular
* expression; a module may alternatively ignore match_entry.
+ *
+ * If the module has its own representation of principal aliases, this
+ * method should invoke func with a stub alias entry for each alias,
+ * created using krb5_dbe_make_alias_entry().
*/
krb5_error_code (*iterate)(krb5_context kcontext,
char *match_entry,
diff --git a/src/include/krb5/kadm5_auth_plugin.h b/src/include/krb5/kadm5_auth_plugin.h
index d514e99be..3f9392362 100644
--- a/src/include/krb5/kadm5_auth_plugin.h
+++ b/src/include/krb5/kadm5_auth_plugin.h
@@ -34,7 +34,7 @@
*
* The kadm5_auth pluggable interface currently has only one supported major
* version, which is 1. Major version 1 has a current minor version number of
- * 1.
+ * 2.
*
* kadm5_auth plugin modules should define a function named
* kadm5_auth_<modulename>_initvt, matching the signature:
@@ -244,6 +244,13 @@ typedef krb5_error_code
(*kadm5_auth_iprop_fn)(krb5_context context, kadm5_auth_moddata data,
krb5_const_principal client);
+/* Optional: authorize an add-alias operation. */
+typedef krb5_error_code
+(*kadm5_auth_addalias_fn)(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client,
+ krb5_const_principal alias_princ,
+ krb5_const_principal target_princ);
+
/*
* Optional: receive a notification that the most recent authorized operation
* has ended. If a kadm5_auth module is also a KDB module, it can assume that
@@ -301,6 +308,8 @@ typedef struct kadm5_auth_vtable_st {
kadm5_auth_free_restrictions_fn free_restrictions;
/* Minor version 1 ends here. */
+ kadm5_auth_addalias_fn addalias;
+ /* Minor version 2 ends here. */
} *kadm5_auth_vtable;
#endif /* KRB5_KADM5_AUTH_PLUGIN_H */
diff --git a/src/include/krb5/kadm5_hook_plugin.h b/src/include/krb5/kadm5_hook_plugin.h
index f4f3730f4..cca6d6350 100644
--- a/src/include/krb5/kadm5_hook_plugin.h
+++ b/src/include/krb5/kadm5_hook_plugin.h
@@ -47,7 +47,7 @@
* does not provide strong guarantees of ABI stability.
*
* The kadm5_hook interface currently has only one supported major version,
- * which is 1. Major version 1 has a current minor version number of 2.
+ * which is 1. Major version 1 has a current minor version number of 3.
*
* kadm5_hook plugins should:
* kadm5_hook_<modulename>_initvt, matching the signature:
@@ -149,6 +149,12 @@ typedef struct kadm5_hook_vtable_1_st {
/* End of minor version 2. */
+ kadm5_ret_t (*alias)(krb5_context context, kadm5_hook_modinfo *modinfo,
+ int stage, krb5_principal alias,
+ krb5_principal target);
+
+ /* End of minor version 3. */
+
} kadm5_hook_vftable_1;
#endif /*H_KRB5_KADM5_HOOK_PLUGIN*/
diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c
index 372457039..f40c69e1c 100644
--- a/src/kadmin/cli/kadmin.c
+++ b/src/kadmin/cli/kadmin.c
@@ -774,6 +774,55 @@ cleanup:
free(ocanon);
}
+void
+kadmin_addalias(int argc, char *argv[], int sci_idx, void *info_ptr)
+{
+ kadm5_ret_t retval;
+ krb5_principal alias = NULL, target = NULL;
+ char *acanon = NULL, *tcanon = NULL;
+
+ if (argc != 3) {
+ error(_("usage: add_alias alias_principal target_principal\n"));
+ return;
+ }
+ retval = kadmin_parse_name(argv[1], &alias);
+ if (retval) {
+ com_err("add_alias", retval, _("while parsing alias principal name"));
+ goto cleanup;
+ }
+ retval = kadmin_parse_name(argv[2], &target);
+ if (retval) {
+ com_err("add_alias", retval, _("while parsing target principal name"));
+ goto cleanup;
+ }
+ retval = krb5_unparse_name(context, alias, &acanon);
+ if (retval) {
+ com_err("add_alias", retval,
+ _("while canonicalizing alias principal"));
+ goto cleanup;
+ }
+ retval = krb5_unparse_name(context, target, &tcanon);
+ if (retval) {
+ com_err("add_alias", retval,
+ _("while canonicalizing target principal"));
+ goto cleanup;
+ }
+ retval = kadm5_create_alias(handle, alias, target);
+ if (retval) {
+ com_err("add_alias", retval,
+ _("while aliasing principal \"%s\" to \"%s\""),
+ acanon, tcanon);
+ goto cleanup;
+ }
+ info(_("Principal \"%s\" aliased to \"%s\".\n"), acanon, tcanon);
+
+cleanup:
+ krb5_free_principal(context, alias);
+ krb5_free_principal(context, target);
+ free(acanon);
+ free(tcanon);
+}
+
static void
cpw_usage(const char *str)
{
diff --git a/src/kadmin/cli/kadmin.h b/src/kadmin/cli/kadmin.h
index f08eac153..bf63d0430 100644
--- a/src/kadmin/cli/kadmin.h
+++ b/src/kadmin/cli/kadmin.h
@@ -42,6 +42,8 @@ extern void kadmin_delprinc(int argc, char *argv[], int sci_idx,
void *info_ptr);
extern void kadmin_renameprinc(int argc, char *argv[], int sci_idx,
void *info_ptr);
+extern void kadmin_addalias(int argc, char *argv[], int sci_idx,
+ void *info_ptr);
extern void kadmin_cpw(int argc, char *argv[], int sci_idx, void *info_ptr);
extern void kadmin_addprinc(int argc, char *argv[], int sci_idx,
void *info_ptr);
diff --git a/src/kadmin/cli/kadmin_ct.ct b/src/kadmin/cli/kadmin_ct.ct
index 705e41840..62fb4d6a8 100644
--- a/src/kadmin/cli/kadmin_ct.ct
+++ b/src/kadmin/cli/kadmin_ct.ct
@@ -38,6 +38,9 @@ request kadmin_modprinc, "Modify principal",
request kadmin_renameprinc, "Rename principal",
rename_principal, renprinc;
+request kadmin_addalias, "Add alias",
+ add_alias, alias;
+
request kadmin_cpw, "Change password",
change_password, cpw;
diff --git a/src/kadmin/dbutil/tabdump.c b/src/kadmin/dbutil/tabdump.c
index da55c2d78..d4b7b7333 100644
--- a/src/kadmin/dbutil/tabdump.c
+++ b/src/kadmin/dbutil/tabdump.c
@@ -69,6 +69,7 @@ struct tdtype {
tdump_policy_fn *policy_fn;
};
+static tdump_princ_fn alias;
static tdump_princ_fn keydata;
static tdump_princ_fn keyinfo;
static tdump_princ_fn princ_flags;
@@ -98,9 +99,13 @@ static char * const princ_stringattrs_fields[] = {
static char * const princ_tktpolicy_fields[] = {
"name", "expiration", "pw_expiration", "max_life", "max_renew_life", NULL
};
+static char * const alias_fields[] = {
+ "aliasname", "targetname", NULL
+};
/* Lookup table for tabdump record types */
static struct tdtype tdtypes[] = {
+ {"alias", alias_fields, alias, NULL},
{"keydata", keydata_fields, keydata, NULL},
{"keyinfo", keyinfo_fields, keyinfo, NULL},
{"princ_flags", princ_flags_fields, princ_flags, NULL},
@@ -252,6 +257,39 @@ write_data(struct rec_args *args, krb5_data *data)
return ret;
}
+static krb5_error_code
+alias(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ krb5_error_code ret;
+ struct rechandle *h = args->rh;
+ krb5_principal target = NULL;
+ char *tname = NULL;
+
+ ret = krb5_dbe_read_alias(util_context, dbe, &target);
+ if (ret)
+ return ret;
+ if (target == NULL)
+ return 0;
+
+ ret = krb5_unparse_name(util_context, target, &tname);
+ if (ret)
+ goto cleanup;
+
+ if (startrec(h) < 0)
+ ret = errno;
+ if (!ret && writefield(h, "%s", name) < 0)
+ ret = errno;
+ if (!ret && writefield(h, "%s", tname) < 0)
+ ret = errno;
+ if (!ret && endrec(h) < 0)
+ ret = errno;
+
+cleanup:
+ krb5_free_principal(util_context, target);
+ krb5_free_unparsed_name(util_context, tname);
+ return ret;
+}
+
/* Write a single record of a keydata/keyinfo key set. */
static krb5_error_code
keyinfo_rec(struct rec_args *args, const char *name, int i, krb5_key_data *kd,
diff --git a/src/kadmin/server/auth.c b/src/kadmin/server/auth.c
index 081b20a8b..224b12190 100644
--- a/src/kadmin/server/auth.c
+++ b/src/kadmin/server/auth.c
@@ -91,7 +91,7 @@ auth_init(krb5_context context, const char *acl_file)
h = k5alloc(sizeof(*h), &ret);
if (h == NULL)
goto cleanup;
- ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt);
+ ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&h->vt);
if (ret) { /* Failed vtable init is non-fatal. */
TRACE_KADM5_AUTH_VTINIT_FAIL(context, ret);
free(h);
@@ -172,6 +172,8 @@ call_module(krb5_context context, auth_handle h, int opcode,
return h->vt.listpols(context, h->data, client);
else if (opcode == OP_IPROP && h->vt.iprop != NULL)
return h->vt.iprop(context, h->data, client);
+ else if (opcode == OP_ADDALIAS && h->vt.addalias != NULL)
+ return h->vt.addalias(context, h->data, client, p1, p2);
return KRB5_PLUGIN_NO_HANDLE;
}
@@ -264,12 +266,12 @@ impose_restrictions(krb5_context context,
krb5_boolean
auth_restrict(krb5_context context, int opcode, krb5_const_principal client,
- kadm5_principal_ent_t ent, long *mask)
+ krb5_const_principal target, kadm5_principal_ent_t ent,
+ long *mask)
{
auth_handle *hp, h;
krb5_boolean authorized = FALSE;
krb5_error_code ret, rs_ret;
- krb5_const_principal target = ent->principal;
struct kadm5_auth_restrictions *rs;
assert(opcode == OP_ADDPRINC || opcode == OP_MODPRINC);
diff --git a/src/kadmin/server/auth.h b/src/kadmin/server/auth.h
index 4d265add7..5f51a9911 100644
--- a/src/kadmin/server/auth.h
+++ b/src/kadmin/server/auth.h
@@ -52,6 +52,7 @@
#define OP_GETPOL 17
#define OP_LISTPOLS 18
#define OP_IPROP 19
+#define OP_ADDALIAS 20
/* Initialize all authorization modules. */
krb5_error_code auth_init(krb5_context context, const char *acl_file);
@@ -70,6 +71,7 @@ krb5_boolean auth(krb5_context context, int opcode,
* restrictions to ent and mask if any modules supply them. */
krb5_boolean auth_restrict(krb5_context context, int opcode,
krb5_const_principal client,
+ krb5_const_principal target,
kadm5_principal_ent_t ent, long *mask);
/* Notify modules that the most recent authorized operation has ended. */
diff --git a/src/kadmin/server/auth_acl.c b/src/kadmin/server/auth_acl.c
index ce9ace36a..a04fb23db 100644
--- a/src/kadmin/server/auth_acl.c
+++ b/src/kadmin/server/auth_acl.c
@@ -720,6 +720,19 @@ acl_iprop(krb5_context context, kadm5_auth_moddata data,
return acl_check(data, ACL_IPROP, client, NULL, NULL);
}
+static krb5_error_code
+acl_addalias(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal alias,
+ krb5_const_principal target)
+{
+ struct kadm5_auth_restrictions *rs;
+
+ if (acl_check(data, ACL_ADD, client, alias, &rs) == 0 && rs == NULL &&
+ acl_check(data, ACL_MODIFY, client, target, &rs) == 0)
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
krb5_error_code
kadm5_auth_acl_initvt(krb5_context context, int maj_ver, int min_ver,
krb5_plugin_vtable vtable)
@@ -751,5 +764,6 @@ kadm5_auth_acl_initvt(krb5_context context, int maj_ver, int min_ver,
vt->getpol = acl_getpol;
vt->listpols = acl_listpols;
vt->iprop = acl_iprop;
+ vt->addalias = acl_addalias;
return 0;
}
diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c
index f0e43d9ae..867d8b040 100644
--- a/src/kadmin/server/kadm_rpc_svc.c
+++ b/src/kadmin/server/kadm_rpc_svc.c
@@ -59,6 +59,7 @@ kadm_1(struct svc_req *rqstp, SVCXPRT *transp)
setkey3_arg setkey_principal3_2_arg;
setkey4_arg setkey_principal4_2_arg;
getpkeys_arg get_principal_keys_2_arg;
+ calias_arg create_alias_2_arg;
} argument;
union {
generic_ret gen_ret;
@@ -241,6 +242,12 @@ kadm_1(struct svc_req *rqstp, SVCXPRT *transp)
local = (bool_t (*)(char *, void *, struct svc_req *))get_principal_keys_2_svc;
break;
+ case CREATE_ALIAS:
+ xdr_argument = (xdrproc_t)xdr_calias_arg;
+ xdr_result = (xdrproc_t)xdr_generic_ret;
+ local = (bool_t (*)(char *, void *, struct svc_req *))create_alias_2_svc;
+ break;
+
default:
krb5_klog_syslog(LOG_ERR, "Invalid KADM5 procedure number: %s, %d",
client_addr(rqstp->rq_xprt), rqstp->rq_proc);
diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c
index 87f67874a..b2371e67a 100644
--- a/src/kadmin/server/server_stubs.c
+++ b/src/kadmin/server/server_stubs.c
@@ -265,7 +265,7 @@ static kadm5_ret_t
stub_setup(krb5_ui_4 api_version, struct svc_req *rqstp, krb5_principal princ,
kadm5_server_handle_t *handle_out, krb5_ui_4 *api_version_out,
gss_buffer_t client_name_out, gss_buffer_t service_name_out,
- char **princ_str_out)
+ char **princ_str_out, kadm5_principal_ent_rec *rec_out)
{
kadm5_ret_t ret;
@@ -289,6 +289,13 @@ stub_setup(krb5_ui_4 api_version, struct svc_req *rqstp, krb5_principal princ,
return KADM5_BAD_PRINCIPAL;
}
+ if (rec_out != NULL) {
+ if (princ == NULL)
+ return KADM5_BAD_PRINCIPAL;
+ return kadm5_get_principal(*handle_out, princ, rec_out,
+ KADM5_PRINCIPAL | KADM5_ATTRIBUTES);
+ }
+
return KADM5_OK;
}
@@ -324,10 +331,11 @@ stub_auth_pol(kadm5_server_handle_t handle, int opcode, const char *policy,
static krb5_boolean
stub_auth_restrict(kadm5_server_handle_t handle, int opcode,
- kadm5_principal_ent_t ent, long *mask)
+ krb5_const_principal princ, kadm5_principal_ent_t ent,
+ long *mask)
{
return auth_restrict(handle->context, opcode, handle->current_caller,
- ent, mask);
+ princ, ent, mask);
}
/* Return true if the client authenticated to kadmin/changepw and princ is not
@@ -440,12 +448,13 @@ create_principal_2_svc(cprinc_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
&handle, &ret->api_version, &client_name,
- &service_name, &prime_arg);
+ &service_name, &prime_arg, NULL);
if (ret->code)
goto exit_func;
if (CHANGEPW_SERVICE(rqstp) ||
- !stub_auth_restrict(handle, OP_ADDPRINC, &arg->rec, &arg->mask)) {
+ !stub_auth_restrict(handle, OP_ADDPRINC, arg->rec.principal,
+ &arg->rec, &arg->mask)) {
ret->code = KADM5_AUTH_ADD;
log_unauth("kadm5_create_principal", prime_arg,
&client_name, &service_name, rqstp);
@@ -480,12 +489,13 @@ create_principal3_2_svc(cprinc3_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
&handle, &ret->api_version, &client_name,
- &service_name, &prime_arg);
+ &service_name, &prime_arg, NULL);
if (ret->code)
goto exit_func;
if (CHANGEPW_SERVICE(rqstp) ||
- !stub_auth_restrict(handle, OP_ADDPRINC, &arg->rec, &arg->mask)) {
+ !stub_auth_restrict(handle, OP_ADDPRINC, arg->rec.principal, &arg->rec,
+ &arg->mask)) {
ret->code = KADM5_AUTH_ADD;
log_unauth("kadm5_create_principal", prime_arg,
&client_name, &service_name, rqstp);
@@ -508,9 +518,15 @@ exit_func:
return TRUE;
}
-/* Return KADM5_PROTECT_KEYS if KRB5_KDB_LOCKDOWN_KEYS is set for princ. */
+/* Return KADM5_PROTECT_KEYS if KRB5_KDB_LOCKDOWN_KEYS is set for rec. */
+static inline kadm5_ret_t
+check_lockdown(kadm5_principal_ent_t rec)
+{
+ return (rec->attributes & KRB5_KDB_LOCKDOWN_KEYS) ? KADM5_PROTECT_KEYS : 0;
+}
+
static kadm5_ret_t
-check_lockdown_keys(kadm5_server_handle_t handle, krb5_principal princ)
+check_lockdown_by_princ(kadm5_server_handle_t handle, krb5_principal princ)
{
kadm5_principal_ent_rec rec;
kadm5_ret_t ret;
@@ -518,7 +534,7 @@ check_lockdown_keys(kadm5_server_handle_t handle, krb5_principal princ)
ret = kadm5_get_principal(handle, princ, &rec, KADM5_ATTRIBUTES);
if (ret)
return ret;
- ret = (rec.attributes & KRB5_KDB_LOCKDOWN_KEYS) ? KADM5_PROTECT_KEYS : 0;
+ ret = check_lockdown(&rec);
kadm5_free_principal_ent(handle, &rec);
return ret;
}
@@ -535,7 +551,7 @@ delete_principal_2_svc(dprinc_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, NULL);
if (ret->code)
goto exit_func;
@@ -545,7 +561,7 @@ delete_principal_2_svc(dprinc_arg *arg, generic_ret *ret,
log_unauth("kadm5_delete_principal", prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = check_lockdown_keys(handle, arg->princ);
+ ret->code = check_lockdown_by_princ(handle, arg->princ);
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_delete_principal", prime_arg, &client_name,
&service_name, rqstp);
@@ -577,6 +593,7 @@ modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 }, rec_copy;
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -584,18 +601,19 @@ modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
&handle, &ret->api_version, &client_name,
- &service_name, &prime_arg);
+ &service_name, &prime_arg, &rec);
if (ret->code)
goto exit_func;
if (CHANGEPW_SERVICE(rqstp) ||
- !stub_auth_restrict(handle, OP_MODPRINC, &arg->rec, &arg->mask)) {
+ !stub_auth_restrict(handle, OP_MODPRINC, rec.principal, &arg->rec,
+ &arg->mask)) {
ret->code = KADM5_AUTH_MODIFY;
log_unauth("kadm5_modify_principal", prime_arg,
&client_name, &service_name, rqstp);
} else if ((arg->mask & KADM5_ATTRIBUTES) &&
(!(arg->rec.attributes & KRB5_KDB_LOCKDOWN_KEYS))) {
- ret->code = check_lockdown_keys(handle, arg->rec.principal);
+ ret->code = check_lockdown(&rec);
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_modify_principal", prime_arg, &client_name,
&service_name, rqstp);
@@ -604,7 +622,11 @@ modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
}
if (ret->code == KADM5_OK) {
- ret->code = kadm5_modify_principal(handle, &arg->rec, arg->mask);
+ /* Modify via the canonicalized principal name using a shallow copy of
+ * arg->rec, to ensure consistency with the ACL check. */
+ rec_copy = arg->rec;
+ rec_copy.principal = rec.principal;
+ ret->code = kadm5_modify_principal(handle, &rec_copy, arg->mask);
if (ret->code != 0)
errmsg = krb5_get_error_message(handle->context, ret->code);
@@ -616,6 +638,7 @@ modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -634,7 +657,7 @@ rename_principal_2_svc(rprinc_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
&ret->api_version, &client_name, &service_name,
- NULL);
+ NULL, NULL);
if (ret->code)
goto exit_func;
@@ -658,7 +681,7 @@ rename_principal_2_svc(rprinc_arg *arg, generic_ret *ret,
log_unauth("kadm5_rename_principal", prime_arg1, &client_name,
&service_name, rqstp);
} else {
- ret->code = check_lockdown_keys(handle, arg->src);
+ ret->code = check_lockdown_by_princ(handle, arg->src);
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_rename_principal", prime_arg1, &client_name,
&service_name, rqstp);
@@ -708,6 +731,7 @@ bool_t
get_principal_2_svc(gprinc_arg *arg, gprinc_ret *ret, struct svc_req *rqstp)
{
char *funcname, *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -715,19 +739,19 @@ get_principal_2_svc(gprinc_arg *arg, gprinc_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
funcname = "kadm5_get_principal";
- if (changepw_not_self(handle, rqstp, arg->princ) ||
- !stub_auth(handle, OP_GETPRINC, arg->princ, NULL, NULL, NULL)) {
+ if (changepw_not_self(handle, rqstp, rec.principal) ||
+ !stub_auth(handle, OP_GETPRINC, rec.principal, NULL, NULL, NULL)) {
ret->code = KADM5_AUTH_GET;
log_unauth(funcname, prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = kadm5_get_principal(handle, arg->princ, &ret->rec,
+ ret->code = kadm5_get_principal(handle, rec.principal, &ret->rec,
arg->mask);
if (ret->code != 0)
@@ -741,6 +765,7 @@ get_principal_2_svc(gprinc_arg *arg, gprinc_ret *ret, struct svc_req *rqstp)
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -756,7 +781,7 @@ get_princs_2_svc(gprincs_arg *arg, gprincs_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
&ret->api_version, &client_name, &service_name,
- NULL);
+ NULL, NULL);
if (ret->code)
goto exit_func;
@@ -793,6 +818,7 @@ chpass_principal_2_svc(chpass_arg *arg, generic_ret *ret,
struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -800,26 +826,28 @@ chpass_principal_2_svc(chpass_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
- ret->code = check_lockdown_keys(handle, arg->princ);
+ ret->code = check_lockdown(&rec);
if (ret->code != KADM5_OK) {
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
&service_name, rqstp);
ret->code = KADM5_AUTH_CHANGEPW;
}
- } else if (changepw_not_self(handle, rqstp, arg->princ) ||
- !stub_auth(handle, OP_CPW, arg->princ, NULL, NULL, NULL)) {
+ } else if (changepw_not_self(handle, rqstp, rec.principal) ||
+ !stub_auth(handle, OP_CPW, rec.principal, NULL, NULL, NULL)) {
ret->code = KADM5_AUTH_CHANGEPW;
log_unauth("kadm5_chpass_principal", prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = check_self_keychange(handle, rqstp, arg->princ);
- if (!ret->code)
- ret->code = kadm5_chpass_principal(handle, arg->princ, arg->pass);
+ ret->code = check_self_keychange(handle, rqstp, rec.principal);
+ if (!ret->code) {
+ ret->code = kadm5_chpass_principal(handle, rec.principal,
+ arg->pass);
+ }
}
if (ret->code != KADM5_AUTH_CHANGEPW) {
@@ -834,6 +862,7 @@ chpass_principal_2_svc(chpass_arg *arg, generic_ret *ret,
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -843,6 +872,7 @@ chpass_principal3_2_svc(chpass3_arg *arg, generic_ret *ret,
struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -850,26 +880,26 @@ chpass_principal3_2_svc(chpass3_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
- ret->code = check_lockdown_keys(handle, arg->princ);
+ ret->code = check_lockdown(&rec);
if (ret->code != KADM5_OK) {
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
&service_name, rqstp);
ret->code = KADM5_AUTH_CHANGEPW;
}
- } else if (changepw_not_self(handle, rqstp, arg->princ) ||
- !stub_auth(handle, OP_CPW, arg->princ, NULL, NULL, NULL)) {
+ } else if (changepw_not_self(handle, rqstp, rec.principal) ||
+ !stub_auth(handle, OP_CPW, rec.principal, NULL, NULL, NULL)) {
ret->code = KADM5_AUTH_CHANGEPW;
log_unauth("kadm5_chpass_principal", prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = check_self_keychange(handle, rqstp, arg->princ);
+ ret->code = check_self_keychange(handle, rqstp, rec.principal);
if (!ret->code) {
- ret->code = kadm5_chpass_principal_3(handle, arg->princ,
+ ret->code = kadm5_chpass_principal_3(handle, rec.principal,
arg->keepold, arg->n_ks_tuple,
arg->ks_tuple, arg->pass);
}
@@ -887,6 +917,7 @@ chpass_principal3_2_svc(chpass3_arg *arg, generic_ret *ret,
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -896,6 +927,7 @@ setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret,
struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -903,11 +935,11 @@ setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
- ret->code = check_lockdown_keys(handle, arg->princ);
+ ret->code = check_lockdown(&rec);
if (ret->code != KADM5_OK) {
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
@@ -915,9 +947,9 @@ setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret,
ret->code = KADM5_AUTH_SETKEY;
}
} else if (!(CHANGEPW_SERVICE(rqstp)) &&
- stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
- ret->code = kadm5_setkey_principal(handle, arg->princ, arg->keyblocks,
- arg->n_keys);
+ stub_auth(handle, OP_SETKEY, rec.principal, NULL, NULL, NULL)) {
+ ret->code = kadm5_setkey_principal(handle, rec.principal,
+ arg->keyblocks, arg->n_keys);
} else {
log_unauth("kadm5_setkey_principal", prime_arg,
&client_name, &service_name, rqstp);
@@ -936,6 +968,7 @@ setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret,
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -945,6 +978,7 @@ setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret,
struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -952,11 +986,11 @@ setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
- ret->code = check_lockdown_keys(handle, arg->princ);
+ ret->code = check_lockdown(&rec);
if (ret->code != KADM5_OK) {
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
@@ -964,10 +998,11 @@ setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret,
ret->code = KADM5_AUTH_SETKEY;
}
} else if (!(CHANGEPW_SERVICE(rqstp)) &&
- stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
- ret->code = kadm5_setkey_principal_3(handle, arg->princ, arg->keepold,
- arg->n_ks_tuple, arg->ks_tuple,
- arg->keyblocks, arg->n_keys);
+ stub_auth(handle, OP_SETKEY, rec.principal, NULL, NULL, NULL)) {
+ ret->code = kadm5_setkey_principal_3(handle, rec.principal,
+ arg->keepold, arg->n_ks_tuple,
+ arg->ks_tuple, arg->keyblocks,
+ arg->n_keys);
} else {
log_unauth("kadm5_setkey_principal", prime_arg,
&client_name, &service_name, rqstp);
@@ -986,6 +1021,7 @@ setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret,
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -995,6 +1031,7 @@ setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret,
struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -1002,11 +1039,11 @@ setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
- ret->code = check_lockdown_keys(handle, arg->princ);
+ ret->code = check_lockdown(&rec);
if (ret->code != KADM5_OK) {
if (ret->code == KADM5_PROTECT_KEYS) {
log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
@@ -1014,9 +1051,10 @@ setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret,
ret->code = KADM5_AUTH_SETKEY;
}
} else if (!(CHANGEPW_SERVICE(rqstp)) &&
- stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
- ret->code = kadm5_setkey_principal_4(handle, arg->princ, arg->keepold,
- arg->key_data, arg->n_key_data);
+ stub_auth(handle, OP_SETKEY, rec.principal, NULL, NULL, NULL)) {
+ ret->code = kadm5_setkey_principal_4(handle, rec.principal,
+ arg->keepold, arg->key_data,
+ arg->n_key_data);
} else {
log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
&service_name, rqstp);
@@ -1035,6 +1073,7 @@ setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret,
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -1042,13 +1081,13 @@ exit_func:
/* Empty out *keys / *nkeys if princ is protected with the lockdown
* attribute, or if we fail to check. */
static kadm5_ret_t
-chrand_check_lockdown(kadm5_server_handle_t handle, krb5_principal princ,
+chrand_check_lockdown(kadm5_server_handle_t handle, kadm5_principal_ent_t rec,
krb5_keyblock **keys, int *nkeys)
{
kadm5_ret_t ret;
int i;
- ret = check_lockdown_keys(handle, princ);
+ ret = check_lockdown(rec);
if (!ret)
return 0;
@@ -1064,6 +1103,7 @@ bool_t
chrand_principal_2_svc(chrand_arg *arg, chrand_ret *ret, struct svc_req *rqstp)
{
char *funcname, *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
krb5_keyblock *k;
@@ -1073,27 +1113,27 @@ chrand_principal_2_svc(chrand_arg *arg, chrand_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
funcname = "kadm5_randkey_principal";
- if (changepw_not_self(handle, rqstp, arg->princ) ||
- !stub_auth(handle, OP_CHRAND, arg->princ, NULL, NULL, NULL)) {
+ if (changepw_not_self(handle, rqstp, rec.principal) ||
+ !stub_auth(handle, OP_CHRAND, rec.principal, NULL, NULL, NULL)) {
ret->code = KADM5_AUTH_CHANGEPW;
log_unauth(funcname, prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = check_self_keychange(handle, rqstp, arg->princ);
+ ret->code = check_self_keychange(handle, rqstp, rec.principal);
if (!ret->code) {
- ret->code = kadm5_randkey_principal(handle, arg->princ,
+ ret->code = kadm5_randkey_principal(handle, rec.principal,
&k, &nkeys);
}
}
if (ret->code == KADM5_OK) {
- ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
+ ret->code = chrand_check_lockdown(handle, &rec, &k, &nkeys);
if (ret->code == KADM5_PROTECT_KEYS)
ret->code = KADM5_OK;
ret->keys = k;
@@ -1112,6 +1152,7 @@ chrand_principal_2_svc(chrand_arg *arg, chrand_ret *ret, struct svc_req *rqstp)
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -1121,6 +1162,7 @@ chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret,
struct svc_req *rqstp)
{
char *funcname, *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
krb5_keyblock *k;
@@ -1130,21 +1172,21 @@ chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
funcname = "kadm5_randkey_principal";
- if (changepw_not_self(handle, rqstp, arg->princ) ||
- !stub_auth(handle, OP_CHRAND, arg->princ, NULL, NULL, NULL)) {
+ if (changepw_not_self(handle, rqstp, rec.principal) ||
+ !stub_auth(handle, OP_CHRAND, rec.principal, NULL, NULL, NULL)) {
ret->code = KADM5_AUTH_CHANGEPW;
log_unauth(funcname, prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = check_self_keychange(handle, rqstp, arg->princ);
+ ret->code = check_self_keychange(handle, rqstp, rec.principal);
if (!ret->code) {
- ret->code = kadm5_randkey_principal_3(handle, arg->princ,
+ ret->code = kadm5_randkey_principal_3(handle, rec.principal,
arg->keepold,
arg->n_ks_tuple,
arg->ks_tuple, &k, &nkeys);
@@ -1152,7 +1194,7 @@ chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret,
}
if (ret->code == KADM5_OK) {
- ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
+ ret->code = chrand_check_lockdown(handle, &rec, &k, &nkeys);
if (ret->code == KADM5_PROTECT_KEYS)
ret->code = KADM5_OK;
ret->keys = k;
@@ -1171,6 +1213,7 @@ chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret,
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -1186,7 +1229,7 @@ create_policy_2_svc(cpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
&ret->api_version, &client_name, &service_name,
- NULL);
+ NULL, NULL);
if (ret->code)
goto exit_func;
@@ -1228,7 +1271,7 @@ delete_policy_2_svc(dpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
&ret->api_version, &client_name, &service_name,
- NULL);
+ NULL, NULL);
if (ret->code)
goto exit_func;
@@ -1268,7 +1311,7 @@ modify_policy_2_svc(mpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
&ret->api_version, &client_name, &service_name,
- NULL);
+ NULL, NULL);
if (ret->code)
goto exit_func;
@@ -1313,7 +1356,7 @@ get_policy_2_svc(gpol_arg *arg, gpol_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
&ret->api_version, &client_name, &service_name,
- NULL);
+ NULL, NULL);
if (ret->code)
goto exit_func;
@@ -1362,7 +1405,7 @@ get_pols_2_svc(gpols_arg *arg, gpols_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
&ret->api_version, &client_name, &service_name,
- NULL);
+ NULL, NULL);
if (ret->code)
goto exit_func;
@@ -1402,7 +1445,7 @@ get_privs_2_svc(krb5_ui_4 *arg, getprivs_ret *ret, struct svc_req *rqstp)
const char *errmsg = NULL;
ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version,
- &client_name, &service_name, NULL);
+ &client_name, &service_name, NULL, NULL);
if (ret->code)
goto exit_func;
@@ -1425,6 +1468,7 @@ bool_t
purgekeys_2_svc(purgekeys_arg *arg, generic_ret *ret, struct svc_req *rqstp)
{
char *funcname, *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -1433,18 +1477,18 @@ purgekeys_2_svc(purgekeys_arg *arg, generic_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
funcname = "kadm5_purgekeys";
if (CHANGEPW_SERVICE(rqstp) ||
- !stub_auth(handle, OP_PURGEKEYS, arg->princ, NULL, NULL, NULL)) {
+ !stub_auth(handle, OP_PURGEKEYS, rec.principal, NULL, NULL, NULL)) {
ret->code = KADM5_AUTH_MODIFY;
log_unauth(funcname, prime_arg, &client_name, &service_name, rqstp);
} else {
- ret->code = kadm5_purgekeys(handle, arg->princ, arg->keepkvno);
+ ret->code = kadm5_purgekeys(handle, rec.principal, arg->keepkvno);
if (ret->code != 0)
errmsg = krb5_get_error_message(handle->context, ret->code);
@@ -1456,6 +1500,7 @@ purgekeys_2_svc(purgekeys_arg *arg, generic_ret *ret, struct svc_req *rqstp)
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -1464,6 +1509,7 @@ bool_t
get_strings_2_svc(gstrings_arg *arg, gstrings_ret *ret, struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -1471,17 +1517,17 @@ get_strings_2_svc(gstrings_arg *arg, gstrings_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
if (CHANGEPW_SERVICE(rqstp) ||
- !stub_auth(handle, OP_GETSTRS, arg->princ, NULL, NULL, NULL)) {
+ !stub_auth(handle, OP_GETSTRS, rec.principal, NULL, NULL, NULL)) {
ret->code = KADM5_AUTH_GET;
log_unauth("kadm5_get_strings", prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = kadm5_get_strings(handle, arg->princ, &ret->strings,
+ ret->code = kadm5_get_strings(handle, rec.principal, &ret->strings,
&ret->count);
if (ret->code != 0)
errmsg = krb5_get_error_message(handle->context, ret->code);
@@ -1494,6 +1540,7 @@ get_strings_2_svc(gstrings_arg *arg, gstrings_ret *ret, struct svc_req *rqstp)
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -1502,6 +1549,7 @@ bool_t
set_string_2_svc(sstring_arg *arg, generic_ret *ret, struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -1509,18 +1557,19 @@ set_string_2_svc(sstring_arg *arg, generic_ret *ret, struct svc_req *rqstp)
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
if (CHANGEPW_SERVICE(rqstp) ||
- !stub_auth(handle, OP_SETSTR, arg->princ, NULL,
+ !stub_auth(handle, OP_SETSTR, rec.principal, NULL,
arg->key, arg->value)) {
ret->code = KADM5_AUTH_MODIFY;
log_unauth("kadm5_mod_strings", prime_arg,
&client_name, &service_name, rqstp);
} else {
- ret->code = kadm5_set_string(handle, arg->princ, arg->key, arg->value);
+ ret->code = kadm5_set_string(handle, rec.principal,
+ arg->key, arg->value);
if (ret->code != 0)
errmsg = krb5_get_error_message(handle->context, ret->code);
@@ -1532,6 +1581,7 @@ set_string_2_svc(sstring_arg *arg, generic_ret *ret, struct svc_req *rqstp)
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
@@ -1547,7 +1597,7 @@ init_2_svc(krb5_ui_4 *arg, generic_ret *ret, struct svc_req *rqstp)
char *cdots, *sdots;
ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version,
- &client_name, &service_name, NULL);
+ &client_name, &service_name, NULL, NULL);
if (ret->code)
goto exit_func;
@@ -1592,6 +1642,7 @@ get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret,
struct svc_req *rqstp)
{
char *prime_arg = NULL;
+ kadm5_principal_ent_rec rec = { 0 };
gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
kadm5_server_handle_t handle;
@@ -1599,13 +1650,13 @@ get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret,
ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
&ret->api_version, &client_name, &service_name,
- &prime_arg);
+ &prime_arg, &rec);
if (ret->code)
goto exit_func;
if (!(CHANGEPW_SERVICE(rqstp)) &&
- stub_auth(handle, OP_EXTRACT, arg->princ, NULL, NULL, NULL)) {
- ret->code = kadm5_get_principal_keys(handle, arg->princ, arg->kvno,
+ stub_auth(handle, OP_EXTRACT, rec.principal, NULL, NULL, NULL)) {
+ ret->code = kadm5_get_principal_keys(handle, rec.principal, arg->kvno,
&ret->key_data, &ret->n_key_data);
} else {
log_unauth("kadm5_get_principal_keys", prime_arg,
@@ -1614,7 +1665,7 @@ get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret,
}
if (ret->code == KADM5_OK) {
- ret->code = check_lockdown_keys(handle, arg->princ);
+ ret->code = check_lockdown(&rec);
if (ret->code != KADM5_OK) {
kadm5_free_kadm5_key_data(handle->context, ret->n_key_data,
ret->key_data);
@@ -1639,6 +1690,42 @@ get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret,
krb5_free_error_message(handle->context, errmsg);
}
+exit_func:
+ kadm5_free_principal_ent(handle, &rec);
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+create_alias_2_svc(calias_arg *arg, generic_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->alias, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg, NULL);
+ if (ret->code)
+ goto exit_func;
+
+ if (CHANGEPW_SERVICE(rqstp) ||
+ !stub_auth(handle, OP_ADDALIAS, arg->alias, arg->target, NULL, NULL)) {
+ ret->code = KADM5_AUTH_INSUFFICIENT;
+ log_unauth("kadm5_create_alias", prime_arg, &client_name,
+ &service_name, rqstp);
+ } else {
+ ret->code = kadm5_create_alias(handle, arg->alias, arg->target);
+ if (ret->code)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+ log_done("kadm5_create_alias", prime_arg, errmsg, &client_name,
+ &service_name, rqstp);
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
exit_func:
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h
index 296c86fa6..4af1ea225 100644
--- a/src/lib/kadm5/admin.h
+++ b/src/lib/kadm5/admin.h
@@ -495,6 +495,9 @@ kadm5_ret_t kadm5_free_strings(void *server_handle,
kadm5_ret_t kadm5_free_kadm5_key_data(krb5_context context, int n_key_data,
kadm5_key_data *key_data);
+kadm5_ret_t kadm5_create_alias(void *server_handle, krb5_principal alias,
+ krb5_principal target);
+
KADM5INT_END_DECLS
#endif /* __KADM5_ADMIN_H__ */
diff --git a/src/lib/kadm5/admin_xdr.h b/src/lib/kadm5/admin_xdr.h
index 9da98451e..12a2598c2 100644
--- a/src/lib/kadm5/admin_xdr.h
+++ b/src/lib/kadm5/admin_xdr.h
@@ -71,3 +71,4 @@ bool_t xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp);
bool_t xdr_kadm5_key_data(XDR *xdrs, kadm5_key_data *objp);
bool_t xdr_getpkeys_arg(XDR *xdrs, getpkeys_arg *objp);
bool_t xdr_getpkeys_ret(XDR *xdrs, getpkeys_ret *objp);
+bool_t xdr_calias_arg(XDR *xdrs, calias_arg *objp);
diff --git a/src/lib/kadm5/clnt/client_principal.c b/src/lib/kadm5/clnt/client_principal.c
index 96d9d1932..976490093 100644
--- a/src/lib/kadm5/clnt/client_principal.c
+++ b/src/lib/kadm5/clnt/client_principal.c
@@ -526,3 +526,23 @@ kadm5_get_principal_keys(void *server_handle, krb5_principal princ,
}
return r.code;
}
+
+kadm5_ret_t
+kadm5_create_alias(void *server_handle, krb5_principal alias,
+ krb5_principal target)
+{
+ calias_arg arg;
+ generic_ret r = { 0, 0 };
+ kadm5_server_handle_t handle = server_handle;
+
+ CHECK_HANDLE(server_handle);
+
+ arg.alias = alias;
+ arg.target = target;
+ arg.api_version = handle->api_version;
+ if (alias == NULL || target == NULL)
+ return EINVAL;
+ if (create_alias_2(&arg, &r, handle->clnt))
+ eret();
+ return r.code;
+}
diff --git a/src/lib/kadm5/clnt/client_rpc.c b/src/lib/kadm5/clnt/client_rpc.c
index c8d844e4c..6dd56872a 100644
--- a/src/lib/kadm5/clnt/client_rpc.c
+++ b/src/lib/kadm5/clnt/client_rpc.c
@@ -212,3 +212,11 @@ get_principal_keys_2(getpkeys_arg *argp, getpkeys_ret *res, CLIENT *clnt)
(xdrproc_t)xdr_getpkeys_arg, (caddr_t)argp,
(xdrproc_t)xdr_getpkeys_ret, (caddr_t)res, TIMEOUT);
}
+
+enum clnt_stat
+create_alias_2(calias_arg *argp, generic_ret *res, CLIENT *clnt)
+{
+ return clnt_call(clnt, CREATE_ALIAS,
+ (xdrproc_t)xdr_calias_arg, (caddr_t)argp,
+ (xdrproc_t)xdr_generic_ret, (caddr_t)res, TIMEOUT);
+}
diff --git a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports
index 9ed7d52dc..43b81e8ae 100644
--- a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports
+++ b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports
@@ -3,6 +3,7 @@ _kadm5_chpass_principal_util
kadm5_chpass_principal
kadm5_chpass_principal_3
kadm5_chpass_principal_util
+kadm5_create_alias
kadm5_create_policy
kadm5_create_principal
kadm5_create_principal_3
@@ -62,6 +63,7 @@ krb5_klog_reopen
krb5_klog_set_context
krb5_klog_syslog
krb5_string_to_keysalts
+xdr_calias_arg
xdr_chpass3_arg
xdr_chpass_arg
xdr_chrand3_arg
diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et
index cf07e8068..f58ddf6d7 100644
--- a/src/lib/kadm5/kadm_err.et
+++ b/src/lib/kadm5/kadm_err.et
@@ -67,4 +67,5 @@ error_code KADM5_SETKEY_BAD_KVNO, "Invalid multiple or duplicate kvnos in setkey
error_code KADM5_AUTH_EXTRACT, "Operation requires ``extract-keys'' privilege"
error_code KADM5_PROTECT_KEYS, "Principal keys are locked down"
error_code KADM5_AUTH_INITIAL, "Operation requires initial ticket"
+error_code KADM5_ALIAS_REALM, "Alias target must be within the same realm"
end
diff --git a/src/lib/kadm5/kadm_rpc.h b/src/lib/kadm5/kadm_rpc.h
index 9efe49a37..bde85478a 100644
--- a/src/lib/kadm5/kadm_rpc.h
+++ b/src/lib/kadm5/kadm_rpc.h
@@ -246,6 +246,13 @@ struct getpkeys_ret {
};
typedef struct getpkeys_ret getpkeys_ret;
+struct calias_arg {
+ krb5_ui_4 api_version;
+ krb5_principal alias;
+ krb5_principal target;
+};
+typedef struct calias_arg calias_arg;
+
#define KADM 2112
#define KADMVERS 2
#define CREATE_PRINCIPAL 1
@@ -360,4 +367,9 @@ extern enum clnt_stat get_principal_keys_2(getpkeys_arg *, getpkeys_ret *,
CLIENT *);
extern bool_t get_principal_keys_2_svc(getpkeys_arg *, getpkeys_ret *,
struct svc_req *);
+
+#define CREATE_ALIAS 27
+extern enum clnt_stat create_alias_2(calias_arg *, generic_ret *, CLIENT *);
+extern bool_t create_alias_2_svc(calias_arg *, generic_ret *,
+ struct svc_req *);
#endif /* __KADM_RPC_H__ */
diff --git a/src/lib/kadm5/kadm_rpc_xdr.c b/src/lib/kadm5/kadm_rpc_xdr.c
index 5e052dd90..8a2def8ff 100644
--- a/src/lib/kadm5/kadm_rpc_xdr.c
+++ b/src/lib/kadm5/kadm_rpc_xdr.c
@@ -1209,3 +1209,18 @@ xdr_getpkeys_ret(XDR *xdrs, getpkeys_ret *objp)
}
return TRUE;
}
+
+bool_t
+xdr_calias_arg(XDR *xdrs, calias_arg *objp)
+{
+ if (!xdr_ui_4(xdrs, &objp->api_version)) {
+ return (FALSE);
+ }
+ if (!xdr_krb5_principal(xdrs, &objp->alias)) {
+ return (FALSE);
+ }
+ if (!xdr_krb5_principal(xdrs, &objp->target)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
diff --git a/src/lib/kadm5/server_internal.h b/src/lib/kadm5/server_internal.h
index 433f4915b..ce2e9197b 100644
--- a/src/lib/kadm5/server_internal.h
+++ b/src/lib/kadm5/server_internal.h
@@ -264,6 +264,13 @@ k5_kadm5_hook_rename (krb5_context context,
int stage,
krb5_principal oprinc, krb5_principal nprinc);
+/** Call alias kadm5_hook entry point. */
+kadm5_ret_t
+k5_kadm5_hook_alias (krb5_context context,
+ kadm5_hook_handle *handles,
+ int stage,
+ krb5_principal alias, krb5_principal target);
+
/** @}*/
#endif /* __KADM5_SERVER_INTERNAL_H__ */
diff --git a/src/lib/kadm5/srv/kadm5_hook.c b/src/lib/kadm5/srv/kadm5_hook.c
index df337bc32..c533d743a 100644
--- a/src/lib/kadm5/srv/kadm5_hook.c
+++ b/src/lib/kadm5/srv/kadm5_hook.c
@@ -64,7 +64,7 @@ k5_kadm5_hook_load(krb5_context context,
handle = k5alloc(sizeof(*handle), &ret);
if (handle == NULL)
goto cleanup;
- ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&handle->vt);
+ ret = (*mod)(context, 1, 3, (krb5_plugin_vtable)&handle->vt);
if (ret != 0) { /* Failed vtable init is non-fatal. */
free(handle);
handle = NULL;
@@ -184,3 +184,11 @@ k5_kadm5_hook_remove(krb5_context context, kadm5_hook_handle *handles,
ITERATE(remove, (context, h->data, stage, princ));
return 0;
}
+
+kadm5_ret_t
+k5_kadm5_hook_alias(krb5_context context, kadm5_hook_handle *handles,
+ int stage, krb5_principal alias, krb5_principal target)
+{
+ ITERATE(alias, (context, h->data, stage, alias, target));
+ return 0;
+}
diff --git a/src/lib/kadm5/srv/libkadm5srv_mit.exports b/src/lib/kadm5/srv/libkadm5srv_mit.exports
index 14c02a7f1..3b39ab590 100644
--- a/src/lib/kadm5/srv/libkadm5srv_mit.exports
+++ b/src/lib/kadm5/srv/libkadm5srv_mit.exports
@@ -4,6 +4,7 @@ hist_princ
kadm5_chpass_principal
kadm5_chpass_principal_3
kadm5_chpass_principal_util
+kadm5_create_alias
kadm5_create_policy
kadm5_create_principal
kadm5_create_principal_3
@@ -74,6 +75,7 @@ master_db
master_princ
osa_free_princ_ent
passwd_check
+xdr_calias_arg
xdr_chpass3_arg
xdr_chpass_arg
xdr_chrand3_arg
diff --git a/src/lib/kadm5/srv/server_kdb.c b/src/lib/kadm5/srv/server_kdb.c
index 4efcaf994..709e7f5d3 100644
--- a/src/lib/kadm5/srv/server_kdb.c
+++ b/src/lib/kadm5/srv/server_kdb.c
@@ -410,9 +410,7 @@ kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name)
krb5_error_code ret;
ret = krb5_db_delete_principal(handle->context, name);
- if (ret == KRB5_KDB_NOENTRY)
- ret = 0;
- return ret;
+ return (ret == KRB5_KDB_NOENTRY) ? KADM5_UNK_PRINC : ret;
}
typedef struct _iter_data {
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index 444c16ed7..1557937f2 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -520,8 +520,6 @@ kadm5_ret_t
kadm5_delete_principal(void *server_handle, krb5_principal principal)
{
unsigned int ret;
- krb5_db_entry *kdb;
- osa_princ_ent_rec adb;
kadm5_server_handle_t handle = server_handle;
CHECK_HANDLE(server_handle);
@@ -535,19 +533,13 @@ kadm5_delete_principal(void *server_handle, krb5_principal principal)
if (krb5_principal_compare(handle->context, principal, master_princ))
return KADM5_PROTECT_PRINCIPAL;
- if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
- return(ret);
ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, principal);
- if (ret) {
- kdb_free_entry(handle, kdb, &adb);
+ if (ret)
return ret;
- }
ret = kdb_delete_entry(handle, principal);
- kdb_free_entry(handle, kdb, &adb);
-
if (ret == 0)
(void) k5_kadm5_hook_remove(handle->context,
handle->hook_handles,
@@ -2056,3 +2048,42 @@ done:
kdb_free_entry(handle, kdb, &adb);
return ret;
}
+
+kadm5_ret_t
+kadm5_create_alias(void *server_handle, krb5_principal alias,
+ krb5_principal target)
+{
+ krb5_db_entry *kdb;
+ osa_princ_ent_rec adb = { 0 };
+ krb5_error_code ret;
+ kadm5_server_handle_t handle = server_handle;
+
+ CHECK_HANDLE(server_handle);
+ if (alias == NULL || target == NULL)
+ return EINVAL;
+ if (!krb5_realm_compare(handle->context, alias, target))
+ return KADM5_ALIAS_REALM;
+
+ ret = kdb_get_entry(handle, alias, &kdb, NULL);
+ if (!ret) {
+ kdb_free_entry(handle, kdb, NULL);
+ return KADM5_DUP;
+ }
+
+ ret = k5_kadm5_hook_alias(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_PRECOMMIT, alias, target);
+ if (ret)
+ return ret;
+
+ ret = krb5_dbe_make_alias_entry(handle->context, alias, target, &kdb);
+ if (ret)
+ return ret;
+ ret = kdb_put_entry(handle, kdb, &adb);
+ krb5_db_free_principal(handle->context, kdb);
+ if (ret)
+ return ret;
+
+ (void) k5_kadm5_hook_alias(handle->context, handle->hook_handles,
+ KADM5_HOOK_STAGE_POSTCOMMIT, alias, target);
+ return 0;
+}
diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c
index a0897f5e8..b6db28d8d 100644
--- a/src/lib/kdb/kdb5.c
+++ b/src/lib/kdb/kdb5.c
@@ -797,27 +797,49 @@ krb5_db_unlock(krb5_context kcontext)
return v->unlock(kcontext);
}
+#define MAX_ALIAS_DEPTH 10
+
krb5_error_code
krb5_db_get_principal(krb5_context kcontext, krb5_const_principal search_for,
- unsigned int flags, krb5_db_entry **entry)
+ unsigned int flags, krb5_db_entry **entry_out)
{
krb5_error_code status = 0;
kdb_vftabl *v;
+ krb5_db_entry *entry;
+ krb5_principal alias_target;
+ int alias_depth = 0;
- *entry = NULL;
+ *entry_out = NULL;
status = get_vftabl(kcontext, &v);
if (status)
return status;
if (v->get_principal == NULL)
return KRB5_PLUGIN_OP_NOTSUPP;
- status = v->get_principal(kcontext, search_for, flags, entry);
+
+ status = v->get_principal(kcontext, search_for, flags, &entry);
if (status)
return status;
+ /* Resolve any aliases up to the maximum depth. */
+ for (;;) {
+ status = krb5_dbe_read_alias(kcontext, entry, &alias_target);
+ if (status)
+ return status;
+ if (alias_target == NULL)
+ break;
+ krb5_db_free_principal(kcontext, entry);
+ status = (++alias_depth > MAX_ALIAS_DEPTH) ? KRB5_KDB_NOENTRY :
+ v->get_principal(kcontext, alias_target, flags, &entry);
+ krb5_free_principal(kcontext, alias_target);
+ if (status)
+ return status;
+ }
+
/* Sort the keys in the db entry as some parts of krb5 expect it to be. */
- if ((*entry)->key_data != NULL)
- krb5_dbe_sort_key_data((*entry)->key_data, (*entry)->n_key_data);
+ if (entry->key_data != NULL)
+ krb5_dbe_sort_key_data(entry->key_data, entry->n_key_data);
+ *entry_out = entry;
return 0;
}
@@ -1036,6 +1058,7 @@ krb5_db_rename_principal(krb5_context kcontext, krb5_principal source,
kdb_vftabl *v;
krb5_error_code status;
krb5_db_entry *entry;
+ krb5_boolean eq;
status = get_vftabl(kcontext, &v);
if (status)
@@ -1050,6 +1073,15 @@ krb5_db_rename_principal(krb5_context kcontext, krb5_principal source,
logging(kcontext))
return KRB5_PLUGIN_OP_NOTSUPP;
+ /* Disallow the operation if source is an alias. */
+ status = krb5_db_get_principal(kcontext, source, 0, &entry);
+ if (status)
+ return status;
+ eq = krb5_principal_compare(kcontext, entry->princ, source);
+ krb5_db_free_principal(kcontext, entry);
+ if (!eq)
+ return KRB5_KDB_ALIAS_UNSUPPORTED;
+
status = krb5_db_get_principal(kcontext, target, 0, &entry);
if (status == 0) {
krb5_db_free_principal(kcontext, entry);
@@ -2789,3 +2821,73 @@ krb5_db_issue_pac(krb5_context context, unsigned int flags,
return v->issue_pac(context, flags, client, replaced_reply_key, server,
krbtgt, authtime, old_pac, new_pac, auth_indicators);
}
+
+krb5_error_code
+krb5_dbe_make_alias_entry(krb5_context context, krb5_const_principal alias,
+ krb5_const_principal target, krb5_db_entry **out)
+{
+ krb5_error_code ret;
+ krb5_principal princ = NULL;
+ char *target_str = NULL;
+ krb5_tl_data *tl = NULL;
+ krb5_db_entry *ent;
+
+ *out = NULL;
+
+ ret = krb5_copy_principal(context, alias, &princ);
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_unparse_name(context, target, &target_str);
+ if (ret)
+ goto cleanup;
+ tl = k5alloc(sizeof(*tl), &ret);
+ if (tl == NULL)
+ goto cleanup;
+ tl->tl_data_next = NULL;
+ tl->tl_data_type = KRB5_TL_ALIAS_TARGET;
+ tl->tl_data_length = strlen(target_str) + 1;
+ tl->tl_data_contents = (uint8_t *)target_str;
+
+ ent = k5alloc(sizeof(*ent), &ret);
+ if (ent == NULL)
+ goto cleanup;
+ ent->len = KRB5_KDB_V1_BASE_LENGTH;
+ ent->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+ ent->princ = princ;
+ ent->tl_data = tl;
+ ent->n_tl_data = 1;
+ princ = NULL;
+ target_str = NULL;
+ tl = NULL;
+ *out = ent;
+
+cleanup:
+ krb5_free_principal(context, princ);
+ krb5_free_unparsed_name(context, target_str);
+ free(tl);
+ return ret;
+}
+
+krb5_error_code
+krb5_dbe_read_alias(krb5_context context, krb5_db_entry *entry,
+ krb5_principal *target_out)
+{
+ krb5_error_code ret;
+ krb5_tl_data tl;
+
+ *target_out = NULL;
+
+ tl.tl_data_type = KRB5_TL_ALIAS_TARGET;
+ ret = krb5_dbe_lookup_tl_data(context, entry, &tl);
+ if (ret)
+ return ret;
+
+ if (tl.tl_data_length == 0)
+ return 0;
+
+ if (tl.tl_data_contents[tl.tl_data_length - 1] != '\0')
+ return KRB5_KDB_TRUNCATED_RECORD;
+
+ return krb5_parse_name(context, (char *)tl.tl_data_contents, target_out);
+}
diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports
index 574bab92f..11cd0bdd1 100644
--- a/src/lib/kdb/libkdb5.exports
+++ b/src/lib/kdb/libkdb5.exports
@@ -107,3 +107,5 @@ ulog_replay
ulog_set_last
xdr_kdb_incr_update_t
krb5_dbe_sort_key_data
+krb5_dbe_make_alias_entry
+krb5_dbe_read_alias
diff --git a/src/lib/krb5/error_tables/kdb5_err.et b/src/lib/krb5/error_tables/kdb5_err.et
index 1b08ec1a6..f803df1d3 100644
--- a/src/lib/krb5/error_tables/kdb5_err.et
+++ b/src/lib/krb5/error_tables/kdb5_err.et
@@ -85,5 +85,6 @@ ec KRB5_LOG_ERROR, "Generic update log error"
ec KRB5_KDB_DBTYPE_MISMATCH, "Database module does not match KDC version"
ec KRB5_KDB_POLICY_REF, "Policy is in use"
ec KRB5_KDB_STRINGS_TOOLONG, "Too much string mapping data"
+ec KRB5_KDB_ALIAS_UNSUPPORTED, "Operation unsupported on alias principal name"
end
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c
index 4ff5219c2..234a2b53b 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c
@@ -128,6 +128,76 @@ krb5_dbe_free_contents(krb5_context context, krb5_db_entry *entry)
return;
}
+static krb5_error_code
+iterate_entry(krb5_context context, krb5_ldap_context *ldap_context,
+ LDAP *ld, LDAPMessage *ent,
+ krb5_error_code (*func)(krb5_pointer, krb5_db_entry *),
+ krb5_pointer func_arg)
+{
+ krb5_error_code ret = 0;
+ krb5_principal cprinc = NULL, nprinc = NULL;
+ krb5_db_entry entry = { 0 }, *entptr;
+ char **canon = NULL, **names = NULL, **list;
+ size_t i;
+
+ canon = ldap_get_values(ld, ent, "krbCanonicalName");
+ names = ldap_get_values(ld, ent, "krbPrincipalName");
+ if (canon == NULL && names == NULL)
+ return 0;
+
+ /* Output an entry for the canonical name if one is given. Otherwise
+ * output an entry for the first name within the realm. */
+ list = (canon != NULL) ? canon : names;
+ for (i = 0; list[i] != NULL; i++) {
+ krb5_free_principal(context, nprinc);
+ nprinc = NULL;
+ ret = krb5_ldap_parse_name(context, list[i], &nprinc);
+ if (ret)
+ goto cleanup;
+
+ if (is_principal_in_realm(ldap_context, nprinc)) {
+ ret = populate_krb5_db_entry(context, ldap_context, ld, ent,
+ nprinc, &entry);
+ if (ret)
+ goto cleanup;
+ ret = (*func)(func_arg, &entry);
+ krb5_dbe_free_contents(context, &entry);
+ if (ret)
+ goto cleanup;
+ break;
+ }
+ }
+
+ /* Output alias entries for each non-canonical name. */
+ if (canon != NULL && names != NULL) {
+ ret = krb5_ldap_parse_name(context, canon[0], &cprinc);
+ if (ret)
+ goto cleanup;
+ for (i = 0; names[i] != NULL; i++) {
+ if (strcmp(names[i], canon[0]) == 0)
+ continue;
+ krb5_free_principal(context, nprinc);
+ nprinc = NULL;
+ ret = krb5_ldap_parse_name(context, names[i], &nprinc);
+ if (ret)
+ goto cleanup;
+ ret = krb5_dbe_make_alias_entry(context, nprinc, cprinc, &entptr);
+ if (ret)
+ goto cleanup;
+ ret = (*func)(func_arg, entptr);
+ krb5_db_free_principal(context, entptr);
+ if (ret)
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ krb5_free_principal(context, cprinc);
+ krb5_free_principal(context, nprinc);
+ ldap_value_free(canon);
+ ldap_value_free(names);
+ return ret;
+}
krb5_error_code
krb5_ldap_iterate(krb5_context context, char *match_expr,
@@ -135,9 +205,8 @@ krb5_ldap_iterate(krb5_context context, char *match_expr,
krb5_pointer func_arg, krb5_flags iterflags)
{
krb5_db_entry entry;
- krb5_principal principal;
- char **subtree=NULL, *princ_name=NULL, *realm=NULL, **values=NULL, *filter=NULL;
- size_t tree=0, ntree=1, i=0;
+ char **subtree=NULL, *realm=NULL, *filter=NULL;
+ size_t tree=0, ntree=1;
krb5_error_code st=0, tempst=0;
LDAP *ld=NULL;
LDAPMessage *result=NULL, *ent=NULL;
@@ -181,33 +250,10 @@ krb5_ldap_iterate(krb5_context context, char *match_expr,
LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes);
for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) {
- values=ldap_get_values(ld, ent, "krbcanonicalname");
- if (values == NULL)
- values=ldap_get_values(ld, ent, "krbprincipalname");
- if (values != NULL) {
- for (i=0; values[i] != NULL; ++i) {
- if (krb5_ldap_parse_principal_name(values[i], &princ_name) != 0)
- continue;
- st = krb5_parse_name(context, princ_name, &principal);
- free(princ_name);
- if (st)
- continue;
-
- if (is_principal_in_realm(ldap_context, principal)) {
- st = populate_krb5_db_entry(context, ldap_context, ld,
- ent, principal, &entry);
- krb5_free_principal(context, principal);
- if (st)
- goto cleanup;
- (*func)(func_arg, &entry);
- krb5_dbe_free_contents(context, &entry);
- break;
- }
- (void) krb5_free_principal(context, principal);
- }
- ldap_value_free(values);
- }
- } /* end of for (ent= ... */
+ st = iterate_entry(context, ldap_context, ld, ent, func, func_arg);
+ if (st)
+ goto cleanup;
+ }
ldap_msgfree(result);
result = NULL;
} /* end of for (tree= ... */
@@ -268,15 +314,17 @@ krb5_ldap_delete_principal(krb5_context context,
GET_HANDLE();
- if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT) {
+ if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT &&
+ (pcount == 1 ||
+ krb5_principal_compare(context, searchfor, entry->princ))) {
st = ldap_delete_ext_s(ld, DN, NULL, NULL);
if (st != LDAP_SUCCESS) {
st = set_ldap_error (context, st, OP_DEL);
goto cleanup;
}
} else {
- if (((st=krb5_unparse_name(context, searchfor, &user)) != 0)
- || ((st=krb5_ldap_unparse_principal_name(user)) != 0))
+ st = krb5_ldap_unparse_name(context, searchfor, &user);
+ if (st)
goto cleanup;
memset(strval, 0, sizeof(strval));
@@ -362,35 +410,6 @@ is_standalone_principal(krb5_context kcontext, krb5_db_entry *entry, int *res)
return code;
}
-/*
- * Unparse princ in the format used for LDAP attributes, and set *user to the
- * result.
- */
-static krb5_error_code
-unparse_principal_name(krb5_context context, krb5_const_principal princ,
- char **user_out)
-{
- krb5_error_code st;
- char *luser = NULL;
-
- *user_out = NULL;
-
- st = krb5_unparse_name(context, princ, &luser);
- if (st)
- goto cleanup;
-
- st = krb5_ldap_unparse_principal_name(luser);
- if (st)
- goto cleanup;
-
- *user_out = luser;
- luser = NULL;
-
-cleanup:
- free(luser);
- return st;
-}
-
/*
* Rename a principal's rdn.
*
@@ -478,10 +497,10 @@ krb5_ldap_rename_principal(krb5_context context, krb5_const_principal source,
goto cleanup;
}
- st = unparse_principal_name(context, source, &suser);
+ st = krb5_ldap_unparse_name(context, source, &suser);
if (st)
goto cleanup;
- st = unparse_principal_name(context, target, &tuser);
+ st = krb5_ldap_unparse_name(context, target, &tuser);
if (st)
goto cleanup;
@@ -565,65 +584,63 @@ cleanup:
return st;
}
-/*
- * Function: krb5_ldap_unparse_principal_name
- *
- * Purpose: Removes '\\' that comes before every occurrence of '@'
- * in the principal name component.
- *
- * Arguments:
- * user_name (input/output) Principal name
- *
- */
-
+/* Unparse princ in the format used for krb5 principal names within LDAP
+ * attributes. */
krb5_error_code
-krb5_ldap_unparse_principal_name(char *user_name)
+krb5_ldap_unparse_name(krb5_context context, krb5_const_principal princ,
+ char **user_out)
{
- char *in, *out;
+ krb5_error_code ret;
+ char *p, *q;
+
+ ret = krb5_unparse_name(context, princ, user_out);
+ if (ret)
+ return ret;
- out = user_name;
- for (in = user_name; *in; in++) {
- if (*in == '\\' && *(in + 1) == '@')
+ /* Remove backslashes preceding at-signs in the unparsed string. */
+ for (q = p = *user_out; *p != '\0'; p++) {
+ if (*p == '\\' && *(p + 1) == '@')
continue;
- *out++ = *in;
+ *q++ = *p;
}
- *out = '\0';
+ *q = '\0';
return 0;
}
-
-/*
- * Function: krb5_ldap_parse_principal_name
- *
- * Purpose: Inserts '\\' before every occurrence of '@'
- * in the principal name component.
- *
- * Arguments:
- * i_princ_name (input) Principal name without '\\'
- * o_princ_name (output) Principal name with '\\'
- *
- * Note: The caller has to free the memory allocated for o_princ_name.
- */
-
+/* Parse username in the format used for krb5 principal names within LDAP
+ * attributes. */
krb5_error_code
-krb5_ldap_parse_principal_name(char *i_princ_name, char **o_princ_name)
+krb5_ldap_parse_name(krb5_context context, const char *username,
+ krb5_principal *out)
{
- const char *at_rlm_name, *p;
+ krb5_error_code ret;
+ const char *at_realm, *p;
+ char *princstr;
struct k5buf buf;
- at_rlm_name = strrchr(i_princ_name, '@');
- if (!at_rlm_name) {
- *o_princ_name = strdup(i_princ_name);
+ *out = NULL;
+
+ /* Make a copy of username, inserting a backslash before each '@'
+ * before the last one. */
+ at_realm = strrchr(username, '@');
+ if (at_realm == NULL) {
+ princstr = strdup(username);
} else {
k5_buf_init_dynamic(&buf);
- for (p = i_princ_name; p < at_rlm_name; p++) {
+ for (p = username; p < at_realm; p++) {
if (*p == '@')
k5_buf_add(&buf, "\\");
k5_buf_add_len(&buf, p, 1);
}
- k5_buf_add(&buf, at_rlm_name);
- *o_princ_name = k5_buf_cstring(&buf);
+ k5_buf_add(&buf, at_realm);
+ princstr = k5_buf_cstring(&buf);
}
- return (*o_princ_name == NULL) ? ENOMEM : 0;
+
+ if (princstr == NULL)
+ return ENOMEM;
+
+ ret = krb5_parse_name(context, princstr, out);
+ free(princstr);
+ return ret;
}
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
index 72a9f960b..71948aac3 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h
@@ -124,10 +124,12 @@ void
krb5_dbe_free_contents(krb5_context, krb5_db_entry *);
krb5_error_code
-krb5_ldap_unparse_principal_name(char *);
+krb5_ldap_unparse_name(krb5_context context, krb5_const_principal princ,
+ char **user_out);
krb5_error_code
-krb5_ldap_parse_principal_name(char *, char **);
+krb5_ldap_parse_name(krb5_context context, const char *username,
+ krb5_principal *out);
struct berval**
krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data,
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
index d929d325c..ae4e03f8c 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
@@ -98,6 +98,110 @@ berval2tl_data(struct berval *in, krb5_tl_data **out)
return 0;
}
+/*
+ * Search for filter at the specified base and scope, expecting to find a
+ * single result. Set *entry_out to an object containing all principal
+ * attributes. Set *result_out to the result message to be freed with
+ * ldap_msgfree(). Set both to NULL if no matching entry is found.
+ */
+static krb5_error_code
+search_at(krb5_context context, krb5_ldap_context *ldap_context,
+ krb5_ldap_server_handle *ldap_server_handle, const char *base,
+ int scope, const char *filter, const char *user,
+ LDAPMessage **entry_out, LDAPMessage **result_out)
+{
+ krb5_error_code st, tempst;
+ LDAPMessage *result = NULL;
+ LDAP *ld = ldap_server_handle->ldap_handle;
+ int nentries;
+
+ *entry_out = *result_out = NULL;
+
+ LDAP_SEARCH(base, scope, filter, principal_attributes);
+ nentries = ldap_count_entries(ld, result);
+ if (nentries > 1) {
+ st = EINVAL;
+ k5_setmsg(context, st,
+ _("Operation cannot continue; more than one "
+ "entry with principal name \"%s\" found"),
+ user);
+ goto cleanup;
+ }
+
+ if (nentries == 1) {
+ *result_out = result;
+ *entry_out = ldap_first_entry(ld, result);
+ return 0;
+ }
+
+cleanup:
+ ldap_msgfree(result);
+ return st;
+}
+
+/*
+ * Search for an LDAP object matching princ, either at the specified dn with
+ * base scope, or, if dn is NULL, in all configured subtrees with the
+ * configured search scope (usually subtree). Set *entry_out and *result_out
+ * in the same way as search_at().
+ */
+static krb5_error_code
+search_princ(krb5_context context, krb5_ldap_context *ldap_context,
+ krb5_ldap_server_handle *ldap_server_handle,
+ krb5_const_principal princ, const char *dn,
+ LDAPMessage **entry_out, LDAPMessage **result_out)
+{
+ krb5_error_code st;
+ char *user = NULL, *filtuser = NULL, *filter = NULL;
+ char **subtreelist = NULL;
+ size_t ntrees = 0, i;
+
+ *entry_out = *result_out = NULL;
+
+ st = krb5_ldap_unparse_name(context, princ, &user);
+ if (st)
+ goto cleanup;
+
+ filtuser = ldap_filter_correct(user);
+ if (filtuser == NULL) {
+ st = ENOMEM;
+ goto cleanup;
+ }
+
+ if (asprintf(&filter, FILTER"%s))", filtuser) < 0) {
+ filter = NULL;
+ st = ENOMEM;
+ goto cleanup;
+ }
+
+ if (dn != NULL) {
+ st = search_at(context, ldap_context, ldap_server_handle, dn,
+ LDAP_SCOPE_BASE, filter, user, entry_out, result_out);
+ goto cleanup;
+ }
+
+ st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees);
+ if (st)
+ goto cleanup;
+
+ for (i = 0; i < ntrees; i++) {
+ st = search_at(context, ldap_context, ldap_server_handle,
+ subtreelist[i], ldap_context->lrparams->search_scope,
+ filter, user, entry_out, result_out);
+ if (st || *entry_out != NULL)
+ goto cleanup;
+ }
+
+cleanup:
+ free(user);
+ free(filtuser);
+ free(filter);
+ while (ntrees > 0)
+ free(subtreelist[--ntrees]);
+ free(subtreelist);
+ return st;
+}
+
/*
* look up a principal in the directory.
*/
@@ -106,18 +210,15 @@ krb5_error_code
krb5_ldap_get_principal(krb5_context context, krb5_const_principal searchfor,
unsigned int flags, krb5_db_entry **entry_ptr)
{
- char *user=NULL, *filter=NULL, *filtuser=NULL;
- size_t tree=0, ntrees=1, princlen=0;
- krb5_error_code tempst=0, st=0;
- char **values=NULL, **subtree=NULL, *cname=NULL;
- LDAP *ld=NULL;
- LDAPMessage *result=NULL, *ent=NULL;
- krb5_ldap_context *ldap_context=NULL;
- kdb5_dal_handle *dal_handle=NULL;
- krb5_ldap_server_handle *ldap_server_handle=NULL;
- krb5_principal cprinc=NULL;
- krb5_boolean found=FALSE;
- krb5_db_entry *entry = NULL;
+ krb5_error_code st;
+ char **values = NULL;
+ LDAP *ld = NULL;
+ LDAPMessage *ent = NULL, *result = NULL;
+ kdb5_dal_handle *dal_handle;
+ krb5_ldap_context *ldap_context;
+ krb5_ldap_server_handle *ldap_server_handle = NULL;
+ krb5_principal cprinc = NULL;
+ krb5_db_entry *entry = NULL;
*entry_ptr = NULL;
@@ -138,116 +239,42 @@ krb5_ldap_get_principal(krb5_context context, krb5_const_principal searchfor,
goto cleanup;
}
- if ((st=krb5_unparse_name(context, searchfor, &user)) != 0)
- goto cleanup;
+ GET_HANDLE();
- if ((st=krb5_ldap_unparse_principal_name(user)) != 0)
+ st = search_princ(context, ldap_context, ldap_server_handle, searchfor,
+ NULL, &ent, &result);
+ if (st)
goto cleanup;
-
- filtuser = ldap_filter_correct(user);
- if (filtuser == NULL) {
- st = ENOMEM;
+ if (ent == NULL) {
+ st = KRB5_KDB_NOENTRY;
goto cleanup;
}
- princlen = strlen(FILTER) + strlen(filtuser) + 2 + 1; /* 2 for closing brackets */
- if ((filter = malloc(princlen)) == NULL) {
- st = ENOMEM;
- goto cleanup;
+ /* Use the canonical principal name if one is present in the object. */
+ values = ldap_get_values(ld, ent, "krbCanonicalName");
+ if (values != NULL && values[0] != NULL) {
+ st = krb5_ldap_parse_name(context, values[0], &cprinc);
+ if (st != 0)
+ goto cleanup;
}
- snprintf(filter, princlen, FILTER"%s))", filtuser);
+ ldap_value_free(values);
- if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0)
+ entry = k5alloc(sizeof(*entry), &st);
+ if (entry == NULL)
+ goto cleanup;
+ st = populate_krb5_db_entry(context, ldap_context, ld, ent,
+ cprinc != NULL ? cprinc : searchfor, entry);
+ if (st)
goto cleanup;
- GET_HANDLE();
- for (tree=0; tree < ntrees && !found; ++tree) {
-
- LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes);
- for (ent=ldap_first_entry(ld, result); ent != NULL && !found; ent=ldap_next_entry(ld, ent)) {
-
- /* get the associated directory user information */
- if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
- size_t i;
-
- /* a wild-card in a principal name can return a list of kerberos principals.
- * Make sure that the correct principal is returned.
- * NOTE: a principalname k* in ldap server will return all the principals starting with a k
- */
- for (i=0; values[i] != NULL; ++i) {
- if (strcmp(values[i], user) == 0) {
- found = TRUE;
- break;
- }
- }
- ldap_value_free(values);
-
- if (!found) /* no matching principal found */
- continue;
- }
-
- if ((values=ldap_get_values(ld, ent, "krbcanonicalname")) != NULL) {
- if (values[0] && strcmp(values[0], user) != 0) {
- /* We matched an alias, not the canonical name. */
- st = krb5_ldap_parse_principal_name(values[0], &cname);
- if (st != 0)
- goto cleanup;
- st = krb5_parse_name(context, cname, &cprinc);
- if (st != 0)
- goto cleanup;
- }
- ldap_value_free(values);
- if (!found)
- continue;
- }
-
- entry = k5alloc(sizeof(*entry), &st);
- if (entry == NULL)
- goto cleanup;
- if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent,
- cprinc ? cprinc : searchfor,
- entry)) != 0)
- goto cleanup;
- }
- ldap_msgfree(result);
- result = NULL;
- } /* for (tree=0 ... */
-
- if (found) {
- *entry_ptr = entry;
- entry = NULL;
- } else
- st = KRB5_KDB_NOENTRY;
+ *entry_ptr = entry;
+ entry = NULL;
cleanup:
ldap_msgfree(result);
krb5_db_free_principal(context, entry);
-
- if (filter)
- free (filter);
-
- if (subtree) {
- for (; ntrees; --ntrees)
- if (subtree[ntrees-1])
- free (subtree[ntrees-1]);
- free (subtree);
- }
-
- if (ldap_server_handle)
- krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
-
- if (user)
- free(user);
-
- if (filtuser)
- free(filtuser);
-
- if (cname)
- free(cname);
-
- if (cprinc)
- krb5_free_principal(context, cprinc);
-
+ krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
+ krb5_free_principal(context, cprinc);
return st;
}
@@ -755,13 +782,93 @@ validate_xargs(krb5_context context,
return 0;
}
+static krb5_error_code
+add_alias(krb5_context context, krb5_ldap_context *ldap_context,
+ krb5_ldap_server_handle *ldap_server_handle,
+ krb5_const_principal alias, krb5_const_principal target)
+{
+ krb5_error_code st;
+ LDAP *ld = ldap_server_handle->ldap_handle;
+ LDAPMessage *ent, *result = NULL;
+ LDAPMod **mods = NULL;
+ char **canon = NULL, **names = NULL, *user = NULL, *dn = NULL;
+ char *strval[2] = { NULL }, errbuf[1024];
+
+ st = search_princ(context, ldap_context, ldap_server_handle, target, NULL,
+ &ent, &result);
+ if (st)
+ goto cleanup;
+ if (ent == NULL) {
+ st = KRB5_KDB_NOENTRY;
+ k5_setmsg(context, st, _("target principal not found"));
+ goto cleanup;
+ }
+
+ dn = ldap_get_dn(ld, ent);
+ if (dn == NULL) {
+ ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
+ st = set_ldap_error(context, st, 0);
+ goto cleanup;
+ }
+ canon = ldap_get_values(ld, ent, "krbCanonicalName");
+ names = ldap_get_values(ld, ent, "krbPrincipalName");
+
+ /* Add a krbCanonicalName attribute if one isn't set. */
+ if (canon == NULL) {
+ if (ldap_count_values(names) != 1) {
+ st = KRB5_KDB_INTERNAL_ERROR;
+ k5_setmsg(context, st,
+ _("cannot add alias to entry with multiple "
+ "krbPrincipalName values and no krbCanonicalName "
+ "attribute"));
+ goto cleanup;
+ }
+ strval[0] = names[0];
+ st = krb5_add_str_mem_ldap_mod(&mods, "krbCanonicalName", LDAP_MOD_ADD,
+ strval);
+ if (st)
+ goto cleanup;
+ }
+
+ /* Add a krbPrincipalName value for the alias name. */
+ st = krb5_ldap_unparse_name(context, alias, &user);
+ if (st)
+ goto cleanup;
+ strval[0] = user;
+ st = krb5_add_str_mem_ldap_mod(&mods, "krbPrincipalName", LDAP_MOD_ADD,
+ strval);
+ if (st)
+ goto cleanup;
+
+ st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
+ if (st == LDAP_TYPE_OR_VALUE_EXISTS) {
+ st = KRB5_KDB_INUSE;
+ goto cleanup;
+ } else if (st != LDAP_SUCCESS) {
+ snprintf(errbuf, sizeof(errbuf), _("Alias modification failed: %s"),
+ ldap_err2string(st));
+ st = translate_ldap_error(st, OP_MOD);
+ k5_setmsg(context, st, "%s", errbuf);
+ goto cleanup;
+ }
+
+cleanup:
+ ldap_msgfree(result);
+ ldap_memfree(dn);
+ ldap_value_free(canon);
+ ldap_value_free(names);
+ krb5_free_unparsed_name(context, user);
+ ldap_mods_free(mods, 1);
+ return st;
+}
+
krb5_error_code
krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
char **db_args)
{
int kerberos_principal_object_type=0;
size_t l=0, ntrees=0, tre=0;
- krb5_error_code st=0, tempst=0;
+ krb5_error_code st=0;
LDAP *ld=NULL;
LDAPMessage *result=NULL, *ent=NULL;
char **subtreelist = NULL;
@@ -778,6 +885,7 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_ldap_server_handle *ldap_server_handle=NULL;
+ krb5_principal alias_target=NULL;
osa_princ_ent_rec princ_ent = {0};
xargs_t xargs = {0};
char *polname = NULL;
@@ -802,9 +910,20 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
goto cleanup;
}
+ /* If this is an alias entry, add an alias to the target and return. */
+ st = krb5_dbe_read_alias(context, entry, &alias_target);
+ if (st)
+ goto cleanup;
+ if (alias_target != NULL) {
+ st = add_alias(context, ldap_context, ldap_server_handle, entry->princ,
+ alias_target);
+ krb5_free_principal(context, alias_target);
+ goto cleanup;
+ }
+
/* get the principal information to act on */
- if (((st=krb5_unparse_name(context, entry->princ, &user)) != 0) ||
- ((st=krb5_ldap_unparse_principal_name(user)) != 0))
+ st = krb5_ldap_unparse_name(context, entry->princ, &user);
+ if (st)
goto cleanup;
filtuser = ldap_filter_correct(user);
if (filtuser == NULL) {
@@ -830,72 +949,29 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
goto cleanup;
if (entry->mask & KADM5_LOAD) {
- size_t tree = 0;
- int numlentries = 0;
-
- /* A load operation is special, will do a mix-in (add krbprinc
- * attrs to a non-krb object entry) if an object exists with a
- * matching krbprincipalname attribute so try to find existing
- * object and set principal_dn. This assumes that the
- * krbprincipalname attribute is unique (only one object entry has
- * a particular krbprincipalname attribute).
+ /*
+ * A load operation is special, will do a mix-in (add krbprinc attrs to
+ * a non-krb object entry) if an object exists with a matching
+ * krbprincipalname attribute so try to find existing object and set
+ * principal_dn. This assumes that the krbprincipalname attribute is
+ * unique (only one object entry has a particular krbprincipalname
+ * attribute).
*/
- if (asprintf(&filter, FILTER"%s))", filtuser) < 0) {
- filter = NULL;
- st = ENOMEM;
- goto cleanup;
- }
-
- /* get the current subtree list */
- if ((st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees)) != 0)
+ st = search_princ(context, ldap_context, ldap_server_handle,
+ entry->princ, principal_dn, &ent, &result);
+ if (st && st != KRB5_KDB_NOENTRY)
goto cleanup;
-
- found_entry = FALSE;
- /* search for entry with matching krbprincipalname attribute */
- for (tree = 0; found_entry == FALSE && tree < ntrees; ++tree) {
+ if (ent != NULL && principal_dn == NULL) {
+ /* Remember this DN to be modified later. */
+ principal_dn = ldap_get_dn(ld, ent);
if (principal_dn == NULL) {
- LDAP_SEARCH_1(subtreelist[tree], ldap_context->lrparams->search_scope, filter, principal_attributes, IGNORE_STATUS);
- } else {
- /* just look for entry with principal_dn */
- LDAP_SEARCH_1(principal_dn, LDAP_SCOPE_BASE, filter, principal_attributes, IGNORE_STATUS);
- }
- if (st == LDAP_SUCCESS) {
- numlentries = ldap_count_entries(ld, result);
- if (numlentries > 1) {
- st = EINVAL;
- k5_setmsg(context, st,
- _("operation can not continue, more than one "
- "entry with principal name \"%s\" found"),
- user);
- goto cleanup;
- } else if (numlentries == 1) {
- found_entry = TRUE;
- if (principal_dn == NULL) {
- ent = ldap_first_entry(ld, result);
- if (ent != NULL) {
- /* setting principal_dn will cause that entry to be modified further down */
- if ((principal_dn = ldap_get_dn(ld, ent)) == NULL) {
- ldap_get_option (ld, LDAP_OPT_RESULT_CODE, &st);
- st = set_ldap_error (context, st, 0);
- goto cleanup;
- }
- }
- }
- }
- } else if (st != LDAP_NO_SUCH_OBJECT) {
- /* could not perform search, return with failure */
- st = set_ldap_error (context, st, 0);
+ ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
+ st = set_ldap_error(context, st, 0);
goto cleanup;
}
- ldap_msgfree(result);
- result = NULL;
- /*
- * If it isn't found then assume a standalone princ entry is to
- * be created.
- */
- } /* end for (tree = 0; principal_dn == ... */
+ }
- if (found_entry == FALSE && principal_dn != NULL) {
+ if (ent == NULL && principal_dn != NULL) {
/*
* if principal_dn is null then there is code further down to
* deal with setting standalone_principal_dn. Also note that
@@ -959,12 +1035,9 @@ krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry,
* any of the subtrees
*/
if (xargs.dn_from_kbd == TRUE) {
- /* Get the current subtree list if we haven't already done so. */
- if (subtreelist == NULL) {
- st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees);
- if (st)
- goto cleanup;
- }
+ st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees);
+ if (st)
+ goto cleanup;
st = validate_xargs(context, ldap_server_handle, &xargs,
standalone_principal_dn, subtreelist, ntrees);
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 1c69dc7f9..41ac0d3b2 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -192,6 +192,7 @@ check-pytests: responder s2p s4u2proxy unlockiter s4u2self
$(RUNPYTEST) $(srcdir)/t_kdcoptions.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_replay.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_sendto_kdc.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_alias.py $(PYTESTFLAGS)
clean:
$(RM) adata conccache etinfo forward gcred hist hooks hrealm
diff --git a/src/tests/t_alias.py b/src/tests/t_alias.py
new file mode 100755
index 000000000..f52163241
--- /dev/null
+++ b/src/tests/t_alias.py
@@ -0,0 +1,124 @@
+from k5test import *
+
+realm = K5Realm(create_host=False)
+
+mark('getprinc')
+realm.addprinc('canon')
+realm.run([kadminl, 'alias', 'alias', 'canon at KRBTEST.COM'])
+realm.run([kadminl, 'getprinc', 'alias'],
+ expected_msg='Principal: canon at KRBTEST.COM')
+
+mark('delprinc')
+realm.run([kadminl, 'delprinc', 'alias'])
+realm.run([kadminl, 'getprinc', 'alias'], expected_code=1,
+ expected_msg='does not exist')
+realm.run([kadminl, 'getprinc', 'canon'], expected_msg=': canon at KRBTEST.COM')
+
+mark('no specified realm')
+realm.run([kadminl, 'alias', 'alias', 'canon'])
+realm.run([kadminl, 'getprinc', 'alias'], expected_msg=': canon at KRBTEST.COM')
+
+mark('cross-realm')
+realm.run([kadminl, 'alias', 'x', 'y at OTHER.REALM'], expected_code=1,
+ expected_msg='Alias target must be within the same realm')
+
+mark('alias as service principal')
+realm.extract_keytab('alias', realm.keytab)
+realm.run([kvno, 'alias'])
+realm.klist('user at KRBTEST.COM', 'alias at KRBTEST.COM')
+
+mark('alias as client principal')
+realm.kinit('alias', flags=['-k'])
+realm.klist('alias at KRBTEST.COM')
+realm.kinit('alias', flags=['-k', '-C'])
+realm.klist('canon at KRBTEST.COM')
+
+mark('chain')
+realm.run([kadminl, 'alias', 'a1', 'canon'])
+realm.run([kadminl, 'alias', 'a2', 'a1'])
+realm.run([kadminl, 'alias', 'a3', 'a2'])
+realm.run([kadminl, 'alias', 'a4', 'a3'])
+realm.run([kadminl, 'alias', 'a5', 'a4'])
+realm.run([kadminl, 'alias', 'a6', 'a5'])
+realm.run([kadminl, 'alias', 'a7', 'a6'])
+realm.run([kadminl, 'alias', 'a8', 'a7'])
+realm.run([kadminl, 'alias', 'a9', 'a8'])
+realm.run([kadminl, 'alias', 'a10', 'a9'])
+realm.run([kadminl, 'alias', 'a11', 'a10'])
+realm.run([kvno, 'a1'])
+realm.run([kvno, 'a2'])
+realm.run([kvno, 'a3'])
+realm.run([kvno, 'a4'])
+realm.run([kvno, 'a5'])
+realm.run([kvno, 'a6'])
+realm.run([kvno, 'a7'])
+realm.run([kvno, 'a8'])
+realm.run([kvno, 'a9'])
+realm.run([kvno, 'a10'])
+realm.run([kvno, 'a11'], expected_code=1,
+ expected_msg='Server a11 at KRBTEST.COM not found in Kerberos database')
+
+mark('circular chain')
+realm.run([kadminl, 'alias', 'selfalias', 'selfalias'])
+realm.run([kvno, 'selfalias'], expected_code=1,
+ expected_msg='Server selfalias at KRBTEST.COM not found')
+
+mark('blocking creations')
+realm.run([kadminl, 'addprinc', '-nokey', 'alias'], expected_code=1,
+ expected_msg='already exists')
+realm.run([kadminl, 'alias', 'alias', 'canon'], expected_code=1,
+ expected_msg='already exists')
+realm.run([kadminl, 'renprinc', 'user', 'alias'], expected_code=1,
+ expected_msg='already exists')
+
+# Non-resolving aliases being overwritable is emergent behavior;
+# change the tests if the behavior changes.
+mark('not blocking creations')
+realm.run([kadminl, 'alias', 'xa1', 'x'])
+realm.run([kadminl, 'alias', 'xa2', 'x'])
+realm.run([kadminl, 'alias', 'xa3', 'x'])
+realm.addprinc('xa1')
+realm.run([kadminl, 'getprinc', 'xa1'], expected_msg=': xa1 at KRBTEST.COM')
+realm.run([kadminl, 'alias', 'xa2', 'canon'])
+realm.run([kadminl, 'getprinc', 'xa2'], expected_msg=': canon at KRBTEST.COM')
+realm.run([kadminl, 'renprinc', 'xa1', 'xa3'])
+realm.run([kadminl, 'getprinc', 'xa3'], expected_msg=': xa3 at KRBTEST.COM')
+
+mark('renprinc')
+realm.run([kadminl, 'renprinc', 'alias', 'nalias'], expected_code=1,
+ expected_msg='Operation unsupported on alias principal name')
+
+mark('modprinc')
+realm.run([kadminl, 'modprinc', '+preauth', 'alias'])
+realm.run([kadminl, 'getprinc', 'canon'], expected_msg='REQUIRES_PRE_AUTH')
+
+mark('cpw')
+realm.run([kadminl, 'cpw', '-pw', 'pw', 'alias'])
+realm.run([kadminl, 'getprinc', 'canon'], expected_msg='vno 2,')
+realm.run([kadminl, 'cpw', '-e', 'aes256-cts', '-pw', 'pw', 'alias'])
+realm.run([kadminl, 'getprinc', 'canon'], expected_msg='vno 3,')
+realm.run([kadminl, 'cpw', '-randkey', 'alias'])
+realm.run([kadminl, 'getprinc', 'canon'], expected_msg='vno 4,')
+realm.run([kadminl, 'cpw', '-e', 'aes256-cts', '-randkey', 'alias'])
+realm.run([kadminl, 'getprinc', 'canon'], expected_msg='vno 5,')
+
+mark('listprincs')
+realm.run([kadminl, 'listprincs'], expected_msg='alias at KRBTEST.COM')
+
+mark('purgekeys')
+realm.run([kadminl, 'purgekeys', '-all', 'alias'])
+realm.run([kadminl, 'getprinc', 'canon'], expected_msg='Number of keys: 0')
+
+mark('setstr')
+realm.run([kadminl, 'setstr', 'alias', 'key', 'value'])
+realm.run([kadminl, 'getstrs', 'canon'], expected_msg='key: value')
+
+mark('getstrs')
+realm.run([kadminl, 'getstrs', 'alias'], expected_msg='key: value')
+
+mark('delstr')
+realm.run([kadminl, 'delstr', 'alias', 'key'])
+realm.run([kadminl, 'getstrs', 'canon'],
+ expected_msg='(No string attributes.)')
+
+success('alias tests')
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index 31a7fb871..fe762d57c 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -25,21 +25,33 @@ all_modify = make_client('all_modify')
all_rename = make_client('all_rename')
all_wildcard = make_client('all_wildcard')
all_extract = make_client('all_extract')
+all_alias = make_client('all_alias')
some_add = make_client('some_add')
some_changepw = make_client('some_changepw')
some_delete = make_client('some_delete')
some_inquire = make_client('some_inquire')
some_modify = make_client('some_modify')
some_rename = make_client('some_rename')
+some_extract = make_client('some_extract')
+some_alias = make_client('some_alias')
restricted_add = make_client('restricted_add')
restricted_modify = make_client('restricted_modify')
restricted_rename = make_client('restricted_rename')
+restricted_alias = make_client('restricted_alias')
wctarget = make_client('wctarget')
admin = make_client('user/admin')
none = make_client('none')
restrictions = make_client('restrictions')
onetwothreefour = make_client('one/two/three/four')
+realm.run([kadminl, 'alias', 'aliastonone', 'none'])
+aliastonone = os.path.join(realm.testdir, 'kadmin_ccache_aliastonone')
+realm.kinit('aliastonone', password('none'),
+ flags=['-S', 'kadmin/admin', '-c', aliastonone])
+
+realm.run([kadminl, 'alias', 'aliastounselected', 'unselected'])
+realm.run([kadminl, 'alias', 'aliastoselected', 'selected'])
+
realm.run([kadminl, 'addpol', '-minlife', '1 day', 'minlife'])
f = open(os.path.join(realm.testdir, 'acl'), 'w')
@@ -53,16 +65,27 @@ all_modify im
all_rename ad
all_wildcard x
all_extract ie
+all_alias am
some_add a selected
+some_add a aliastounselected
some_changepw c selected
+some_changepw c aliastounselected
some_delete d selected
+some_delete d aliastounselected
some_inquire i selected
+some_inquire i aliastounselected
some_modify im selected
+some_modify im aliastounselected
+some_extract ie selected
+some_extract ie aliastounselected
some_rename d from
some_rename a to
+some_alias a aliasname
+some_alias m canon
restricted_add a * +preauth
restricted_modify im * +preauth
restricted_rename ad * +preauth
+restricted_alias ai * +preauth
*/* d *2/*1
# The next line is a regression test for #8154; it is not used directly.
@@ -92,7 +115,13 @@ for pw in (['-pw', 'newpw'], ['-randkey']):
expected_msg=msg)
kadmin_as(some_changepw, ['cpw'] + args + ['unselected'],
expected_code=1, expected_msg=msg)
+ # Verify that the ACL check is canonicalized.
+ kadmin_as(some_changepw, ['cpw'] + args + ['aliastounselected'],
+ expected_code=1, expected_msg=msg)
+ kadmin_as(some_changepw, ['cpw'] + args + ['aliastoselected'])
kadmin_as(none, ['cpw'] + args + ['none'])
+ kadmin_as(aliastonone, ['cpw'] + args + ['none'],
+ expected_code=1, expected_msg=msg)
realm.run([kadminl, 'modprinc', '-policy', 'minlife', 'none'])
msg = "Current password's minimum life has not expired"
kadmin_as(none, ['cpw'] + args + ['none'], expected_code=1,
@@ -123,6 +152,12 @@ for ks in ([], ['-e', 'aes256-cts']):
expected_msg="Operation requires ``add'' privilege")
kadmin_as(some_add, ['addprinc'] + args + ['unselected'], expected_code=1,
expected_msg="Operation requires ``add'' privilege")
+ # Verify that the ACL check isn't canonicalized. (We need the alias
+ # to resolve or we will overwrite it, currently.)
+ realm.addprinc('unselected')
+ kadmin_as(some_add, ['addprinc'] + args + ['aliastounselected'],
+ expected_code=1, expected_msg='already exists')
+ realm.run([kadminl, 'delprinc', 'unselected'])
mark('delprinc')
realm.addprinc('unselected', 'pw')
@@ -134,6 +169,11 @@ kadmin_as(none, ['delprinc', 'unselected'], expected_code=1,
expected_msg="Operation requires ``delete'' privilege")
kadmin_as(some_delete, ['delprinc', 'unselected'], expected_code=1,
expected_msg="Operation requires ``delete'' privilege")
+# Verify that the ACL check isn't canonicalized.
+kadmin_as(some_delete, ['delprinc', 'aliastounselected'])
+realm.run([kadminl, 'alias', 'aliastounselected', 'unselected'])
+kadmin_as(some_delete, ['delprinc', 'aliastoselected'], expected_code=1,
+ expected_msg="Operation requires ``delete'' privilege")
realm.run([kadminl, 'delprinc', 'unselected'])
mark('getpol')
@@ -155,6 +195,11 @@ kadmin_as(none, ['getprinc', 'selected'], expected_code=1,
expected_msg="Operation requires ``get'' privilege")
kadmin_as(some_inquire, ['getprinc', 'unselected'], expected_code=1,
expected_msg="Operation requires ``get'' privilege")
+# Verify that the ACL check is canonicalized.
+kadmin_as(some_inquire, ['getprinc', 'aliastounselected'], expected_code=1,
+ expected_msg="Operation requires ``get'' privilege")
+kadmin_as(some_inquire, ['getprinc', 'aliastoselected'],
+ expected_msg='Principal: selected at KRBTEST.COM')
kadmin_as(none, ['getprinc', 'none'],
expected_msg='Principal: none at KRBTEST.COM')
realm.run([kadminl, 'delprinc', 'selected'])
@@ -176,6 +221,11 @@ kadmin_as(none, ['getstrs', 'selected'], expected_code=1,
expected_msg="Operation requires ``get'' privilege")
kadmin_as(some_inquire, ['getstrs', 'unselected'], expected_code=1,
expected_msg="Operation requires ``get'' privilege")
+# Verify that the ACL check is canonicalized.
+kadmin_as(some_inquire, ['getstrs', 'aliastounselected'], expected_code=1,
+ expected_msg="Operation requires ``get'' privilege")
+kadmin_as(some_inquire, ['getstrs', 'aliastoselected'],
+ expected_msg='key: value')
kadmin_as(none, ['getstrs', 'none'], expected_msg='(No string attributes.)')
realm.run([kadminl, 'delprinc', 'selected'])
realm.run([kadminl, 'delprinc', 'unselected'])
@@ -201,6 +251,10 @@ kadmin_as(all_inquire, ['modprinc', '-maxlife', '1 hour', 'selected'],
expected_msg="Operation requires ``modify'' privilege")
kadmin_as(some_modify, ['modprinc', '-maxlife', '1 hour', 'unselected'],
expected_code=1, expected_msg='Operation requires')
+# Verify that the ACL check is canonicalized.
+kadmin_as(some_modify, ['modprinc', '-maxlife', '1 hour', 'aliastounselected'],
+ expected_code=1, expected_msg='Operation requires')
+kadmin_as(some_modify, ['modprinc', '-maxlife', '2 hours', 'aliastoselected'])
realm.run([kadminl, 'delprinc', 'selected'])
realm.run([kadminl, 'delprinc', 'unselected'])
@@ -213,6 +267,10 @@ kadmin_as(none, ['purgekeys', 'selected'], expected_code=1,
expected_msg="Operation requires ``modify'' privilege")
kadmin_as(some_modify, ['purgekeys', 'unselected'], expected_code=1,
expected_msg="Operation requires ``modify'' privilege")
+# Verify that the ACL check is canonicalized.
+kadmin_as(some_modify, ['purgekeys', 'aliastounselected'], expected_code=1,
+ expected_msg="Operation requires ``modify'' privilege")
+kadmin_as(some_modify, ['purgekeys', 'aliastoselected'])
kadmin_as(none, ['purgekeys', 'none'])
realm.run([kadminl, 'delprinc', 'selected'])
realm.run([kadminl, 'delprinc', 'unselected'])
@@ -232,6 +290,15 @@ kadmin_as(some_rename, ['renprinc', 'from', 'notto'], expected_code=1,
realm.run([kadminl, 'renprinc', 'from', 'notfrom'])
kadmin_as(some_rename, ['renprinc', 'notfrom', 'to'], expected_code=1,
expected_msg="Insufficient authorization for operation")
+# Verify that the ACL check isn't canonicalized.
+realm.run([kadminl, 'alias', 'aliastofrom', 'from'])
+realm.run([kadminl, 'alias', 'aliastoto', 'to'])
+kadmin_as(some_rename, ['renprinc', 'aliastofrom', 'to'], expected_code=1,
+ expected_msg="Insufficient authorization for operation")
+kadmin_as(some_rename, ['renprinc', 'from', 'aliastoto'], expected_code=1,
+ expected_msg="Insufficient authorization for operation")
+realm.run([kadminl, 'delprinc', 'aliastofrom'])
+realm.run([kadminl, 'delprinc', 'aliastoto'])
kadmin_as(restricted_rename, ['renprinc', 'notfrom', 'to'], expected_code=1,
expected_msg="Insufficient authorization for operation")
realm.run([kadminl, 'delprinc', 'notfrom'])
@@ -245,6 +312,10 @@ kadmin_as(none, ['setstr', 'selected', 'key', 'value'], expected_code=1,
expected_msg="Operation requires ``modify'' privilege")
kadmin_as(some_modify, ['setstr', 'unselected', 'key', 'value'],
expected_code=1, expected_msg='Operation requires')
+# Verify that the ACL check is canonicalized.
+kadmin_as(some_modify, ['setstr', 'aliastounselected', 'key', 'value'],
+ expected_code=1, expected_msg='Operation requires')
+kadmin_as(some_modify, ['setstr', 'aliastoselected', 'key', 'value'])
realm.run([kadminl, 'delprinc', 'selected'])
realm.run([kadminl, 'delprinc', 'unselected'])
@@ -283,13 +354,24 @@ realm.run([kadminl, 'getprinc', 'type3'],
expected_msg='Maximum renewable life: 0 days 02:00:00')
mark('extract')
+realm.addprinc('selected')
+realm.addprinc('unselected')
realm.run([kadminl, 'addprinc', '-pw', 'pw', 'extractkeys'])
+msg = "Operation requires ``extract-keys'' privilege"
kadmin_as(all_wildcard, ['ktadd', '-norandkey', 'extractkeys'],
- expected_code=1,
- expected_msg="Operation requires ``extract-keys'' privilege")
+ expected_code=1, expected_msg=msg)
kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'])
realm.kinit('extractkeys', flags=['-k'])
+kadmin_as(some_extract, ['ktadd', '-norandkey', 'selected'])
+kadmin_as(some_extract, ['ktadd', '-norandkey', 'unselected'], expected_code=1,
+ expected_msg=msg)
+# Verify that the ACL check is canonicalized.
+kadmin_as(some_extract, ['ktadd', '-norandkey', 'aliastounselected'],
+ expected_code=1, expected_msg=msg)
+kadmin_as(some_extract, ['ktadd', '-norandkey', 'aliastoselected'])
os.remove(realm.keytab)
+realm.run([kadminl, 'delprinc', 'selected'])
+realm.run([kadminl, 'delprinc', 'unselected'])
mark('lockdown_keys')
kadmin_as(all_modify, ['modprinc', '+lockdown_keys', 'extractkeys'])
@@ -312,6 +394,22 @@ kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'])
realm.kinit('extractkeys', flags=['-k'])
os.remove(realm.keytab)
+mark('alias')
+kadmin_as(all_alias, ['alias', 'aliasname', 'canon'])
+realm.run([kadminl, 'delprinc', 'aliasname'])
+kadmin_as(some_alias, ['alias', 'aliasname', 'canon'])
+realm.run([kadminl, 'delprinc', 'aliasname'])
+kadmin_as(all_add, ['alias', 'aliasname', 'canon'], expected_code=1,
+ expected_msg="Insufficient authorization for operation")
+kadmin_as(all_inquire, ['alias', 'aliasname', 'canon'], expected_code=1,
+ expected_msg="Insufficient authorization for operation")
+kadmin_as(some_alias, ['alias', 'aliasname', 'notcanon'], expected_code=1,
+ expected_msg="Insufficient authorization for operation")
+kadmin_as(some_alias, ['alias', 'notaliasname', 'canon'], expected_code=1,
+ expected_msg="Insufficient authorization for operation")
+kadmin_as(restricted_alias, ['alias', 'aliasname', 'canon'], expected_code=1,
+ expected_msg="Insufficient authorization for operation")
+
# Verify that self-service key changes require an initial ticket.
mark('self-service initial ticket')
realm.run([kadminl, 'cpw', '-pw', password('none'), 'none'])
diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py
index c1a717b99..14d57923f 100755
--- a/src/tests/t_kdb.py
+++ b/src/tests/t_kdb.py
@@ -360,14 +360,14 @@ mark('LDAP service principal aliases')
# Test service principal aliases.
realm.addprinc('canon', password('canon'))
-ldap_modify('dn: krbPrincipalName=canon at KRBTEST.COM,cn=t1,cn=krb5\n'
- 'changetype: modify\n'
- 'add: krbPrincipalName\n'
- 'krbPrincipalName: alias at KRBTEST.COM\n'
- 'krbPrincipalName: ent at abc@KRBTEST.COM\n'
- '-\n'
- 'add: krbCanonicalName\n'
- 'krbCanonicalName: canon at KRBTEST.COM\n')
+realm.run([kadminl, 'alias', 'alias', 'canon'])
+realm.run([kadminl, 'alias', 'ent\\@abc', 'canon'])
+out = ldap_search('(krbPrincipalName=canon*)')
+if ('krbPrincipalName: canon at KRBTEST.COM' not in out or
+ 'krbPrincipalName: alias at KRBTEST.COM' not in out or
+ 'krbPrincipalName: ent at abc@KRBTEST.COM' not in out or
+ 'krbCanonicalName: canon at KRBTEST.COM' not in out):
+ fail('expected names not found in canon object')
realm.run([kadminl, 'getprinc', 'alias'],
expected_msg='Principal: canon at KRBTEST.COM\n')
realm.run([kadminl, 'getprinc', 'ent\\@abc'],
@@ -382,14 +382,7 @@ realm.kinit(realm.user_princ, password('user'), ['-S', 'alias'])
realm.klist(realm.user_princ, 'alias at KRBTEST.COM')
# Make sure an alias to the local TGS is still treated like an alias.
-ldap_modify('dn: krbPrincipalName=krbtgt/KRBTEST.COM at KRBTEST.COM,'
- 'cn=KRBTEST.COM,cn=krb5\n'
- 'changetype: modify\n'
- 'add:krbPrincipalName\n'
- 'krbPrincipalName: tgtalias at KRBTEST.COM\n'
- '-\n'
- 'add: krbCanonicalName\n'
- 'krbCanonicalName: krbtgt/KRBTEST.COM at KRBTEST.COM\n')
+realm.run([kadminl, 'alias', 'tgtalias', 'krbtgt/KRBTEST.COM'])
realm.run([kadminl, 'getprinc', 'tgtalias'],
expected_msg='Principal: krbtgt/KRBTEST.COM at KRBTEST.COM')
realm.kinit(realm.user_princ, password('user'))
@@ -429,6 +422,23 @@ realm.klist('ent\\@abc at KRBTEST.COM', 'alias at KRBTEST.COM')
# Test client name canonicalization in non-krbtgt AS reply
realm.kinit('alias', password('canon'), ['-C', '-S', 'kadmin/changepw'])
+# Test deleting an alias.
+mark('LDAP alias deletion')
+realm.run([kadminl, 'delprinc', 'alias'])
+realm.run([kadminl, 'getprinc', 'alias'], expected_code=1,
+ expected_msg='Principal does not exist')
+realm.run([kadminl, 'getprinc', 'ent\\@abc'],
+ expected_msg='Principal: canon at KRBTEST.COM\n')
+realm.run([kadminl, 'getprinc', 'canon'],
+ expected_msg='Principal: canon at KRBTEST.COM\n')
+
+# Test deleting a canonical name when an alias is present.
+realm.run([kadminl, 'delprinc', 'canon'])
+realm.run([kadminl, 'getprinc', 'canon'], expected_code=1,
+ expected_msg='Principal does not exist')
+realm.run([kadminl, 'getprinc', 'ent\\@abc'], expected_code=1,
+ expected_msg='Principal does not exist')
+
mark('LDAP password history')
# Test password history.
@@ -551,6 +561,8 @@ realm.run([kdb5_util, 'load', '-update', dumpfile])
out = realm.run([kadminl, 'getprinc', 'pwuser'])
if 'Password expiration date: [never]' in out:
fail('pw_expiration not preserved across dump and load')
+realm.run([kadminl, 'getprinc', 'tgtalias'],
+ expected_msg='Principal: krbtgt/KRBTEST.COM at KRBTEST.COM')
# Destroy the realm.
kldaputil(['destroy', '-f'])
diff --git a/src/tests/t_tabdump.py b/src/tests/t_tabdump.py
index 49531bf49..54117467f 100755
--- a/src/tests/t_tabdump.py
+++ b/src/tests/t_tabdump.py
@@ -19,7 +19,13 @@ def checkkeys(rows, dumptype, names):
realm = K5Realm(start_kdc=False, get_creds=False)
+realm.run([kadminl, 'alias', 'useralias', 'user'])
+rows = getrows('alias')
+checkkeys(rows, 'alias', ["aliasname", "targetname"])
+if (rows[0]['aliasname'] != 'useralias at KRBTEST.COM' or
+ rows[0]['targetname'] != 'user at KRBTEST.COM'):
+ fail('tabdump alias principal names')
rows = getrows('keyinfo')
checkkeys(rows, 'keyinfo',
More information about the cvs-krb5
mailing list