summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/uiduniq/uid.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/plugins/uiduniq/uid.c')
-rw-r--r--ldap/servers/plugins/uiduniq/uid.c1073
1 files changed, 1073 insertions, 0 deletions
diff --git a/ldap/servers/plugins/uiduniq/uid.c b/ldap/servers/plugins/uiduniq/uid.c
new file mode 100644
index 00000000..f32e63ac
--- /dev/null
+++ b/ldap/servers/plugins/uiduniq/uid.c
@@ -0,0 +1,1073 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * uid.c
+ *
+ * Implements a directory server pre-operation plugin to test
+ * attributes for uniqueness within a defined subtree in the
+ * directory.
+ *
+ * Called uid.c since the original purpose of the plugin was to
+ * check the uid attribute in user entries.
+ */
+#include <slapi-plugin.h>
+#include <portable.h>
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include <string.h>
+#include "dirver.h"
+#include "plugin-utils.h"
+
+#if defined( LDAP_DEBUG ) && !defined( DEBUG )
+#define DEBUG
+#endif
+
+#define UNTAGGED_PARAMETER 12
+
+/* Quoting routine - this should be in a library somewhere (slapi?) */
+int ldap_quote_filter_value(
+ char *value, int len,
+ char *out, int maxLen,
+ int *outLen);
+
+
+static int search_one_berval(const char *baseDN, const char *attrName,
+ const struct berval *value, const char *target);
+
+/*
+ * ISSUES:
+ * How should this plugin handle ACL issues? It seems wrong to reject
+ * adds and modifies because there is already a conflicting UID, when
+ * the request would have failed because of an ACL check anyway.
+ *
+ * This code currently defines a maximum filter string size of 512. Is
+ * this large enough?
+ *
+ * This code currently does not quote the value portion of the filter as
+ * it is created. This is a bug.
+ */
+
+/* */
+#define BEGIN do {
+#define END } while(0);
+
+/*
+ * Slapi plugin descriptor
+ */
+static char *plugin_name = "NSUniqueAttr";
+static Slapi_PluginDesc
+pluginDesc = {
+ "NSUniqueAttr", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "Enforce unique attribute values"
+};
+static void* plugin_identity = NULL;
+
+
+/*
+ * More information about constraint failure
+ */
+static char *moreInfo =
+ "Another entry with the same attribute value already exists";
+
+static void
+freePblock( Slapi_PBlock *spb ) {
+ if ( spb )
+ {
+ slapi_free_search_results_internal( spb );
+ slapi_pblock_destroy( spb );
+ }
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * op_error - Record (and report) an operational error.
+ * name changed to uid_op_error so as not to conflict with the external function
+ * of the same name thereby preventing compiler warnings.
+ */
+static int
+uid_op_error(int internal_error)
+{
+ slapi_log_error(
+ SLAPI_LOG_PLUGIN,
+ plugin_name,
+ "Internal error: %d\n",
+ internal_error);
+
+ return LDAP_OPERATIONS_ERROR;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * Create an LDAP search filter from the attribute
+ * name and value supplied.
+ */
+
+static char *
+create_filter(const char *attribute, const struct berval *value)
+{
+ char *filter;
+ char *fp;
+ char *max;
+ int attrLen;
+ int valueLen;
+ int filterLen;
+
+ /* Compute the length of the required buffer */
+ attrLen = strlen(attribute);
+
+ if (ldap_quote_filter_value(value->bv_val,
+ value->bv_len, 0, 0, &valueLen))
+ return 0;
+
+ filterLen = attrLen + 1 + valueLen + 1;
+
+ /* Allocate the buffer */
+ filter = slapi_ch_malloc(filterLen);
+ fp = filter;
+ max = &filter[filterLen];
+
+ /* Place attribute name in filter */
+ strcpy(fp, attribute);
+ fp += attrLen;
+
+ /* Place comparison operator */
+ *fp++ = '=';
+
+ /* Place value in filter */
+ if (ldap_quote_filter_value(value->bv_val, value->bv_len,
+ fp, max-fp, &valueLen)) { slapi_ch_free((void**)&filter); return 0; }
+ fp += valueLen;
+
+ /* Terminate */
+ *fp = 0;
+
+ return filter;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * search - search a subtree for entries with a named attribute matching
+ * the list of values. An entry matching the 'target' DN is
+ * not considered in the search.
+ *
+ * If 'attr' is NULL, the values are taken from 'values'.
+ * If 'attr' is non-NULL, the values are taken from 'attr'.
+ *
+ * Return:
+ * LDAP_SUCCESS - no matches, or the attribute matches the
+ * target dn.
+ * LDAP_CONSTRAINT_VIOLATION - an entry was found that already
+ * contains the attribute value.
+ * LDAP_OPERATIONS_ERROR - a server failure.
+ */
+static int
+search(const char *baseDN, const char *attrName, Slapi_Attr *attr,
+ struct berval **values, const char *target)
+{
+ int result;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH baseDN=%s attr=%s target=%s\n", baseDN, attrName,
+ target?target:"None");
+#endif
+
+ result = LDAP_SUCCESS;
+
+ /* If no values, can't possibly be a conflict */
+ if ( (Slapi_Attr *)NULL == attr && (struct berval **)NULL == values )
+ return result;
+
+ /*
+ * Perform the search for each value provided
+ *
+ * Another possibility would be to search for all the values at once.
+ * However, this is more complex (for filter creation) and unique
+ * attributes values are probably only changed one at a time anyway.
+ */
+ if ( (Slapi_Attr *)NULL != attr )
+ {
+ Slapi_Value *v = NULL;
+ int vhint = -1;
+
+ for ( vhint = slapi_attr_first_value( attr, &v );
+ vhint != -1 && LDAP_SUCCESS == result;
+ vhint = slapi_attr_next_value( attr, vhint, &v ))
+ {
+ result = search_one_berval(baseDN,attrName,
+ slapi_value_get_berval(v),target);
+ }
+ }
+ else
+ {
+ for (;*values != NULL && LDAP_SUCCESS == result; values++)
+ {
+ result = search_one_berval(baseDN,attrName,*values,target);
+ }
+ }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH result = %d\n", result);
+#endif
+
+ return( result );
+}
+
+
+static int
+search_one_berval(const char *baseDN, const char *attrName,
+ const struct berval *value, const char *target)
+{
+ int result;
+ char *filter;
+ Slapi_PBlock *spb;
+
+ result = LDAP_SUCCESS;
+
+ /* If no value, can't possibly be a conflict */
+ if ( (struct berval *)NULL == value )
+ return result;
+
+ filter = 0;
+ spb = 0;
+
+ BEGIN
+ int err;
+ int sres;
+ Slapi_Entry **entries;
+ static char *attrs[] = { "1.1", 0 };
+
+ /* Create the filter - this needs to be freed */
+ filter = create_filter(attrName, value);
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH filter=%s\n", filter);
+#endif
+
+ /* Perform the search using the new internal API */
+ spb = slapi_pblock_new();
+ if (!spb) { result = uid_op_error(2); break; }
+
+ slapi_search_internal_set_pb(spb, baseDN, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0 /* attrs only */, NULL, NULL, plugin_identity, 0 /* actions */);
+ slapi_search_internal_pb(spb);
+
+ err = slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_RESULT, &sres);
+ if (err) { result = uid_op_error(3); break; }
+
+ /* Allow search to report that there is nothing in the subtree */
+ if (sres == LDAP_NO_SUCH_OBJECT) break;
+
+ /* Other errors are bad */
+ if (sres) { result = uid_op_error(3); break; }
+
+ err = slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+ if (err) { result = uid_op_error(4); break; }
+
+ /*
+ * Look at entries returned. Any entry found must be the
+ * target entry or the constraint fails.
+ */
+ for(;*entries;entries++)
+ {
+ char *dn = slapi_entry_get_dn(*entries);
+
+ /*
+ * DNs are returned in the original value used to insert
+ * the entry. This must be "normalized" for comparison.
+ *
+ * This normalization is done "in-place" (modifying the value
+ * in the entry). This is OK, since this is the only user
+ * of this copy of the entry.
+ */
+ slapi_dn_normalize_case(dn);
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH entry dn=%s\n", dn);
+#endif
+
+ /*
+ * It is a Constraint Violation if any entry is found, unless
+ * the entry is the target entry (if any).
+ */
+ if (!target || strcmp(dn, target) != 0)
+ {
+ result = LDAP_CONSTRAINT_VIOLATION;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH complete result=%d\n", result);
+#endif
+ END
+
+ /* Clean-up */
+ if (spb) {
+ slapi_free_search_results_internal(spb);
+ slapi_pblock_destroy(spb);
+ }
+
+ slapi_ch_free((void**)&filter);
+
+ return result;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * searchAllSubtrees - search all subtrees in argv for entries
+ * with a named attribute matching the list of values, by
+ * calling search for each one.
+ *
+ * If 'attr' is NULL, the values are taken from 'values'.
+ * If 'attr' is non-NULL, the values are taken from 'attr'.
+ *
+ * Return:
+ * LDAP_SUCCESS - no matches, or the attribute matches the
+ * target dn.
+ * LDAP_CONSTRAINT_VIOLATION - an entry was found that already
+ * contains the attribute value.
+ * LDAP_OPERATIONS_ERROR - a server failure.
+ */
+static int
+searchAllSubtrees(int argc, char *argv[], const char *attrName,
+ Slapi_Attr *attr, struct berval **values, const char *dn)
+{
+ int result = LDAP_SUCCESS;
+
+ /*
+ * For each DN in the managed list, do uniqueness checking if
+ * the target DN is a subnode in the tree.
+ */
+ for(;argc > 0;argc--,argv++)
+ {
+ result = search(*argv, attrName, attr, values, dn);
+ if (result) break;
+ }
+ return result;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * getArguments - parse invocation parameters
+ * Return:
+ * 0 - success
+ * >0 - error parsing parameters
+ */
+static int
+getArguments(Slapi_PBlock *pb, char **attrName, char **markerObjectClass,
+ char **requiredObjectClass)
+{
+ int argc;
+ char **argv;
+
+ /*
+ * Get the arguments
+ */
+ if (slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc))
+ {
+ return uid_op_error(10);
+ }
+
+ if (slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv))
+ {
+ return uid_op_error(11);
+ }
+
+ /*
+ * Required arguments: attribute and markerObjectClass
+ * Optional argument: requiredObjectClass
+ */
+ for(;argc > 0;argc--,argv++)
+ {
+ char *param = *argv;
+ char *delimiter = strchr(param, '=');
+ if (NULL == delimiter)
+ {
+ /* Old style untagged parameter */
+ *attrName = *argv;
+ return UNTAGGED_PARAMETER;
+ }
+ if (strncasecmp(param, "attribute", delimiter-param) == 0)
+ {
+ /* It's OK to set a pointer here, because ultimately it points
+ * inside the argv array of the pblock, which will be staying
+ * arround.
+ */
+ *attrName = delimiter+1;
+ } else if (strncasecmp(param, "markerobjectclass", delimiter-param) == 0)
+ {
+ *markerObjectClass = delimiter+1;
+ } else if (strncasecmp(param, "requiredobjectclass", delimiter-param) == 0)
+ {
+ *requiredObjectClass = delimiter+1;
+ }
+ }
+ if (!*attrName || !*markerObjectClass)
+ {
+ return uid_op_error(13);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * findSubtreeAndSearch - walk up the tree to find an entry with
+ * the marker object class; if found, call search from there and
+ * return the result it returns
+ *
+ * If 'attr' is NULL, the values are taken from 'values'.
+ * If 'attr' is non-NULL, the values are taken from 'attr'.
+ *
+ * Return:
+ * LDAP_SUCCESS - no matches, or the attribute matches the
+ * target dn.
+ * LDAP_CONSTRAINT_VIOLATION - an entry was found that already
+ * contains the attribute value.
+ * LDAP_OPERATIONS_ERROR - a server failure.
+ */
+static int
+findSubtreeAndSearch(char *parentDN, const char *attrName, Slapi_Attr *attr,
+ struct berval **values, const char *target, const char *markerObjectClass)
+{
+ int result = LDAP_SUCCESS;
+ Slapi_PBlock *spb = NULL;
+
+ while (NULL != (parentDN = slapi_dn_parent(parentDN)))
+ {
+ if (spb = dnHasObjectClass(parentDN, markerObjectClass))
+ {
+ freePblock(spb);
+ /*
+ * Do the search. There is no entry that is allowed
+ * to have the attribute already.
+ */
+ result = search(parentDN, attrName, attr, values, target);
+ break;
+ }
+ }
+ return result;
+}
+
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_add - pre-operation plug-in for add
+ */
+static int
+preop_add(Slapi_PBlock *pb)
+{
+ int result;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "ADD begin\n");
+#endif
+
+ result = LDAP_SUCCESS;
+
+ /*
+ * Do constraint check on the added entry. Set result.
+ */
+
+ BEGIN
+ int err;
+ char *attrName = NULL;
+ char *markerObjectClass = NULL;
+ char *requiredObjectClass = NULL;
+ char *dn;
+ int isupdatedn;
+ Slapi_Entry *e;
+ Slapi_Attr *attr;
+ int argc;
+ char **argv = NULL;
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &isupdatedn);
+ if (err) { result = uid_op_error(50); break; }
+ if (isupdatedn)
+ {
+ break;
+ }
+
+ /*
+ * Get the arguments
+ */
+ result = getArguments(pb, &attrName, &markerObjectClass,
+ &requiredObjectClass);
+ if (UNTAGGED_PARAMETER == result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "ADD parameter untagged: %s\n", attrName);
+ result = LDAP_SUCCESS;
+ /* Statically defined subtrees to monitor */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = uid_op_error(53); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = uid_op_error(54); break; }
+ argc--; argv++; /* First argument was attribute name */
+ } else if (0 != result)
+ {
+ break;
+ }
+
+ /*
+ * Get the target DN for this add operation
+ */
+ err = slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn);
+ if (err) { result = uid_op_error(51); break; }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "ADD target=%s\n", dn);
+#endif
+
+ /*
+ * Get the entry data for this add. Check whether it
+ * contains a value for the unique attribute
+ */
+ err = slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+ if (err) { result = uid_op_error(52); break; }
+
+ err = slapi_entry_attr_find(e, attrName, &attr);
+ if (err) break; /* no unique attribute */
+
+ /*
+ * Check if it contains the required object class
+ */
+ if (NULL != requiredObjectClass)
+ {
+ if (!entryHasObjectClass(pb, e, requiredObjectClass))
+ {
+ /* No, so we don't have to do anything */
+ break;
+ }
+ }
+
+ /*
+ * Passed all the requirements - this is an operation we
+ * need to enforce uniqueness on. Now find all parent entries
+ * with the marker object class, and do a search for each one.
+ */
+ if (NULL != markerObjectClass)
+ {
+ /* Subtree defined by location of marker object class */
+ result = findSubtreeAndSearch(dn, attrName, attr, NULL,
+ dn, markerObjectClass);
+ } else
+ {
+ /* Subtrees listed on invocation line */
+ result = searchAllSubtrees(argc, argv, attrName, attr, NULL, dn);
+ }
+ END
+
+ if (result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "ADD result %d\n", result);
+
+ /* Send failure to the client */
+ slapi_send_ldap_result(pb, result, 0, moreInfo, 0, 0);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+}
+
+static void
+addMod(LDAPMod ***modary, int *capacity, int *nmods, LDAPMod *toadd)
+{
+ if (*nmods == *capacity) {
+ *capacity += 4;
+ if (*modary) {
+ *modary = (LDAPMod **)slapi_ch_realloc((char *)*modary, *capacity * sizeof(LDAPMod *));
+ } else {
+ *modary = (LDAPMod **)slapi_ch_malloc(*capacity * sizeof(LDAPMod *));
+ }
+ }
+ *modary[*nmods] = toadd;
+ (*nmods)++;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_modify - pre-operation plug-in for modify
+ */
+static int
+preop_modify(Slapi_PBlock *pb)
+{
+
+ int result = LDAP_SUCCESS;
+ Slapi_PBlock *spb = NULL;
+ LDAPMod **checkmods = NULL;
+ int checkmodsCapacity = 0;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODIFY begin\n");
+#endif
+
+ BEGIN
+ int err;
+ char *attrName;
+ char *markerObjectClass=NULL;
+ char *requiredObjectClass=NULL;
+ LDAPMod **mods;
+ int modcount = 0;
+ int ii;
+ LDAPMod *mod;
+ char *dn;
+ int isupdatedn;
+ int argc;
+ char **argv = NULL;
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &isupdatedn);
+ if (err) { result = uid_op_error(60); break; }
+ if (isupdatedn)
+ {
+ break;
+ }
+
+ /*
+ * Get the arguments
+ */
+ result = getArguments(pb, &attrName, &markerObjectClass,
+ &requiredObjectClass);
+ if (UNTAGGED_PARAMETER == result)
+ {
+ result = LDAP_SUCCESS;
+ /* Statically defined subtrees to monitor */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = uid_op_error(53); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = uid_op_error(54); break; }
+ argc--; /* First argument was attribute name */
+ argv++;
+ } else if (0 != result)
+ {
+ break;
+ }
+
+ err = slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ if (err) { result = uid_op_error(61); break; }
+
+ /* There may be more than one mod that matches e.g.
+ changetype: modify
+ delete: uid
+ uid: balster1950
+ -
+ add: uid
+ uid: scottg
+
+ So, we need to first find all mods that contain the attribute
+ which are add or replace ops and are bvalue encoded
+ */
+ /* find out how many mods meet this criteria */
+ for(;*mods;mods++)
+ {
+ mod = *mods;
+ if ((slapi_attr_type_cmp(mod->mod_type, attrName, 1) == 0) && /* mod contains target attr */
+ (mod->mod_op & LDAP_MOD_BVALUES) && /* mod is bval encoded (not string val) */
+ (mod->mod_bvalues && mod->mod_bvalues[0]) && /* mod actually contains some values */
+ (((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) || /* mod is add */
+ (mod->mod_op & LDAP_MOD_REPLACE))) /* mod is replace */
+ {
+ addMod(&checkmods, &checkmodsCapacity, &modcount, mod);
+ }
+ }
+ if (modcount == 0) {
+ break; /* no mods to check, we are done */
+ }
+
+ /* Get the target DN */
+ err = slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &dn);
+ if (err) { result = uid_op_error(11); break; }
+
+ if (requiredObjectClass &&
+ !(spb = dnHasObjectClass(dn, requiredObjectClass))) { break; }
+
+ /*
+ * Passed all the requirements - this is an operation we
+ * need to enforce uniqueness on. Now find all parent entries
+ * with the marker object class, and do a search for each one.
+ */
+ /*
+ * stop checking at first mod that fails the check
+ */
+ for (ii = 0; (result == 0) && (ii < modcount); ++ii)
+ {
+ mod = checkmods[ii];
+ if (NULL != markerObjectClass)
+ {
+ /* Subtree defined by location of marker object class */
+ result = findSubtreeAndSearch(dn, attrName, NULL,
+ mod->mod_bvalues, dn,
+ markerObjectClass);
+ } else
+ {
+ /* Subtrees listed on invocation line */
+ result = searchAllSubtrees(argc, argv, attrName, NULL,
+ mod->mod_bvalues, dn);
+ }
+ }
+ END
+
+ slapi_ch_free((void **)&checkmods);
+ freePblock(spb);
+ if (result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODIFY result %d\n", result);
+
+ slapi_send_ldap_result(pb, result, 0, moreInfo, 0, 0);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_modrdn - Pre-operation call for modify RDN
+ *
+ * Check that the new RDN does not include attributes that
+ * cause a constraint violation
+ */
+static int
+preop_modrdn(Slapi_PBlock *pb)
+{
+ int result;
+ Slapi_Entry *e;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN begin\n");
+#endif
+
+ /* Init */
+ result = LDAP_SUCCESS;
+ e = 0;
+
+ BEGIN
+ int err;
+ char *attrName;
+ char *markerObjectClass=NULL;
+ char *requiredObjectClass=NULL;
+ char *dn;
+ char *superior;
+ char *rdn;
+ int isupdatedn;
+ Slapi_Attr *attr;
+ int argc;
+ char **argv = NULL;
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &isupdatedn);
+ if (err) { result = uid_op_error(30); break; }
+ if (isupdatedn)
+ {
+ break;
+ }
+
+ /*
+ * Get the arguments
+ */
+ result = getArguments(pb, &attrName, &markerObjectClass,
+ &requiredObjectClass);
+ if (UNTAGGED_PARAMETER == result)
+ {
+ result = LDAP_SUCCESS;
+ /* Statically defined subtrees to monitor */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = uid_op_error(53); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = uid_op_error(54); break; }
+ argc--; /* First argument was attribute name */
+ argv++;
+ } else if (0 != result)
+ {
+ break;
+ }
+
+ /* Get the DN of the entry being renamed */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &dn);
+ if (err) { result = uid_op_error(31); break; }
+
+ /* Get superior value - unimplemented in 3.0/4.0/5.0 DS */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &superior);
+ if (err) { result = uid_op_error(32); break; }
+
+ /*
+ * No superior means the entry is just renamed at
+ * its current level in the tree. Use the target DN for
+ * determining which managed tree this belongs to
+ */
+ if (!superior) superior = dn;
+
+ /* Get the new RDN - this has the attribute values */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &rdn);
+ if (err) { result = uid_op_error(33); break; }
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN newrdn=%s\n", rdn);
+#endif
+
+ /*
+ * Parse the RDN into attributes by creating a "dummy" entry
+ * and setting the attributes from the RDN.
+ *
+ * The new entry must be freed.
+ */
+ e = slapi_entry_alloc();
+ if (!e) { result = uid_op_error(34); break; }
+
+ /* NOTE: strdup on the rdn, since it will be freed when
+ * the entry is freed */
+
+ slapi_entry_set_dn(e, slapi_ch_strdup(rdn));
+
+ err = slapi_entry_add_rdn_values(e);
+ if (err)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN bad rdn value=%s\n", rdn);
+ break; /* Bad DN */
+ }
+
+ /*
+ * Find any unique attribute data in the new RDN
+ */
+ err = slapi_entry_attr_find(e, attrName, &attr);
+ if (err) break; /* no UID attribute */
+
+ /*
+ * Passed all the requirements - this is an operation we
+ * need to enforce uniqueness on. Now find all parent entries
+ * with the marker object class, and do a search for each one.
+ */
+ if (NULL != markerObjectClass)
+ {
+ /* Subtree defined by location of marker object class */
+ result = findSubtreeAndSearch(dn, attrName, attr, NULL, dn,
+ markerObjectClass);
+ } else
+ {
+ /* Subtrees listed on invocation line */
+ result = searchAllSubtrees(argc, argv, attrName, attr, NULL, dn);
+ }
+ END
+ /* Clean-up */
+ if (e) slapi_entry_free(e);
+
+ if (result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN result %d\n", result);
+
+ slapi_send_ldap_result(pb, result, 0, moreInfo, 0, 0);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * Initialize the plugin
+ *
+ * uidunique_init (the old name) is deprecated
+ */
+int
+NSUniqueAttr_Init(Slapi_PBlock *pb)
+{
+ int err = 0;
+
+ BEGIN
+ int err;
+ int argc;
+ char **argv;
+
+ /* Declare plugin version */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01);
+ if (err) break;
+
+ /*
+ * Get plugin identity and store it for later use
+ * Used for internal operations
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ /* PR_ASSERT (plugin_identity); */
+
+ /*
+ * Get and normalize arguments
+ */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) break;
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) break;
+
+ /* First argument is the unique attribute name */
+ if (argc < 1) { err = -1; break; }
+ argv++; argc--;
+
+ for(;argc > 0;argc--, argv++)
+ slapi_dn_normalize_case(*argv);
+
+ /* Provide descriptive information */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void*)&pluginDesc);
+ if (err) break;
+
+ /* Register functions */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
+ (void*)preop_add);
+ if (err) break;
+
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+ (void*)preop_modify);
+ if (err) break;
+
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN,
+ (void*)preop_modrdn);
+ if (err) break;
+
+ END
+
+ if (err) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "NSUniqueAttr_Init",
+ "Error: %d\n", err);
+ err = -1;
+ }
+ else
+ slapi_log_error(SLAPI_LOG_PLUGIN, "NSUniqueAttr_Init",
+ "plugin loaded\n");
+
+ return err;
+}
+
+int
+uidunique_init(Slapi_PBlock *pb)
+{
+ return NSUniqueAttr_Init(pb);
+}
+
+
+/* ------------------------------------------------------------ */
+/*
+ * ldap_quote_filter_value
+ *
+ * Quote the filter value according to RFC 2254 (Dec 1997)
+ *
+ * value - a UTF8 string containing the value. It may contain
+ * any of the chars needing quotes ( '(' ')' '*' '/' and NUL ).
+ * len - the length of the UTF8 value
+ * out - a buffer to recieve the converted value. May be NULL, in
+ * which case, only the length of the output is computed (and placed in
+ * outLen).
+ * maxLen - the size of the output buffer. It is an error if this length
+ * is exceeded. Ignored if out is NULL.
+ * outLen - recieves the size of the output. If an error occurs, this
+ * result is not available.
+ *
+ * Returns
+ * 0 - success
+ * -1 - failure (usually a buffer overflow)
+ */
+int /* Error value */
+ldap_quote_filter_value(
+ char *value, int len,
+ char *out, int maxLen,
+ int *outLen)
+{
+ int err;
+ char *eValue;
+ int resLen;
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ static char hexchars[16] = "0123456789abcdef";
+#endif
+
+ err = 0;
+ eValue = &value[len];
+ resLen = 0;
+
+ /*
+ * Convert each character in the input string
+ */
+ while(value < eValue)
+ {
+ switch(*value)
+ {
+ case '(':
+ case ')':
+ case '*':
+ case '\\':
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ case 0:
+#endif
+ /* Handle characters needing special escape sequences */
+
+ /* Compute size of output */
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ resLen += 3;
+#else
+ resLen += 2;
+#endif
+
+ /* Generate output if requested */
+ if (out)
+ {
+ /* Check for overflow */
+ if (resLen > maxLen) { err = -1; break; }
+
+ *out++ = '\\';
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ *out++ = hexchars[(*value >> 4) & 0xF];
+ *out++ = hexchars[*value & 0xF];
+#else
+ *out++ = *value;
+#endif
+ }
+
+ break;
+
+ default:
+ /* Compute size of output */
+ resLen += 1;
+
+ /* Generate output if requested */
+ if (out)
+ {
+ if (resLen > maxLen) { err = -1; break; }
+ *out++ = *value;
+ }
+
+ break;
+ }
+
+ if (err) break;
+
+ value++;
+ }
+
+ if (!err) *outLen = resLen;
+
+ return err;
+}