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
**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.
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)
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
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:
* 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 */
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
* 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);
/*
* 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
* 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,
*
* 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:
(*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
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 */
* 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:
/* 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*/
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)
{
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);
request kadmin_renameprinc, "Rename principal",
rename_principal, renprinc;
+request kadmin_addalias, "Add alias",
+ add_alias, alias;
+
request kadmin_cpw, "Change password",
change_password, cpw;
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;
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},
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,
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);
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;
}
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);
#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);
* 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. */
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)
vt->getpol = acl_getpol;
vt->listpols = acl_listpols;
vt->iprop = acl_iprop;
+ vt->addalias = acl_addalias;
return 0;
}
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;
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);
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;
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;
}
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
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);
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);
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;
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;
}
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;
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);
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;
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);
}
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);
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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);
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;
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)
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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;
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) {
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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);
}
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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,
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);
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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,
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);
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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,
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);
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
/* 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;
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;
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;
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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);
}
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;
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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;
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;
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;
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;
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;
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;
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);
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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);
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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);
}
exit_func:
+ kadm5_free_principal_ent(handle, &rec);
stub_cleanup(handle, prime_arg, &client_name, &service_name);
return TRUE;
}
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;
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;
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,
}
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);
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;
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__ */
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);
}
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;
+}
(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);
+}
kadm5_chpass_principal
kadm5_chpass_principal_3
kadm5_chpass_principal_util
+kadm5_create_alias
kadm5_create_policy
kadm5_create_principal
kadm5_create_principal_3
krb5_klog_set_context
krb5_klog_syslog
krb5_string_to_keysalts
+xdr_calias_arg
xdr_chpass3_arg
xdr_chpass_arg
xdr_chrand3_arg
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
};
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
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__ */
}
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);
+}
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__ */
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;
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;
+}
kadm5_chpass_principal
kadm5_chpass_principal_3
kadm5_chpass_principal_util
+kadm5_create_alias
kadm5_create_policy
kadm5_create_principal
kadm5_create_principal_3
master_princ
osa_free_princ_ent
passwd_check
+xdr_calias_arg
xdr_chpass3_arg
xdr_chpass_arg
xdr_chrand3_arg
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 {
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);
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,
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;
+}
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;
}
kdb_vftabl *v;
krb5_error_code status;
krb5_db_entry *entry;
+ krb5_boolean eq;
status = get_vftabl(kcontext, &v);
if (status)
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);
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);
+}
ulog_set_last
xdr_kdb_incr_update_t
krb5_dbe_sort_key_data
+krb5_dbe_make_alias_entry
+krb5_dbe_read_alias
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
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,
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;
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= ... */
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));
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.
*
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;
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;
}
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,
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.
*/
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;
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;
}
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;
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;
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) {
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
* 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);
$(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
--- /dev/null
+from k5test import *
+
+realm = K5Realm(create_host=False)
+
+mark('getprinc')
+realm.addprinc('canon')
+realm.run([kadminl, 'alias', 'alias', 'canon@KRBTEST.COM'])
+realm.run([kadminl, 'getprinc', 'alias'],
+ expected_msg='Principal: canon@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@KRBTEST.COM')
+
+mark('no specified realm')
+realm.run([kadminl, 'alias', 'alias', 'canon'])
+realm.run([kadminl, 'getprinc', 'alias'], expected_msg=': canon@KRBTEST.COM')
+
+mark('cross-realm')
+realm.run([kadminl, 'alias', 'x', 'y@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@KRBTEST.COM', 'alias@KRBTEST.COM')
+
+mark('alias as client principal')
+realm.kinit('alias', flags=['-k'])
+realm.klist('alias@KRBTEST.COM')
+realm.kinit('alias', flags=['-k', '-C'])
+realm.klist('canon@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@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@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@KRBTEST.COM')
+realm.run([kadminl, 'alias', 'xa2', 'canon'])
+realm.run([kadminl, 'getprinc', 'xa2'], expected_msg=': canon@KRBTEST.COM')
+realm.run([kadminl, 'renprinc', 'xa1', 'xa3'])
+realm.run([kadminl, 'getprinc', 'xa3'], expected_msg=': xa3@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@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')
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')
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.
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,
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')
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')
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@KRBTEST.COM')
kadmin_as(none, ['getprinc', 'none'],
expected_msg='Principal: none@KRBTEST.COM')
realm.run([kadminl, 'delprinc', 'selected'])
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'])
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'])
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'])
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'])
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'])
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'])
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'])
# Test service principal aliases.
realm.addprinc('canon', password('canon'))
-ldap_modify('dn: krbPrincipalName=canon@KRBTEST.COM,cn=t1,cn=krb5\n'
- 'changetype: modify\n'
- 'add: krbPrincipalName\n'
- 'krbPrincipalName: alias@KRBTEST.COM\n'
- 'krbPrincipalName: ent@abc@KRBTEST.COM\n'
- '-\n'
- 'add: krbCanonicalName\n'
- 'krbCanonicalName: canon@KRBTEST.COM\n')
+realm.run([kadminl, 'alias', 'alias', 'canon'])
+realm.run([kadminl, 'alias', 'ent\\@abc', 'canon'])
+out = ldap_search('(krbPrincipalName=canon*)')
+if ('krbPrincipalName: canon@KRBTEST.COM' not in out or
+ 'krbPrincipalName: alias@KRBTEST.COM' not in out or
+ 'krbPrincipalName: ent@abc@KRBTEST.COM' not in out or
+ 'krbCanonicalName: canon@KRBTEST.COM' not in out):
+ fail('expected names not found in canon object')
realm.run([kadminl, 'getprinc', 'alias'],
expected_msg='Principal: canon@KRBTEST.COM\n')
realm.run([kadminl, 'getprinc', 'ent\\@abc'],
realm.klist(realm.user_princ, 'alias@KRBTEST.COM')
# Make sure an alias to the local TGS is still treated like an alias.
-ldap_modify('dn: krbPrincipalName=krbtgt/KRBTEST.COM@KRBTEST.COM,'
- 'cn=KRBTEST.COM,cn=krb5\n'
- 'changetype: modify\n'
- 'add:krbPrincipalName\n'
- 'krbPrincipalName: tgtalias@KRBTEST.COM\n'
- '-\n'
- 'add: krbCanonicalName\n'
- 'krbCanonicalName: krbtgt/KRBTEST.COM@KRBTEST.COM\n')
+realm.run([kadminl, 'alias', 'tgtalias', 'krbtgt/KRBTEST.COM'])
realm.run([kadminl, 'getprinc', 'tgtalias'],
expected_msg='Principal: krbtgt/KRBTEST.COM@KRBTEST.COM')
realm.kinit(realm.user_princ, password('user'))
# 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@KRBTEST.COM\n')
+realm.run([kadminl, 'getprinc', 'canon'],
+ expected_msg='Principal: canon@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.
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@KRBTEST.COM')
# Destroy the realm.
kldaputil(['destroy', '-f'])
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@KRBTEST.COM' or
+ rows[0]['targetname'] != 'user@KRBTEST.COM'):
+ fail('tabdump alias principal names')
rows = getrows('keyinfo')
checkkeys(rows, 'keyinfo',