ldap/servers/plugins/uiduniq/uid.c | 141 ++++++++++++++++++++++++-------------
ldap/servers/slapd/entry.c | 86 ++++++++++++++++++++++
ldap/servers/slapd/slapi-plugin.h | 18 ++++
3 files changed, 196 insertions(+), 49 deletions(-)
New commits:
commit 20833dee417422c53ac0f88195eba81199b0f37b
Author: Nathan Kinder <nkinder(a)redhat.com>
Date: Wed Oct 27 11:45:37 2010 -0700
Bug 619623 - attr-unique-plugin ignores requiredObjectClass on modrdn operations
The attribute uniqueness plug-in is not checking if renamed entries
meet the requiredObjectClass requirements. In addition, a rename
that uses a new superior is not properly handled.
This patch adds a check for requiredObjectClass for MODRDN ops. I
also added a helper function called slapi_entry_rename() which allows
the caller to apply a MODRDN operation to a Slapi_Entry. My patch
makes use of this to create a renamed dummy entry that is used to
check for any attribute uniqueness conflicts.
diff --git a/ldap/servers/plugins/uiduniq/uid.c b/ldap/servers/plugins/uiduniq/uid.c
index b856db0..4871823 100644
--- a/ldap/servers/plugins/uiduniq/uid.c
+++ b/ldap/servers/plugins/uiduniq/uid.c
@@ -771,6 +771,9 @@ preop_modify(Slapi_PBlock *pb)
err = slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &dn);
if (err) { result = uid_op_error(11); break; }
+ /*
+ * Check if it has the required object class
+ */
if (requiredObjectClass &&
!(spb = dnHasObjectClass(dn, requiredObjectClass))) { break; }
@@ -823,18 +826,16 @@ preop_modify(Slapi_PBlock *pb)
static int
preop_modrdn(Slapi_PBlock *pb)
{
- int result;
- Slapi_Entry *e;
+ int result = LDAP_SUCCESS;
+ Slapi_Entry *e = NULL;
+ Slapi_DN *sdn = NULL;
+ Slapi_Value *sv_requiredObjectClass = NULL;
#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 = NULL;
@@ -843,8 +844,9 @@ preop_modrdn(Slapi_PBlock *pb)
char *dn;
char *superior;
char *rdn;
- int isupdatedn;
- Slapi_Attr *attr;
+ int deloldrdn = 0;
+ int isupdatedn;
+ Slapi_Attr *attr;
int argc;
char **argv = NULL;
@@ -879,10 +881,19 @@ preop_modrdn(Slapi_PBlock *pb)
break;
}
+ /* Create a Slapi_Value for the requiredObjectClass to use
+ * for checking the entry. */
+ if (requiredObjectClass) {
+ sv_requiredObjectClass = slapi_value_new_string(requiredObjectClass);
+ }
+
/* 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; }
+ /* Create a Slapi_DN to use for searching. */
+ sdn = slapi_sdn_new_dn_byref(dn);
+
/* 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; }
@@ -902,27 +913,18 @@ preop_modrdn(Slapi_PBlock *pb)
"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 */
+ /* See if the old RDN value is being deleted. */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_DELOLDRDN, &deloldrdn);
+ if (err) { result = uid_op_error(34); break; }
- slapi_entry_set_dn(e, slapi_ch_strdup(rdn));
+ /* Get the entry that is being renamed so we can make a dummy copy
+ * of what it will look like after the rename. */
+ err = slapi_search_internal_get_entry(sdn, NULL, &e, plugin_identity);
+ if (err != LDAP_SUCCESS) { result = uid_op_error(35); break; }
- 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 */
- }
+ /* Apply the rename operation to the dummy entry. */
+ err = slapi_entry_rename(e, rdn, deloldrdn, superior);
+ if (err != LDAP_SUCCESS) { result = uid_op_error(36); break; }
/*
* Find any unique attribute data in the new RDN
@@ -931,6 +933,12 @@ preop_modrdn(Slapi_PBlock *pb)
if (err) break; /* no UID attribute */
/*
+ * Check if it has the required object class
+ */
+ if (requiredObjectClass &&
+ !slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sv_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.
@@ -938,7 +946,7 @@ preop_modrdn(Slapi_PBlock *pb)
if (NULL != markerObjectClass)
{
/* Subtree defined by location of marker object class */
- result = findSubtreeAndSearch(dn, attrName, attr, NULL,
+ result = findSubtreeAndSearch(slapi_entry_get_dn(e), attrName, attr, NULL,
requiredObjectClass, dn,
markerObjectClass);
} else
@@ -949,6 +957,8 @@ preop_modrdn(Slapi_PBlock *pb)
}
END
/* Clean-up */
+ slapi_sdn_free(&sdn);
+ slapi_value_free(&sv_requiredObjectClass);
if (e) slapi_entry_free(e);
if (result)
diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c
index 22f4ae0..33ac468 100644
--- a/ldap/servers/slapd/entry.c
+++ b/ldap/servers/slapd/entry.c
@@ -3056,6 +3056,92 @@ slapi_entry_has_children(const Slapi_Entry *entry)
}
/*
+ * Renames an entry to simulate a MODRDN operation
+ */
+int
+slapi_entry_rename(Slapi_Entry *e, const char *newrdn, int deleteoldrdn, const char *newsuperior)
+{
+ int err = LDAP_SUCCESS;
+ char *newdn = NULL;
+ char *olddn = NULL;
+ Slapi_RDN *oldrdn = NULL;
+ Slapi_Mods *smods = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> slapi_entry_rename\n", 0, 0, 0 );
+
+ /* Check if entry or newrdn are NULL. */
+ if (!e || !newrdn) {
+ err = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ /* Get the old DN. */
+ olddn = slapi_entry_get_dn(e);
+
+ /* If deleteoldrdn, find old RDN values and remove them from the entry. */
+ if (deleteoldrdn) {
+ char *type = NULL;
+ char * val = NULL;
+ int num_rdns = 0;
+ int i = 0;
+
+ oldrdn = slapi_rdn_new_dn(olddn);
+
+ /* Create mods based on the number of rdn elements. */
+ num_rdns = slapi_rdn_get_num_components(oldrdn);
+ smods = slapi_mods_new();
+ slapi_mods_init(smods, num_rdns + 2);
+
+ /* Loop through old rdns and construct a mod to remove the value. */
+ for (i = 0; i < num_rdns; i++) {
+ if (slapi_rdn_get_next(oldrdn, i, &type, &val) != -1) {
+ slapi_mods_add(smods, LDAP_MOD_DELETE, type, strlen(val), val);
+ }
+ }
+
+ /* Apply the mods to the entry. */
+ if ((err = slapi_entry_apply_mods(e, slapi_mods_get_ldapmods_byref(smods))) != LDAP_SUCCESS) {
+ /* A problem was encountered applying the mods. Bail. */
+ goto done;
+ }
+ }
+
+ /* We remove the parentid and entrydn since the backend will change these.
+ * We don't want to give the caller an inconsistent entry. */
+ slapi_entry_attr_delete(e, "parentid");
+ slapi_entry_attr_delete(e, "entrydn");
+
+ /* Build new DN. If newsuperior is set, just use "newrdn,newsuperior". If
+ * newsuperior is not set, need to add newrdn to old superior. */
+ if (newsuperior) {
+ newdn = slapi_ch_smprintf("%s,%s", newrdn, newsuperior);
+ } else {
+ char *oldsuperior = NULL;
+
+ oldsuperior = slapi_dn_parent(olddn);
+ newdn = slapi_ch_smprintf("%s,%s", newrdn, oldsuperior);
+
+ slapi_ch_free_string(&oldsuperior);
+ }
+
+ /* Set the new DN in the entry. This hands off the memory used by newdn to the entry. */
+ slapi_entry_set_dn(e, newdn);
+
+ /* Set the RDN in the entry. */
+ slapi_entry_set_rdn(e, newdn);
+
+ /* Add RDN values to entry. */
+ err = slapi_entry_add_rdn_values(e);
+
+done:
+ slapi_rdn_free(&oldrdn);
+ slapi_mods_free(&smods);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= slapi_entry_rename\n", 0, 0, 0 );
+ return err;
+}
+
+/*
* Apply a set of modifications to an entry
*/
int
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 266fb93..536809d 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -1979,6 +1979,24 @@ void slapi_entry_diff(Slapi_Mods *smods, Slapi_Entry *e1, Slapi_Entry *e2, int d
*/
int slapi_entry_apply_mods(Slapi_Entry *e, LDAPMod **mods);
+/**
+ * Renames a Slapi_Entry.
+ *
+ * This function will rename an existing \c Slapi_Entry, similar to what
+ * would happen with a \c MODRDN operation. New RDN values will be added
+ * as attributes to the entry and old RDN values will be deleted if requested.
+ *
+ * \param e Entry that you want to rename.
+ * \param newrdn The new RDN value to be used for renaming the entry. This must
+ * not be \c NULL.
+ * \param deleteoldrdn Will delete the old RDN values from the entry if set to \c 1.
+ * \param newsuperior The new superior DN to use when renaming the entry. Set this
+ * to \c NULL if you do not want to move the entry.
+ * \return \c LDAP_SUCCESS if the rename was successful, otherwise an LDAP error
+ * is returned.
+ */
+int slapi_entry_rename(Slapi_Entry *e, const char *newrdn, int deleteoldrdn, const char *newsuperior);
+
/*------------------------
* Entry flags.
commit 6a8d9ee5e7ca68b12cd22cb7d87c3db262bca2e7
Author: Nathan Kinder <nkinder(a)redhat.com>
Date: Mon Oct 25 16:44:21 2010 -0700
Bug 619633 - Make attribute uniqueness obey requiredObjectClass
The attribute uniqueness plug-in currently does not enforce the
requiredObjectClass setting properly. The plug-in checks that the
entry being modified meets the requiredObjectClass setting, but it
does not check if the potential conflicts in the database meet this
requirement as well.
This patch changes the behavior to check that both the target and
potential conflicting entries meet the requiredObjectClass setting.
diff --git a/ldap/servers/plugins/uiduniq/uid.c b/ldap/servers/plugins/uiduniq/uid.c
index 01d635e..b856db0 100644
--- a/ldap/servers/plugins/uiduniq/uid.c
+++ b/ldap/servers/plugins/uiduniq/uid.c
@@ -70,7 +70,7 @@ int ldap_quote_filter_value(
static int search_one_berval(const char *baseDN, const char *attrName,
- const struct berval *value, const char *target);
+ const struct berval *value, const char *requiredObjectClass, const char *target);
/*
* ISSUES:
@@ -141,13 +141,14 @@ uid_op_error(int internal_error)
*/
static char *
-create_filter(const char *attribute, const struct berval *value)
+create_filter(const char *attribute, const struct berval *value, const char *requiredObjectClass)
{
char *filter;
char *fp;
char *max;
int attrLen;
int valueLen;
+ int classLen = 0;
int filterLen;
PR_ASSERT(attribute);
@@ -159,13 +160,29 @@ create_filter(const char *attribute, const struct berval *value)
value->bv_len, 0, 0, &valueLen))
return 0;
- filterLen = attrLen + 1 + valueLen + 1;
+ if (requiredObjectClass) {
+ classLen = strlen(requiredObjectClass);
+ /* "(&(objectClass=)())" == 19 */
+ filterLen = attrLen + 1 + valueLen + classLen + 19 + 1;
+ } else {
+ filterLen = attrLen + 1 + valueLen + 1;
+ }
/* Allocate the buffer */
filter = slapi_ch_malloc(filterLen);
fp = filter;
max = &filter[filterLen];
+ /* Place AND expression and objectClass in filter */
+ if (requiredObjectClass) {
+ strcpy(fp, "(&(objectClass=");
+ fp += 15;
+ strcpy(fp, requiredObjectClass);
+ fp += classLen;
+ *fp++ = ')';
+ *fp++ = '(';
+ }
+
/* Place attribute name in filter */
strcpy(fp, attribute);
fp += attrLen;
@@ -178,6 +195,12 @@ create_filter(const char *attribute, const struct berval *value)
fp, max-fp, &valueLen)) { slapi_ch_free((void**)&filter); return 0; }
fp += valueLen;
+ /* Close AND expression if a requiredObjectClass was set */
+ if (requiredObjectClass) {
+ *fp++ = ')';
+ *fp++ = ')';
+ }
+
/* Terminate */
*fp = 0;
@@ -202,7 +225,8 @@ create_filter(const char *attribute, const struct berval *value)
*/
static int
search(const char *baseDN, const char *attrName, Slapi_Attr *attr,
- struct berval **values, const char *target)
+ struct berval **values, const char *requiredObjectClass,
+ const char *target)
{
int result;
@@ -234,15 +258,17 @@ search(const char *baseDN, const char *attrName, Slapi_Attr *attr,
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);
+ result = search_one_berval(baseDN, attrName,
+ slapi_value_get_berval(v),
+ requiredObjectClass, target);
}
}
else
{
for (;*values != NULL && LDAP_SUCCESS == result; values++)
{
- result = search_one_berval(baseDN,attrName,*values,target);
+ result = search_one_berval(baseDN, attrName, *values, requiredObjectClass,
+ target);
}
}
@@ -257,7 +283,8 @@ search(const char *baseDN, const char *attrName, Slapi_Attr *attr,
static int
search_one_berval(const char *baseDN, const char *attrName,
- const struct berval *value, const char *target)
+ const struct berval *value, const char *requiredObjectClass,
+ const char *target)
{
int result;
char *filter;
@@ -279,7 +306,7 @@ search_one_berval(const char *baseDN, const char *attrName,
static char *attrs[] = { "1.1", 0 };
/* Create the filter - this needs to be freed */
- filter = create_filter(attrName, value);
+ filter = create_filter(attrName, value, requiredObjectClass);
#ifdef DEBUG
slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
@@ -376,7 +403,8 @@ search_one_berval(const char *baseDN, const char *attrName,
*/
static int
searchAllSubtrees(int argc, char *argv[], const char *attrName,
- Slapi_Attr *attr, struct berval **values, const char *dn)
+ Slapi_Attr *attr, struct berval **values, const char *requiredObjectClass,
+ const char *dn)
{
int result = LDAP_SUCCESS;
@@ -391,7 +419,7 @@ searchAllSubtrees(int argc, char *argv[], const char *attrName,
* worry about that here.
*/
if (slapi_dn_issuffix(dn, *argv)) {
- result = search(*argv, attrName, attr, values, dn);
+ result = search(*argv, attrName, attr, values, requiredObjectClass, dn);
if (result) break;
}
}
@@ -480,7 +508,8 @@ getArguments(Slapi_PBlock *pb, char **attrName, char **markerObjectClass,
*/
static int
findSubtreeAndSearch(char *parentDN, const char *attrName, Slapi_Attr *attr,
- struct berval **values, const char *target, const char *markerObjectClass)
+ struct berval **values, const char *requiredObjectClass, const char *target,
+ const char *markerObjectClass)
{
int result = LDAP_SUCCESS;
Slapi_PBlock *spb = NULL;
@@ -494,7 +523,8 @@ findSubtreeAndSearch(char *parentDN, const char *attrName, Slapi_Attr *attr,
* Do the search. There is no entry that is allowed
* to have the attribute already.
*/
- result = search(parentDN, attrName, attr, values, target);
+ result = search(parentDN, attrName, attr, values, requiredObjectClass,
+ target);
break;
}
}
@@ -606,11 +636,13 @@ preop_add(Slapi_PBlock *pb)
{
/* Subtree defined by location of marker object class */
result = findSubtreeAndSearch(dn, attrName, attr, NULL,
- dn, markerObjectClass);
+ requiredObjectClass, dn,
+ markerObjectClass);
} else
{
/* Subtrees listed on invocation line */
- result = searchAllSubtrees(argc, argv, attrName, attr, NULL, dn);
+ result = searchAllSubtrees(argc, argv, attrName, attr, NULL,
+ requiredObjectClass, dn);
}
END
@@ -756,14 +788,13 @@ preop_modify(Slapi_PBlock *pb)
if (NULL != markerObjectClass)
{
/* Subtree defined by location of marker object class */
- result = findSubtreeAndSearch(dn, attrName, NULL,
- mod->mod_bvalues, dn,
- markerObjectClass);
+ result = findSubtreeAndSearch(dn, attrName, NULL, mod->mod_bvalues,
+ requiredObjectClass, dn, markerObjectClass);
} else
{
/* Subtrees listed on invocation line */
result = searchAllSubtrees(argc, argv, attrName, NULL,
- mod->mod_bvalues, dn);
+ mod->mod_bvalues, requiredObjectClass, dn);
}
}
END
@@ -907,12 +938,14 @@ preop_modrdn(Slapi_PBlock *pb)
if (NULL != markerObjectClass)
{
/* Subtree defined by location of marker object class */
- result = findSubtreeAndSearch(dn, attrName, attr, NULL, dn,
- markerObjectClass);
+ result = findSubtreeAndSearch(dn, attrName, attr, NULL,
+ requiredObjectClass, dn,
+ markerObjectClass);
} else
{
/* Subtrees listed on invocation line */
- result = searchAllSubtrees(argc, argv, attrName, attr, NULL, dn);
+ result = searchAllSubtrees(argc, argv, attrName, attr, NULL,
+ requiredObjectClass, dn);
}
END
/* Clean-up */