diff options
author | Sarah Day <sarahday@mit.edu> | 2016-04-29 10:26:31 -0400 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2016-05-23 12:46:51 -0400 |
commit | 2ac75e548afadde4f87f20fcc1ee1472cdac3fed (patch) | |
tree | 0fa2d0bf44960f7399448b0ffe88c2f9835b0dc4 /src/plugins | |
parent | c38838be956ce72fcd7142f14bc374dc13dd8bb2 (diff) | |
download | krb5-2ac75e548afadde4f87f20fcc1ee1472cdac3fed.zip krb5-2ac75e548afadde4f87f20fcc1ee1472cdac3fed.tar.gz krb5-2ac75e548afadde4f87f20fcc1ee1472cdac3fed.tar.bz2 |
Implement principal renaming in LDAP
The generic method of renaming principals (by adding a new entry and
deleting the old one) does not work in LDAP. Add an LDAP
implementation of rename that properly renames the DN and attributes
when necessary.
[ghudson@mit.edu: minor naming changes and code simplifications]
ticket: 8065
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/kdb/ldap/ldap_exp.c | 2 | ||||
-rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c | 52 | ||||
-rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.h | 8 | ||||
-rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c | 216 | ||||
-rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h | 7 | ||||
-rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c | 2 | ||||
-rw-r--r-- | src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports | 1 |
7 files changed, 286 insertions, 2 deletions
diff --git a/src/plugins/kdb/ldap/ldap_exp.c b/src/plugins/kdb/ldap/ldap_exp.c index 66f4891..d524941 100644 --- a/src/plugins/kdb/ldap/ldap_exp.c +++ b/src/plugins/kdb/ldap/ldap_exp.c @@ -61,7 +61,7 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_ldap, kdb_function_table) = { /* free_principal */ krb5_ldap_free_principal, /* put_principal */ krb5_ldap_put_principal, /* delete_principal */ krb5_ldap_delete_principal, - /* rename_principal */ NULL, + /* rename_principal */ krb5_ldap_rename_principal, /* iterate */ krb5_ldap_iterate, /* create_policy */ krb5_ldap_create_password_policy, /* get_policy */ krb5_ldap_get_password_policy, diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c index b29a944..32efc4f 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c @@ -799,6 +799,48 @@ get_str_from_tl_data(krb5_context context, krb5_db_entry *entry, int type, return 0; } +/* + * Replace the relative DN component of dn with newrdn. + */ +krb5_error_code +replace_rdn(krb5_context context, const char *dn, const char *newrdn, + char **newdn_out) +{ + krb5_error_code ret; + LDAPDN ldn = NULL; + LDAPRDN lrdn = NULL; + char *next; + + *newdn_out = NULL; + + ret = ldap_str2dn(dn, &ldn, LDAP_DN_FORMAT_LDAPV3); + if (ret != LDAP_SUCCESS || ldn[0] == NULL) { + ret = EINVAL; + goto cleanup; + } + + ret = ldap_str2rdn(newrdn, &lrdn, &next, LDAP_DN_FORMAT_LDAPV3); + if (ret != LDAP_SUCCESS) { + ret = EINVAL; + goto cleanup; + } + + ldap_rdnfree(ldn[0]); + ldn[0] = lrdn; + lrdn = NULL; + + ret = ldap_dn2str(ldn, newdn_out, LDAP_DN_FORMAT_LDAPV3); + if (ret != LDAP_SUCCESS) + ret = KRB5_KDB_SERVER_INTERNAL_ERR; + +cleanup: + if (ldn != NULL) + ldap_dnfree(ldn); + if (lrdn != NULL) + ldap_rdnfree(lrdn); + return ret; +} + krb5_error_code krb5_get_userdn(krb5_context context, krb5_db_entry *entry, char **userdn) { @@ -1069,6 +1111,16 @@ krb5_add_int_mem_ldap_mod(LDAPMod ***list, char *attribute, int op, int value) } krb5_error_code +krb5_ldap_modify_ext(krb5_context context, LDAP *ld, const char *dn, + LDAPMod **mods, int op) +{ + int ret; + + ret = ldap_modify_ext_s(ld, dn, mods, NULL, NULL); + return (ret == LDAP_SUCCESS) ? 0 : set_ldap_error(context, ret, op); +} + +krb5_error_code krb5_ldap_lock(krb5_context kcontext, int mode) { krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP; diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.h index fb4f3ce..9ea5dd5 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.h +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.h @@ -60,6 +60,10 @@ krb5_error_code krb5_get_userdn(krb5_context, krb5_db_entry *, char **); krb5_error_code +replace_rdn(krb5_context context, const char *dn, const char *newrdn, + char **newdn); + +krb5_error_code store_tl_data(krb5_tl_data *, int, void *); krb5_error_code @@ -93,6 +97,10 @@ krb5_error_code krb5_add_int_mem_ldap_mod(LDAPMod ***, char *, int , int); krb5_error_code +krb5_ldap_modify_ext(krb5_context context, LDAP *ld, const char *dn, + LDAPMod **mods, int op); + +krb5_error_code krb5_ldap_free_mod_array(LDAPMod **); krb5_error_code diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c index d4802c5..56839ff 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c @@ -352,6 +352,222 @@ cleanup: return st; } +/* + * Set *res will to 1 if entry is a standalone principal entry, 0 if not. On + * error, the value of *res is not defined. + */ +static inline krb5_error_code +is_standalone_principal(krb5_context kcontext, krb5_db_entry *entry, int *res) +{ + krb5_error_code code; + + code = krb5_get_princ_type(kcontext, entry, res); + if (!code) + *res = (*res == KDB_STANDALONE_PRINCIPAL_OBJECT) ? 1 : 0; + 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. + * + * NOTE: Not every LDAP ds supports deleting the old rdn. If that is desired, + * it will have to be deleted afterwards. + */ +static krb5_error_code +rename_principal_rdn(krb5_context context, LDAP *ld, const char *dn, + const char *newprinc, char **newdn_out) +{ + int ret; + char *newrdn = NULL; + + *newdn_out = NULL; + + ret = asprintf(&newrdn, "krbprincipalname=%s", newprinc); + if (ret < 0) + return ENOMEM; + + /* + * ldap_rename_s takes a deleteoldrdn parameter, but setting it to 1 fails + * on 389 Directory Server (as of version 1.3.5.4) if the old RDN value + * contains uppercase letters. Instead, change the RDN without deleting + * the old value and delete it later. + */ + ret = ldap_rename_s(ld, dn, newrdn, NULL, 0, NULL, NULL); + if (ret == -1) { + ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ret); + ret = set_ldap_error(context, ret, OP_MOD); + goto cleanup; + } + + ret = replace_rdn(context, dn, newrdn, newdn_out); + +cleanup: + free(newrdn); + return ret; +} + +/* + * Rename a principal. + */ +krb5_error_code +krb5_ldap_rename_principal(krb5_context context, krb5_const_principal source, + krb5_const_principal target) +{ + int is_standalone; + krb5_error_code st; + char *suser = NULL, *tuser = NULL, *strval[2], *dn = NULL, *newdn = NULL; + krb5_db_entry *entry = NULL; + krb5_kvno mkvno; + struct berval **bersecretkey = NULL; + kdb5_dal_handle *dal_handle = NULL; + krb5_ldap_context *ldap_context = NULL; + krb5_ldap_server_handle *ldap_server_handle = NULL; + LDAP *ld = NULL; + LDAPMod **mods = NULL; + + /* Clear the global error string */ + krb5_clear_error_message(context); + + SETUP_CONTEXT(); + if (ldap_context->lrparams == NULL || ldap_context->container_dn == NULL) + return EINVAL; + + /* get ldap handle */ + GET_HANDLE(); + + /* Pass no flags. Principal aliases won't be returned, which is a good + * thing since we don't support renaming aliases. */ + st = krb5_ldap_get_principal(context, source, 0, &entry); + if (st) + goto cleanup; + + st = is_standalone_principal(context, entry, &is_standalone); + if (st) + goto cleanup; + + st = krb5_get_userdn(context, entry, &dn); + if (st) + goto cleanup; + if (dn == NULL) { + st = EINVAL; + k5_setmsg(context, st, _("dn information missing")); + goto cleanup; + } + + st = unparse_principal_name(context, source, &suser); + if (st) + goto cleanup; + st = unparse_principal_name(context, target, &tuser); + if (st) + goto cleanup; + + /* Specialize the salt and store it first so that in case of an error the + * correct salt will still be used. */ + st = krb5_dbe_specialize_salt(context, entry); + if (st) + goto cleanup; + + st = krb5_dbe_lookup_mkvno(context, entry, &mkvno); + if (st) + goto cleanup; + + bersecretkey = krb5_encode_krbsecretkey(entry->key_data, entry->n_key_data, + mkvno); + if (bersecretkey == NULL) { + st = ENOMEM; + goto cleanup; + } + + st = krb5_add_ber_mem_ldap_mod(&mods, "krbPrincipalKey", + LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, + bersecretkey); + if (st != 0) + goto cleanup; + + /* Update the principal. */ + st = krb5_ldap_modify_ext(context, ld, dn, mods, OP_MOD); + if (st) + goto cleanup; + ldap_mods_free(mods, 1); + mods = NULL; + + /* If this is a standalone principal, we want to rename the DN of the LDAP + * entry. If not, we will modify the entry without changing its DN. */ + if (is_standalone) { + st = rename_principal_rdn(context, ld, dn, tuser, &newdn); + if (st) + goto cleanup; + free(dn); + dn = newdn; + newdn = NULL; + } + + /* There can be more than one krbPrincipalName, so we have to delete + * the old one and add the new one. */ + strval[0] = suser; + strval[1] = NULL; + st = krb5_add_str_mem_ldap_mod(&mods, "krbPrincipalName", LDAP_MOD_DELETE, + strval); + if (st) + goto cleanup; + + strval[0] = tuser; + strval[1] = NULL; + if (!is_standalone) { + st = krb5_add_str_mem_ldap_mod(&mods, "krbPrincipalName", LDAP_MOD_ADD, + strval); + if (st) + goto cleanup; + } + + st = krb5_add_str_mem_ldap_mod(&mods, "krbCanonicalName", LDAP_MOD_REPLACE, + strval); + if (st) + goto cleanup; + + /* Update the principal. */ + st = krb5_ldap_modify_ext(context, ld, dn, mods, OP_MOD); + if (st) + goto cleanup; + +cleanup: + free(dn); + free(suser); + free(tuser); + krb5_ldap_free_principal(context, entry); + ldap_mods_free(mods, 1); + krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); + return st; +} /* * Function: krb5_ldap_unparse_principal_name diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h index 2e01592..752f54f 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h @@ -105,6 +105,9 @@ krb5_ldap_get_principal(krb5_context , krb5_const_principal , krb5_error_code krb5_ldap_delete_principal(krb5_context, krb5_const_principal); +krb5_error_code +krb5_ldap_rename_principal(krb5_context context, krb5_const_principal source, + krb5_const_principal target); void krb5_ldap_free_principal(krb5_context, krb5_db_entry *); @@ -128,6 +131,10 @@ krb5_ldap_unparse_principal_name(char *); krb5_error_code krb5_ldap_parse_principal_name(char *, char **); +struct berval** +krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data, + krb5_kvno mkvno); + krb5_error_code krb5_decode_histkey(krb5_context, struct berval **, osa_princ_ent_rec *); diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c index 79c4cf0..74bc361 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c @@ -503,7 +503,7 @@ cleanup: } /* Decoding ASN.1 encoded key */ -static struct berval ** +struct berval ** krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data, krb5_kvno mkvno) { diff --git a/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports b/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports index 36bde5a..9d1db88 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports +++ b/src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports @@ -9,6 +9,7 @@ krb5_ldap_read_server_params krb5_ldap_put_principal krb5_ldap_get_principal krb5_ldap_delete_principal +krb5_ldap_rename_principal krb5_ldap_free_principal krb5_ldap_iterate krb5_ldap_read_krbcontainer_dn |