소스 검색

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.
Nathan Kinder 15 년 전
부모
커밋
c53b8b31c8
3개의 변경된 파일92개의 추가작업 그리고 15개의 파일을 삭제
  1. 42 0
      ldap/servers/slapd/mapping_tree.c
  2. 37 15
      ldap/servers/slapd/passwd_extop.c
  3. 13 0
      ldap/servers/slapd/slapi-plugin.h

+ 42 - 0
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

+ 37 - 15
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);
 

+ 13 - 0
ldap/servers/slapd/slapi-plugin.h

@@ -3139,6 +3139,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.
  *