aboutsummaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authorSarah Day <sarahday@mit.edu>2016-04-29 10:26:31 -0400
committerGreg Hudson <ghudson@mit.edu>2016-05-23 12:46:51 -0400
commit2ac75e548afadde4f87f20fcc1ee1472cdac3fed (patch)
tree0fa2d0bf44960f7399448b0ffe88c2f9835b0dc4 /src/plugins
parentc38838be956ce72fcd7142f14bc374dc13dd8bb2 (diff)
downloadkrb5-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.c2
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c52
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.h8
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c216
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.h7
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c2
-rw-r--r--src/plugins/kdb/ldap/libkdb_ldap/libkdb_ldap.exports1
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