From: Greg Hudson Date: Wed, 15 Jan 2025 02:25:51 +0000 (-0500) Subject: Add alias support X-Git-Tag: krb5-1.22-beta1~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5d3fe31bf1dc48e8ee946bf65428611958cac329;p=thirdparty%2Fkrb5.git 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. --- diff --git a/doc/admin/admin_commands/kadmin_local.rst b/doc/admin/admin_commands/kadmin_local.rst index 2435b3c361..b4edc79243 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 444c58bcd9..8147e9766e 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 65542c1a4e..908dfd1e7e 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 2fd07242a0..685ec272f4 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 d2252d505b..7beee28df8 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 d514e99beb..3f9392362d 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__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 f4f3730f4d..cca6d63503 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__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 372457039f..f40c69e1c6 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 f08eac153d..bf63d04307 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 705e41840e..62fb4d6a8f 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 da55c2d78d..d4b7b73337 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 081b20a8b6..224b121904 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 4d265add7c..5f51a9911c 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 ce9ace36ae..a04fb23db3 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 f0e43d9aea..867d8b040d 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 87f67874a0..b2371e67ac 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 296c86fa61..4af1ea2256 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 9da98451e4..12a2598c23 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 96d9d19322..9764900935 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 c8d844e4c7..6dd56872ac 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 9ed7d52dcb..43b81e8aeb 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 cf07e8068c..f58ddf6d72 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 9efe49a373..bde85478ad 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 5e052dd90c..8a2def8ff7 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 433f4915b2..ce2e9197b5 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 df337bc32e..c533d743a9 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 14c02a7f1a..3b39ab590d 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 4efcaf9941..709e7f5d30 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 444c16ed75..1557937f22 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 a0897f5e88..b6db28d8da 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 574bab92f6..11cd0bdd16 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 1b08ec1a6f..f803df1d3a 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 4ff5219c25..234a2b53b7 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 72a9f960b4..71948aac3f 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 d929d325c3..ae4e03f8cf 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 1c69dc7f98..41ac0d3b2a 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 0000000000..f521632411 --- /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@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') diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py index 31a7fb871e..fe762d57c3 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@KRBTEST.COM') kadmin_as(none, ['getprinc', 'none'], expected_msg='Principal: none@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 c1a717b998..14d57923fd 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@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'], @@ -382,14 +382,7 @@ realm.kinit(realm.user_princ, password('user'), ['-S', 'alias']) 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')) @@ -429,6 +422,23 @@ realm.klist('ent\\@abc@KRBTEST.COM', 'alias@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@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. @@ -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@KRBTEST.COM') # Destroy the realm. kldaputil(['destroy', '-f']) diff --git a/src/tests/t_tabdump.py b/src/tests/t_tabdump.py index 49531bf498..54117467f5 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@KRBTEST.COM' or + rows[0]['targetname'] != 'user@KRBTEST.COM'): + fail('tabdump alias principal names') rows = getrows('keyinfo') checkkeys(rows, 'keyinfo',