summaryrefslogtreecommitdiffstats
path: root/daemons
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2010-10-19 17:11:31 -0400
committerSimo Sorce <ssorce@redhat.com>2010-10-28 07:58:31 -0400
commit984942ee49436ee0355c5b9d0f40019d2bfc22d7 (patch)
tree1a6cdf39beea91a6f72cb019cd20f7dbc9dd1b59 /daemons
parent97bcbdec2f9b2c2b88208ad969fb378a29fe9378 (diff)
downloadfreeipa-984942ee49436ee0355c5b9d0f40019d2bfc22d7.tar.gz
freeipa-984942ee49436ee0355c5b9d0f40019d2bfc22d7.tar.xz
freeipa-984942ee49436ee0355c5b9d0f40019d2bfc22d7.zip
Add new plugin used to modify related attributes after a modrdn operation.
Diffstat (limited to 'daemons')
-rw-r--r--daemons/configure.ac1
-rw-r--r--daemons/ipa-slapi-plugins/Makefile.am1
-rw-r--r--daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am42
-rw-r--r--daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c991
-rw-r--r--daemons/ipa-slapi-plugins/ipa-modrdn/modrdn-conf.ldif15
5 files changed, 1050 insertions, 0 deletions
diff --git a/daemons/configure.ac b/daemons/configure.ac
index 53240469f..b7ad33fed 100644
--- a/daemons/configure.ac
+++ b/daemons/configure.ac
@@ -282,6 +282,7 @@ AC_CONFIG_FILES([
ipa-slapi-plugins/ipa-winsync/Makefile
ipa-slapi-plugins/ipa-version/Makefile
ipa-slapi-plugins/ipa-uuid/Makefile
+ ipa-slapi-plugins/ipa-modrdn/Makefile
])
AC_OUTPUT
diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am
index ea82c3927..1ae2351dd 100644
--- a/daemons/ipa-slapi-plugins/Makefile.am
+++ b/daemons/ipa-slapi-plugins/Makefile.am
@@ -2,6 +2,7 @@ NULL =
SUBDIRS = \
ipa-enrollment \
+ ipa-modrdn \
ipa-pwd-extop \
ipa-uuid \
ipa-version \
diff --git a/daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am b/daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am
new file mode 100644
index 000000000..577062464
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am
@@ -0,0 +1,42 @@
+NULL =
+
+INCLUDES = \
+ -I. \
+ -I$(srcdir) \
+ -I/usr/include/dirsrv \
+ -DPREFIX=\""$(prefix)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ $(MOZLDAP_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(NULL)
+
+plugindir = $(libdir)/dirsrv/plugins
+plugin_LTLIBRARIES = \
+ libipa_modrdn.la \
+ $(NULL)
+
+libipa_modrdn_la_SOURCES = \
+ ipa_modrdn.c \
+ $(NULL)
+
+libipa_modrdn_la_LDFLAGS = -avoid-version
+
+libipa_modrdn_la_LIBADD = \
+ $(MOZLDAP_LIBS) \
+ $(NULL)
+
+appdir = $(IPA_DATA_DIR)
+app_DATA = \
+ modrdn-conf.ldif \
+ $(NULL)
+
+EXTRA_DIST = \
+ $(app_DATA) \
+ $(NULL)
+
+MAINTAINERCLEANFILES = \
+ *~ \
+ Makefile.in
diff --git a/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
new file mode 100644
index 000000000..6e484960a
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
@@ -0,0 +1,991 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/**
+ * IPA MODRDN plug-in
+ */
+#include <string.h>
+#include <stdbool.h>
+#include "slapi-plugin.h"
+#include "nspr.h"
+#include "prclist.h"
+
+#define IPAMODRDN_PLUGIN_NAME "ipa-modrdn-plugin"
+#define IPAMODRDN_PLUGIN_VERSION 0x00010000
+
+#define IPAMODRDN_DN "cn=IPA MODRDN,cn=plugins,cn=config" /* temporary */
+
+#define EOK 0
+#define EFAIL -1
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
+#endif
+
+#define log_func discard_const(__func__)
+
+#define LOG(fmt, ...) \
+ slapi_log_error(SLAPI_LOG_PLUGIN, \
+ IPAMODRDN_PLUGIN_NAME, \
+ fmt, ##__VA_ARGS__)
+
+#define LOG_CONFIG(fmt, ...) \
+ slapi_log_error(SLAPI_LOG_CONFIG, \
+ IPAMODRDN_PLUGIN_NAME, \
+ fmt, ##__VA_ARGS__)
+
+#define LOG_FATAL(fmt, ...) \
+ slapi_log_error(SLAPI_LOG_FATAL, log_func, \
+ "[file %s, line %d]: " fmt, \
+ __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define LOG_TRACE(fmt, ...) \
+ slapi_log_error(SLAPI_LOG_TRACE, log_func, fmt, ##__VA_ARGS__)
+
+#define LOG_OOM() LOG_FATAL("Out of Memory!\n")
+
+/**
+ * IPA MODRDN config types
+ */
+#define IPAMODRDN_SATTR "ipaModRDNsourceAttr"
+#define IPAMODRDN_TATTR "ipaModRDNtargetAttr"
+#define IPAMODRDN_PREFIX "ipaModRDNprefix"
+#define IPAMODRDN_SUFFIX "ipaModRDNsuffix"
+#define IPAMODRDN_FILTER "ipaModRDNfilter"
+#define IPAMODRDN_SCOPE "ipaModRDNscope"
+
+#define IPAMODRDN_FEATURE_DESC "IPA MODRDN"
+#define IPAMODRDN_PLUGIN_DESC "IPA MODRDN plugin"
+#define IPAMODRDN_POSTOP_DESC "IPA MODRDN postop plugin"
+
+static Slapi_PluginDesc pdesc = {
+ IPAMODRDN_FEATURE_DESC,
+ "Red Hat, Inc.",
+ "1.0",
+ IPAMODRDN_PLUGIN_DESC
+};
+
+/**
+ * linked list of config entries
+ */
+
+struct configEntry {
+ PRCList list;
+ char *dn;
+ char *sattr;
+ char *tattr;
+ char *prefix;
+ char *suffix;
+ char *filter;
+ Slapi_Filter *slapi_filter;
+ char *scope;
+};
+
+static PRCList *ipamodrdn_global_config = NULL;
+static PRRWLock *g_ipamodrdn_cache_lock;
+
+static void *_PluginID = NULL;
+static char *_PluginDN = NULL;
+
+static int g_plugin_started = 0;
+
+
+/**
+ *
+ * management functions
+ *
+ */
+int ipamodrdn_init(Slapi_PBlock * pb);
+static int ipamodrdn_start(Slapi_PBlock * pb);
+static int ipamodrdn_close(Slapi_PBlock * pb);
+
+/**
+ *
+ * Local operation functions
+ *
+ */
+static int ipamodrdn_load_plugin_config();
+static int ipamodrdn_parse_config_entry(Slapi_Entry * e, bool apply);
+static void ipamodrdn_delete_config();
+static void ipamodrdn_free_config_entry(struct configEntry ** entry);
+
+/**
+ *
+ * helpers
+ *
+ */
+static char *ipamodrdn_get_dn(Slapi_PBlock * pb);
+static int ipamodrdn_dn_is_config(char *dn);
+static int ipamodrdn_list_contains_attr(char **list, char *attr);
+static int ipamodrdn_list_contains_attrs(char **list, char **attrs);
+static void ipamodrdn_list_remove_attr(char **list, char *attr);
+
+/**
+ *
+ * the ops (where the real work is done)
+ *
+ */
+static int ipamodrdn_config_check_post_op(Slapi_PBlock * pb);
+static int ipamodrdn_post_op(Slapi_PBlock * pb);
+
+/**
+ * debug functions - global, for the debugger
+ */
+void ipamodrdn_dump_config();
+void ipamodrdn_dump_config_entry(struct configEntry *);
+
+/**
+ * set the debug level
+ */
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/**
+ *
+ * Deal with cache locking
+ *
+ */
+void ipamodrdn_read_lock()
+{
+ PR_RWLock_Rlock(g_ipamodrdn_cache_lock);
+}
+
+void ipamodrdn_write_lock()
+{
+ PR_RWLock_Wlock(g_ipamodrdn_cache_lock);
+}
+
+void ipamodrdn_unlock()
+{
+ PR_RWLock_Unlock(g_ipamodrdn_cache_lock);
+}
+
+/**
+ *
+ * Get the plug-in version
+ *
+ */
+int ipamodrdn_version()
+{
+ return IPAMODRDN_PLUGIN_VERSION;
+}
+
+/**
+ * Plugin identity mgmt
+ */
+void setPluginID(void *pluginID)
+{
+ _PluginID = pluginID;
+}
+
+void *getPluginID()
+{
+ return _PluginID;
+}
+
+void setPluginDN(char *pluginDN)
+{
+ _PluginDN = pluginDN;
+}
+
+char *getPluginDN()
+{
+ return _PluginDN;
+}
+
+/*
+ ipamodrdn_init
+ -------------
+ adds our callbacks to the list
+*/
+int
+ipamodrdn_init(Slapi_PBlock *pb)
+{
+ int status = EOK;
+ char *plugin_identity = NULL;
+
+ LOG_TRACE("--in-->\n");
+
+ /**
+ * Store the plugin identity for later use.
+ * Used for internal operations
+ */
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ PR_ASSERT(plugin_identity);
+ setPluginID(plugin_identity);
+
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &pdesc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) ipamodrdn_start) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) ipamodrdn_close) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+ (void *) ipamodrdn_config_check_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+ (void *) ipamodrdn_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+ (void *) ipamodrdn_config_check_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+ (void *) ipamodrdn_config_check_post_op) != 0) {
+ LOG_FATAL("failed to register plugin\n");
+ status = EFAIL;
+ }
+
+ LOG_TRACE("<--out--\n");
+ return status;
+}
+
+
+/*
+ ipamodrdn_start
+ --------------
+ Kicks off the config cache.
+ It is called after ipamodrdn_init.
+*/
+static int
+ipamodrdn_start(Slapi_PBlock * pb)
+{
+ char *plugindn = NULL;
+
+ LOG_TRACE("--in-->\n");
+
+ /* Check if we're already started */
+ if (g_plugin_started) {
+ goto done;
+ }
+
+ g_ipamodrdn_cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "ipaModRDN");
+
+ if (!g_ipamodrdn_cache_lock) {
+ LOG_FATAL("lock creation failed\n");
+
+ return EFAIL;
+ }
+
+ /**
+ * Get the plug-in target dn from the system
+ * and store it for future use. This should avoid
+ * hardcoding of DN's in the code.
+ */
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn);
+ if (NULL == plugindn || 0 == strlen(plugindn)) {
+ LOG("had to use hard coded config dn\n");
+ plugindn = IPAMODRDN_DN;
+ } else {
+ LOG("config at %s\n", plugindn);
+
+ }
+
+ setPluginDN(plugindn);
+
+ /*
+ * Load the config for our plug-in
+ */
+ ipamodrdn_global_config = (PRCList *)
+ slapi_ch_calloc(1, sizeof(struct configEntry));
+ PR_INIT_CLIST(ipamodrdn_global_config);
+
+ if (ipamodrdn_load_plugin_config() != EOK) {
+ LOG_FATAL("unable to load plug-in configuration\n");
+ return EFAIL;
+ }
+
+ g_plugin_started = 1;
+ LOG("ready for service\n");
+ LOG_TRACE("<--out--\n");
+
+done:
+ return EOK;
+}
+
+/*
+ ipamodrdn_close
+ --------------
+ closes down the cache
+*/
+static int
+ipamodrdn_close(Slapi_PBlock * pb)
+{
+ LOG_TRACE( "--in-->\n");
+
+ ipamodrdn_delete_config();
+
+ slapi_ch_free((void **)&ipamodrdn_global_config);
+
+ LOG_TRACE("<--out--\n");
+
+ return EOK;
+}
+
+/*
+ * config looks like this
+ * - cn=myplugin
+ * --- cn=posix
+ * ------ cn=accounts
+ * ------ cn=groups
+ * --- cn=samba
+ * --- cn=etc
+ * ------ cn=etc etc
+ */
+static int
+ipamodrdn_load_plugin_config()
+{
+ int status = EOK;
+ int result;
+ int i;
+ time_t now;
+ Slapi_PBlock *search_pb;
+ Slapi_Entry **entries = NULL;
+
+ LOG_TRACE("--in-->\n");
+
+ ipamodrdn_write_lock();
+ ipamodrdn_delete_config();
+
+ search_pb = slapi_pblock_new();
+
+ slapi_search_internal_set_pb(search_pb, getPluginDN(),
+ LDAP_SCOPE_SUBTREE, "objectclass=*",
+ NULL, 0, NULL, NULL, getPluginID(), 0);
+ slapi_search_internal_pb(search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+
+ if (LDAP_SUCCESS != result) {
+ status = EFAIL;
+ goto cleanup;
+ }
+
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+ if (NULL == entries || NULL == entries[0]) {
+ status = EOK;
+ goto cleanup;
+ }
+
+ for (i = 0; (entries[i] != NULL); i++) {
+ /* We don't care about the status here because we may have
+ * some invalid config entries, but we just want to continue
+ * looking for valid ones. */
+ ipamodrdn_parse_config_entry(entries[i], true);
+ }
+
+ cleanup:
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ ipamodrdn_unlock();
+ LOG_TRACE("<--out--\n");
+
+ return status;
+}
+
+/*
+ * ipamodrdn_parse_config_entry()
+ *
+ * Parses a single config entry. If apply is non-zero, then
+ * we will load and start using the new config. You can simply
+ * validate config without making any changes by setting apply
+ * to 0.
+ *
+ * Returns EOK if the entry is valid and EFAIL
+ * if it is invalid.
+ */
+static int
+ipamodrdn_parse_config_entry(Slapi_Entry * e, bool apply)
+{
+ char *value;
+ struct configEntry *entry = NULL;
+ struct configEntry *config_entry;
+ PRCList *list;
+ int entry_added = 0;
+ int i = 0;
+ int ret = EOK;
+
+ LOG_TRACE("--in-->\n");
+
+ /* If this is the main MODRDN plug-in config entry, just bail. */
+ if (strcasecmp(getPluginDN(), slapi_entry_get_ndn(e)) == 0) {
+ ret = EFAIL;
+ goto bail;
+ }
+
+ entry = (struct configEntry *)
+ slapi_ch_calloc(1, sizeof(struct configEntry));
+ if (NULL == entry) {
+ ret = EFAIL;
+ goto bail;
+ }
+
+ value = slapi_entry_get_ndn(e);
+ if (value) {
+ entry->dn = slapi_ch_strdup(value);
+ }
+
+ LOG_CONFIG("----------> dn [%s]\n", entry->dn);
+
+ entry->sattr = slapi_entry_attr_get_charptr(e, IPAMODRDN_SATTR);
+ if (!entry->sattr) {
+ LOG_FATAL("The %s config setting is required for %s.\n",
+ IPAMODRDN_SATTR, entry->dn);
+ ret = EFAIL;
+ goto bail;
+ }
+ LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_SATTR, entry->sattr);
+
+ entry->tattr = slapi_entry_attr_get_charptr(e, IPAMODRDN_TATTR);
+ if (!entry->tattr) {
+ LOG_FATAL("The %s config setting is required for %s.\n",
+ IPAMODRDN_TATTR, entry->dn);
+ ret = EFAIL;
+ goto bail;
+ }
+ LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_TATTR, entry->tattr);
+
+ value = slapi_entry_attr_get_charptr(e, IPAMODRDN_PREFIX);
+ if (value && value[0]) {
+ entry->prefix = value;
+ } else {
+ entry->prefix = "";
+ }
+ LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_PREFIX, entry->prefix);
+
+ value = slapi_entry_attr_get_charptr(e, IPAMODRDN_SUFFIX);
+ if (value && value[0]) {
+ entry->suffix = value;
+ } else {
+ entry->suffix = "";
+ }
+ LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_SUFFIX, entry->suffix);
+
+ value = slapi_entry_attr_get_charptr(e, IPAMODRDN_FILTER);
+ if (value) {
+ entry->filter = value;
+ if (NULL == (entry->slapi_filter = slapi_str2filter(value))) {
+ LOG_FATAL("Error: Invalid search filter in entry [%s]: [%s]\n",
+ entry->dn, value);
+ ret = EFAIL;
+ goto bail;
+ }
+ } else {
+ LOG_FATAL("The %s config setting is required for %s.\n",
+ IPAMODRDN_FILTER, entry->dn);
+ ret = EFAIL;
+ goto bail;
+ }
+ LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_FILTER, value);
+
+ value = slapi_entry_attr_get_charptr(e, IPAMODRDN_SCOPE);
+ if (value) {
+ entry->scope = value;
+ } else {
+ LOG_FATAL("The %s config config setting is required for %s.\n",
+ IPAMODRDN_SCOPE, entry->dn);
+ ret = EFAIL;
+ goto bail;
+ }
+ LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_SCOPE, entry->scope);
+
+ /* If we were only called to validate config, we can
+ * just bail out before applying the config changes */
+ if (!apply) {
+ goto bail;
+ }
+
+ /**
+ * Finally add the entry to the list.
+ * We sort by scope dn length with longer
+ * dn's first - this allows the scope
+ * checking code to be simple and quick and
+ * cunningly linear.
+ */
+ if (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
+ list = PR_LIST_HEAD(ipamodrdn_global_config);
+ while (list != ipamodrdn_global_config) {
+ config_entry = (struct configEntry *) list;
+
+ if (slapi_dn_issuffix(entry->scope, config_entry->scope)) {
+ PR_INSERT_BEFORE(&(entry->list), list);
+ LOG_CONFIG("store [%s] before [%s] \n",
+ entry->scope, config_entry->scope);
+ entry_added = 1;
+ break;
+ }
+
+ next:
+ list = PR_NEXT_LINK(list);
+
+ if (ipamodrdn_global_config == list) {
+ /* add to tail */
+ PR_INSERT_BEFORE(&(entry->list), list);
+ LOG_CONFIG("store [%s] at tail\n", entry->scope);
+ entry_added = 1;
+ break;
+ }
+ }
+ } else {
+ /* first entry */
+ PR_INSERT_LINK(&(entry->list), ipamodrdn_global_config);
+ LOG_CONFIG("store [%s] at head \n", entry->scope);
+ entry_added = 1;
+ }
+
+bail:
+ if (0 == entry_added) {
+ /* Don't log error if we weren't asked to apply config */
+ if (apply && (entry != NULL)) {
+ LOG_FATAL("Invalid config entry [%s] skipped\n", entry->dn);
+ }
+ ipamodrdn_free_config_entry(&entry);
+ } else {
+ ret = EOK;
+ }
+
+ LOG_TRACE("<--out--\n");
+
+ return ret;
+}
+
+static void
+ipamodrdn_free_config_entry(struct configEntry **entry)
+{
+ struct configEntry *e;
+
+ if (!entry || !*entry) {
+ return;
+ }
+
+ e = *entry;
+
+ if (e->dn) {
+ LOG_CONFIG("freeing config entry [%s]\n", e->dn);
+ slapi_ch_free_string(&e->dn);
+ }
+ if (e->sattr) {
+ slapi_ch_free_string(&e->sattr);
+ }
+ if (e->tattr) {
+ slapi_ch_free_string(&e->tattr);
+ }
+ if (e->prefix) {
+ slapi_ch_free_string(&e->prefix);
+ }
+ if (e->suffix) {
+ slapi_ch_free_string(&e->prefix);
+ }
+ if (e->filter) {
+ slapi_ch_free_string(&e->filter);
+ }
+ if (e->slapi_filter) {
+ slapi_filter_free(e->slapi_filter, 1);
+ }
+ if (e->scope) {
+ slapi_ch_free_string(&e->scope);
+ }
+
+ slapi_ch_free((void **)entry);
+}
+
+static void
+ipamodrdn_delete_configEntry(PRCList *entry)
+{
+ PR_REMOVE_LINK(entry);
+ ipamodrdn_free_config_entry((struct configEntry **) &entry);
+}
+
+static void
+ipamodrdn_delete_config()
+{
+ PRCList *list;
+
+ while (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
+ list = PR_LIST_HEAD(ipamodrdn_global_config);
+ ipamodrdn_delete_configEntry(list);
+ }
+
+ return;
+}
+
+/****************************************************
+ Helpers
+****************************************************/
+
+static char *ipamodrdn_get_dn(Slapi_PBlock * pb)
+{
+ char *dn = NULL;
+
+ LOG_TRACE("--in-->\n");
+
+ if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn)) {
+ LOG_FATAL("failed to get dn of changed entry");
+ }
+
+ LOG_TRACE("<--out--\n");
+
+ return dn;
+}
+
+/* config check
+ matching config dn or a descendent reloads config
+*/
+static int ipamodrdn_dn_is_config(char *dn)
+{
+ int ret = 0;
+
+ LOG_TRACE("--in-->\n");
+
+ if (slapi_dn_issuffix(dn, getPluginDN())) {
+ ret = 1;
+ }
+
+ LOG_TRACE("<--out--\n");
+
+ return ret;
+}
+
+/****************************************************
+ Functions that actually do things other
+ than config and startup
+****************************************************/
+
+/*
+ * ipamodrdn_list_contains_attr()
+ *
+ * Checks if a attr is contained in a list of attrs.
+ * Returns 1 if the attr is found, 0 otherwise.
+ */
+static int
+ipamodrdn_list_contains_attr(char **list, char *attr)
+{
+ int ret = 0;
+ int i = 0;
+
+ if (list && attr) {
+ for (i = 0; list[i]; i++) {
+ if (slapi_attr_types_equivalent(attr, list[i])) {
+ ret = 1;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * ipamodrdn_list_contains_attrs()
+ *
+ * Checks if all attrs in one list (attrs) are contained
+ * in another list of attrs (list). Returns 1 if all
+ * attrs are found, 0 otherwise.
+ */
+static int
+ipamodrdn_list_contains_attrs(char **list, char **attrs)
+{
+ int ret = 1;
+ int i = 0;
+ int j = 0;
+
+ if (list && attrs) {
+ for (i = 0; attrs[i]; i++) {
+ int found = 0;
+
+ for (j = 0; list[j]; j++) {
+ if (slapi_attr_types_equivalent(attrs[i], list[i])) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret = 0;
+ break;
+ }
+ }
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * ipamodrdn_list_remove_attr()
+ *
+ * Removes a attr from a list of attrs.
+ */
+static void
+ipamodrdn_list_remove_attr(char **list, char *attr)
+{
+ int i = 0;
+ int found_attr = 0;
+
+ if (list && attr) {
+ /* Go through the list until we find the attr that
+ * we want to remove. We simply free the attr we
+ * want to remove and shift the remaining array
+ * elements down by one index. This will leave us
+ * with two NULL elements at the end of the list,
+ * but this should not cause any problems. */
+ for (i = 0; list[i]; i++) {
+ if (found_attr) {
+ list[i] = list[i + 1];
+ } else if (slapi_attr_types_equivalent(attr, list[i])) {
+ slapi_ch_free_string(&list[i]);
+ list[i] = list[i + 1];
+ found_attr = 1;
+ }
+ }
+ }
+}
+
+static int
+ipamodrdn_change_attr(struct configEntry *cfgentry,
+ char *targetdn, const char *value)
+{
+ Slapi_PBlock *mod_pb = slapi_pblock_new();
+ LDAPMod mod;
+ LDAPMod *mods[2];
+ char *val[2] = { NULL };
+ int ret;
+
+ val[0] = slapi_ch_smprintf("%s%s%s",
+ cfgentry->prefix, value, cfgentry->suffix);
+ if (!val[0]) {
+ LOG_OOM();
+ ret = EFAIL;
+ goto done;
+ }
+ val[1] = 0;
+
+ mod.mod_op = LDAP_MOD_REPLACE;
+ mod.mod_type = cfgentry->tattr;
+ mod.mod_values = val;
+
+ mods[0] = &mod;
+ mods[1] = 0;
+
+ LOG("Setting %s to %s in entry (%s)\n", cfgentry->tattr, targetdn);
+
+ /* Perform the modify operation. */
+ slapi_modify_internal_set_pb(mod_pb, targetdn, mods,
+ 0, 0, getPluginID(), 0);
+ slapi_modify_internal_pb(mod_pb);
+ slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ LOG_FATAL("Failed to change attribute with error %d\n", ret);
+ ret = EFAIL;
+ }
+ ret = EOK;
+
+done:
+ if (val[0]) slapi_ch_free_string(&(val[0]));
+ slapi_pblock_destroy(mod_pb);
+ return ret;
+}
+
+
+
+
+/* for mods and adds:
+ where dn's are supplied, the closest in scope
+ is used as long as the type filter matches
+ and the attr value has not been generated yet.
+*/
+
+static int ipamodrdn_post_op(Slapi_PBlock *pb)
+{
+ char *dn = NULL;
+ PRCList *list = NULL;
+ struct configEntry *cfgentry = NULL;
+ struct slapi_entry *e = NULL;
+ Slapi_Attr *sattr = NULL;
+ Slapi_Attr *tattr = NULL;
+ char *value = NULL;
+ char *errstr = NULL;
+ int ret = LDAP_SUCCESS;
+
+ LOG_TRACE("--in-->\n");
+
+ /* Just bail if we aren't ready to service requests yet. */
+ if (!g_plugin_started) {
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+ if (NULL == e) {
+ goto done;
+ }
+
+ dn = slapi_entry_get_ndn(e);
+ if (NULL == dn) {
+ goto done;
+ }
+
+ ipamodrdn_read_lock();
+
+ if (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
+ list = PR_LIST_HEAD(ipamodrdn_global_config);
+
+ for(list = PR_LIST_HEAD(ipamodrdn_global_config);
+ list != ipamodrdn_global_config;
+ list = PR_NEXT_LINK(list)) {
+ cfgentry = (struct configEntry *) list;
+
+ /* is the entry in scope? */
+ if (cfgentry->scope) {
+ if (!slapi_dn_issuffix(dn, cfgentry->scope)) {
+ continue;
+ }
+ }
+
+ /* does the entry match the filter? */
+ if (cfgentry->slapi_filter) {
+ ret = slapi_vattr_filter_test(pb, e,
+ cfgentry->slapi_filter, 0);
+ if (ret != LDAP_SUCCESS) {
+ continue;
+ }
+ }
+
+ if (slapi_entry_attr_find(e, cfgentry->sattr, &sattr) != 0) {
+ LOG_TRACE("Source attr %s not found for %d\n",
+ cfgentry->sattr, dn);
+ continue;
+ }
+ if (slapi_entry_attr_find(e, cfgentry->tattr, &tattr) != 0) {
+ LOG_TRACE("Target attr %s not found for %d\n",
+ cfgentry->tattr, dn);
+ } else {
+ Slapi_Value *val;
+ const char *strval;
+
+ slapi_attr_first_value(sattr, &val);
+ if (!val) {
+ LOG_FATAL("Source attr %s is empty\n", cfgentry->sattr);
+ continue;
+ }
+ strval = slapi_value_get_string(val);
+
+ ret = ipamodrdn_change_attr(cfgentry, dn, strval);
+ if (ret != EOK) {
+ LOG_FATAL("Failed to set target attr %s for %d\n",
+ cfgentry->tattr, dn);
+ }
+ }
+ }
+ }
+
+ ipamodrdn_unlock();
+
+ ret = LDAP_SUCCESS;
+
+done:
+ if (ret) {
+ LOG("operation failure [%d]\n", ret);
+ ret = EFAIL;
+ }
+
+ LOG_TRACE("<--out--\n");
+
+ return ret;
+}
+
+static int ipamodrdn_config_check_post_op(Slapi_PBlock * pb)
+{
+ char *dn;
+
+ LOG_TRACE("--in-->\n");
+
+ if ((dn = ipamodrdn_get_dn(pb))) {
+ if (ipamodrdn_dn_is_config(dn)) {
+ ipamodrdn_load_plugin_config();
+ }
+ }
+
+ LOG_TRACE("<--out--\n");
+
+ return 0;
+}
+
+
+/****************************************************
+ End of
+ Functions that actually do things other
+ than config and startup
+****************************************************/
+
+/**
+ * debug functions to print config
+ */
+void ipamodrdn_dump_config()
+{
+ PRCList *list;
+
+ ipamodrdn_read_lock();
+
+ if (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
+ list = PR_LIST_HEAD(ipamodrdn_global_config);
+ while (list != ipamodrdn_global_config) {
+ ipamodrdn_dump_config_entry((struct configEntry *) list);
+ list = PR_NEXT_LINK(list);
+ }
+ }
+
+ ipamodrdn_unlock();
+}
+
+
+void ipamodrdn_dump_config_entry(struct configEntry * entry)
+{
+ int i = 0;
+
+ printf("<---- source attr-----> %s\n", entry->sattr);
+ printf("<---- target attr-----> %s\n", entry->tattr);
+ printf("<---- prefix ---------> %s\n", entry->prefix);
+ printf("<---- suffix ---------> %s\n", entry->suffix);
+ printf("<---- filter ---------> %s\n", entry->filter);
+ printf("<---- scope ----------> %s\n", entry->scope);
+}
diff --git a/daemons/ipa-slapi-plugins/ipa-modrdn/modrdn-conf.ldif b/daemons/ipa-slapi-plugins/ipa-modrdn/modrdn-conf.ldif
new file mode 100644
index 000000000..54916eb74
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-modrdn/modrdn-conf.ldif
@@ -0,0 +1,15 @@
+dn: cn=IPA MODRDN,cn=plugins,cn=config
+changetype: add
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: IPA MODRDN
+nsslapd-pluginpath: libipa_modrdn
+nsslapd-plugininitfunc: ipamodrdn_init
+nsslapd-plugintype: postoperation
+nsslapd-pluginenabled: on
+nsslapd-pluginid: ipamodrdn_version
+nsslapd-pluginversion: 1.0
+nsslapd-pluginvendor: Red Hat, Inc.
+nsslapd-plugindescription: IPA MODRDN plugin
+nsslapd-plugin-depends-on-type: database