ldap/servers/slapd/mapping_tree.c | 42 ++++++++++++++++++++++++++++++
ldap/servers/slapd/passwd_extop.c | 52 +++++++++++++++++++++++++++-----------
ldap/servers/slapd/slapi-plugin.h | 13 +++++++++
3 files changed, 92 insertions(+), 15 deletions(-)
New commits:
commit 92ca2bbfa7a3462b9e4317b0fdb6828f60612021
Author: Nathan Kinder <nkinder(a)redhat.com>
Date: Thu Apr 8 15:55:29 2010 -0700
Bug 578863 - Password modify extop needs to send referrals on replicas
The password modify extended operation was modifying the local database
on a read-only replica instead of returning a referral. The server is
designed to let the plugin ID used for updating password retry info make
local updates instead of returning a referral. This plugin ID was being
used by the password extop code, which it should not be doing.
The second issue is that we need to check if a referral needs to be sent
as early as possible when processing the extop request. We don't want
to reject the change if an entry does not exist before checking if a
referral is necessary since the server we refer to may have the target
entry present. This required adding a new helper function that allows
one to see if a write operation to a particular DN would require a
referral to be sent. The password modify extop code leverages this new
function to get the referrals and return them to the client if necessary.
diff --git a/ldap/servers/slapd/mapping_tree.c b/ldap/servers/slapd/mapping_tree.c
index 479909a..e9d5c3f 100644
--- a/ldap/servers/slapd/mapping_tree.c
+++ b/ldap/servers/slapd/mapping_tree.c
@@ -1925,6 +1925,48 @@ static int sdn_is_nulldn(const Slapi_DN *sdn){
return 0;
}
+/* Checks if a write operation for a particular DN would
+ * require a referral to be sent. */
+int slapi_dn_write_needs_referral(Slapi_DN *target_sdn, Slapi_Entry **referral)
+{
+ mapping_tree_node *target_node = NULL;
+ int ret = 0;
+
+ if(mapping_tree_freed){
+ /* shutdown detected */
+ goto done;
+ }
+
+ if(!mapping_tree_inited) {
+ mapping_tree_init();
+ }
+
+ if (target_sdn) {
+ mtn_lock();
+
+ /* Get the mapping tree node that is the best match for the target dn. */
+ target_node = slapi_get_mapping_tree_node_by_dn(target_sdn);
+ if (target_node == NULL) {
+ target_node = mapping_tree_root;
+ }
+
+ /* See if we need to return a referral. */
+ if ((target_node->mtn_state == MTN_REFERRAL) ||
+ (target_node->mtn_state == MTN_REFERRAL_ON_UPDATE)) {
+ *referral = (target_node->mtn_referral_entry ?
+ slapi_entry_dup(target_node->mtn_referral_entry) :
+ NULL);
+ if (*referral) {
+ ret = 1;
+ }
+ }
+
+ mtn_unlock();
+ }
+
+ done:
+ return ret;
+}
/*
* Description:
* The reason we have a mapping tree. This function selects a backend or
diff --git a/ldap/servers/slapd/passwd_extop.c b/ldap/servers/slapd/passwd_extop.c
index b5f30e0..14341f9 100644
--- a/ldap/servers/slapd/passwd_extop.c
+++ b/ldap/servers/slapd/passwd_extop.c
@@ -167,7 +167,7 @@ static int passwd_apply_mods(Slapi_PBlock *pb_orig, const char *dn, Slapi_Mods *
slapi_modify_internal_set_pb (&pb, dn,
slapi_mods_get_ldapmods_byref(mods),
req_controls_copy, NULL, /* UniqueID */
- pw_get_componentID(), /* PluginID */
+ plugin_get_default_component_id(), /* PluginID */
0); /* Flags */
/* We copy the connection from the original pblock into the
@@ -472,6 +472,8 @@ passwd_modify_extop( Slapi_PBlock *pb )
LDAPControl **req_controls = NULL;
LDAPControl **resp_controls = NULL;
passwdPolicy *pwpolicy = NULL;
+ Slapi_DN *target_sdn = NULL;
+ Slapi_Entry *referrals = NULL;
/* Slapi_DN sdn; */
LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_extop\n", 0, 0, 0 );
@@ -607,19 +609,33 @@ parse_req_done:
/* LDAPDebug( LDAP_DEBUG_ARGS, "passwd: dn (%s), oldPasswd (%s) ,newPasswd (%s)\n",
dn, oldPasswd, newPasswd); */
-
- /* Get Bind DN */
- slapi_pblock_get( pb, SLAPI_CONN_DN, &bindDN );
+ /* Get Bind DN */
+ slapi_pblock_get( pb, SLAPI_CONN_DN, &bindDN );
+
+ /* Find and set the target DN. */
+ if (dn && *dn != '\0') {
+ target_sdn = slapi_sdn_new_dn_byref(dn);
+ slapi_pblock_set(pb, SLAPI_TARGET_DN, dn);
+ } else if (bindDN && *bindDN != '\0') {
+ target_sdn = slapi_sdn_new_dn_byref(bindDN);
+ slapi_pblock_set(pb, SLAPI_TARGET_DN, bindDN);
+ }
- /* If the connection is bound anonymously, we must refuse to process this operation. */
- if (bindDN == NULL || *bindDN == '\0') {
+ /* Check if we need to send any referrals. */
+ if (slapi_dn_write_needs_referral(target_sdn, &referrals)) {
+ rc = LDAP_REFERRAL;
+ goto free_and_return;
+ }
+
+ /* If the connection is bound anonymously, we must refuse to process this operation. */
+ if (bindDN == NULL || *bindDN == '\0') {
/* Refuse the operation because they're bound anonymously */
errMesg = "Anonymous Binds are not allowed.\n";
rc = LDAP_INSUFFICIENT_ACCESS;
goto free_and_return;
- }
+ }
- if (oldPasswd == NULL || *oldPasswd == '\0') {
+ if (oldPasswd == NULL || *oldPasswd == '\0') {
/* If user is authenticated, they already gave their password during
* the bind operation (or used sasl or client cert auth or OS creds) */
slapi_pblock_get(pb, SLAPI_CONN_AUTHMETHOD, &authmethod);
@@ -628,7 +644,7 @@ parse_req_done:
rc = LDAP_INSUFFICIENT_ACCESS;
goto free_and_return;
}
- }
+ }
/* Fetch the password policy. We need this in case we need to
* generate a password as well as for some policy checks. */
@@ -694,7 +710,7 @@ parse_req_done:
"Missing userIdentity in request, using the bind DN instead.\n",
0, 0, 0 );
}
-
+
slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn );
/* Now we have the DN, look for the entry */
@@ -807,6 +823,16 @@ parse_req_done:
/* Free anything that we allocated above */
free_and_return:
+ slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop",
+ errMesg ? errMesg : "success" );
+
+ if ((rc == LDAP_REFERRAL) && (referrals)) {
+ send_referrals_from_entry(pb, referrals);
+ } else {
+ send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
+ }
+
+ slapi_sdn_free(&target_sdn);
slapi_ch_free_string(&bindDN); /* slapi_pblock_get SLAPI_CONN_DN does strdup */
slapi_ch_free_string(&oldPasswd);
slapi_ch_free_string(&newPasswd);
@@ -821,6 +847,7 @@ free_and_return:
slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, NULL );
slapi_ch_free_string(&authmethod);
delete_passwdPolicy(&pwpolicy);
+ slapi_entry_free(referrals);
if ( targetEntry != NULL ){
slapi_entry_free (targetEntry);
@@ -831,11 +858,6 @@ free_and_return:
ber = NULL;
}
- slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop",
- errMesg ? errMesg : "success" );
- send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
-
-
/* We can free the generated password bval now */
ber_bvfree(gen_passwd);
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 2640ed1..883e966 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -2918,6 +2918,19 @@ int slapi_dn_isroot( const char *dn );
int slapi_dn_isbesuffix( Slapi_PBlock *pb, const char *dn );
/**
+ * Checks if writes to a particular DN need to send a referral.
+ *
+ * \param target_sdn The target DN that you want to check.
+ * \param referral The address of a pointer to receive a referral
+ * if one is needed.
+ * \return \c 1 if a referral needs to be sent.
+ * \return \c 0 if no referral is needed.
+ * \warning The referral entry must be freed when it is no longer
+ * being used.
+ */
+int slapi_dn_write_needs_referral(Slapi_DN *target_sdn, Slapi_Entry **referral);
+
+/**
* Converts the second RDN type value to the berval value.
*
* Returns the new RDN value as a berval value in \c bv. This function