浏览代码

Add password change extended operation support.

David Boreham 21 年之前
父节点
当前提交
40d1844a21
共有 4 个文件被更改,包括 567 次插入45 次删除
  1. 1 1
      ldap/servers/slapd/Makefile
  2. 10 3
      ldap/servers/slapd/main.c
  3. 40 41
      ldap/servers/slapd/modify.c
  4. 516 0
      ldap/servers/slapd/passwd_extop.c

+ 1 - 1
ldap/servers/slapd/Makefile

@@ -88,7 +88,7 @@ REGULAR_SLAPD_OBJS=	abandon.o bind.o  \
 	configdse.o pw_mgmt.o auth.o \
 	psearch.o conntable.o \
     stubs.o protect_db.o fileio.o lite_entries.o \
-	getopt_ext.o start_tls_extop.o
+	getopt_ext.o start_tls_extop.o passwd_extop.o
 FEDSE_OBJ= fedse.o
 FEDSE_SRC= fedse.c
 SLAPD_OBJS=	$(REGULAR_SLAPD_OBJS) $(FEDSE_OBJ)

+ 10 - 3
ldap/servers/slapd/main.c

@@ -1015,12 +1015,17 @@ main( int argc, char **argv)
 		/* --ugaston: register the start-tls plugin */
 #ifndef _WIN32		
 		if ( slapd_security_library_is_initialized() != 0 ) {
+	
+		
 		         start_tls_register_plugin();
 			 LDAPDebug( LDAP_DEBUG_PLUGIN, 
 				    "Start TLS plugin registered.\n",
 				    0, 0, 0 );
 		} 
 #endif
+	    passwd_modify_register_plugin();
+	    LDAPDebug( LDAP_DEBUG_PLUGIN, 
+				    "Password Modify plugin registered.\n", 0, 0, 0 );
 
 	    plugin_startall(argc, argv, 1 /* Start Backends */, 1 /* Start Globals */); 
 		if (housekeeping_start((time_t)0, NULL) == NULL) {
@@ -1246,12 +1251,13 @@ process_command_line(int argc, char **argv, char *myname,
 		{"encrypt",ArgOptional,'E'},
 		{0,0,0}};
 
-	char *opts_archive2db = "vd:i:a:SD:";
+	char *opts_archive2db = "vd:i:a:n:SD:";
 	struct opt_ext long_options_archive2db[] = {
 		{"version",ArgNone,'v'},
 		{"debug",ArgRequired,'d'},
 		{"pidfile",ArgRequired,'i'},
 		{"archive",ArgRequired,'a'},
+		{"backEndInstName",ArgRequired,'n'},
 		{"allowMultipleProcesses",ArgNone,'S'},		
 		{"instanceDir",ArgRequired,'D'},
 		{0,0,0}};
@@ -1486,10 +1492,11 @@ process_command_line(int argc, char **argv, char *myname,
 		case 'w':	/* set startup pid file */
 			start_pid_file = rel2abspath( optarg_ext );
 			break;
-		case 'n':	/* which backend to do ldif2db for */
+		case 'n':	/* which backend to do ldif2db/bak2db for */
 			if (slapd_exemode == SLAPD_EXEMODE_LDIF2DB ||
 				slapd_exemode == SLAPD_EXEMODE_DBTEST ||
-				slapd_exemode == SLAPD_EXEMODE_DB2INDEX) {
+				slapd_exemode == SLAPD_EXEMODE_DB2INDEX ||
+				slapd_exemode == SLAPD_EXEMODE_ARCHIVE2DB) {
 				/* The -n argument will give the name of a backend instance. */
 				cmd_line_instance_name = optarg_ext;
 			} else if (slapd_exemode == SLAPD_EXEMODE_DB2LDIF) {

+ 40 - 41
ldap/servers/slapd/modify.c

@@ -90,17 +90,15 @@ do_modify( Slapi_PBlock *pb )
 	int					err;
 	int					pw_change = 0; 	/* 0= no password change */
 	int					ignored_some_mods = 0;
+	int                 has_password_mod = 0; /* number of password mods */
 	char				*old_pw = NULL;	/* remember the old password */
 	char				*dn;
-	LDAPControl		**ctrlp = NULL;
 
 	LDAPDebug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
 
 	slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
 	ber = operation->o_ber;
 
-	slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
-
 	/* count the modify request */
 	PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsModifyEntryOps);
 
@@ -217,47 +215,45 @@ do_modify( Slapi_PBlock *pb )
 		/* check for password change */
 		if ( mod->mod_bvalues != NULL && 
 			 strcasecmp( mod->mod_type, SLAPI_USERPWD_ATTR ) == 0 ){
-			if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
-				op_shared_log_error_access (pb, "MOD", dn, "failed to decode LDAP controls");
-				send_ldap_result( pb, err, NULL, NULL, 0, NULL );
-				goto free_and_return;
-			}
-			pw_change = op_shared_allow_pw_change (pb, mod, &old_pw);
-			if (pw_change == -1)
-			{
-				ber_bvecfree(mod->mod_bvalues);
-				slapi_ch_free((void **)&(mod->mod_type));
-				slapi_ch_free((void **)&mod);
-				goto free_and_return;
-			}
+			has_password_mod++;
 		}
 
 		mod->mod_op |= LDAP_MOD_BVALUES;
 		slapi_mods_add_ldapmod (&smods, mod);
 	}
 
-	if ( tag == LBER_ERROR && !ctrlp )
+	/* check for decoding error */
+	if ( tag == LBER_ERROR )
 	{
 		op_shared_log_error_access (pb, "MOD", dn, "decoding error");
 		send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL );
 		goto free_and_return;
 	} 
 
-	if ( slapi_mods_get_num_mods (&smods) == 0 )
+	/* decode the optional controls - put them in the pblock */
+	if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 )
 	{
-		int		lderr;
-		char	*emsg;
+		op_shared_log_error_access (pb, "MOD", dn, "failed to decode LDAP controls");
+		send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+		goto free_and_return;
+	}
 
-		if ( ignored_some_mods ) {
-			lderr = LDAP_UNWILLING_TO_PERFORM;
-			emsg = "no modifiable attributes specified";
-		} else {
-			lderr = LDAP_PROTOCOL_ERROR;
-			emsg = "no modifications specified";
+	/* if there are any password mods, see if they are allowed */
+	if (has_password_mod) {
+		/* iterate through the mods looking for password mods */
+		for (mod = slapi_mods_get_first_mod(&smods);
+			 mod;
+			 mod = slapi_mods_get_next_mod(&smods)) {
+			if ( mod->mod_bvalues != NULL && 
+				 strcasecmp( mod->mod_type, SLAPI_USERPWD_ATTR ) == 0 ) {
+				/* assumes controls have already been decoded and placed
+				   in the pblock */
+				pw_change = op_shared_allow_pw_change (pb, mod, &old_pw);
+				if (pw_change == -1) {
+					goto free_and_return;
+				}
+			}
 		}
-		op_shared_log_error_access (pb, "MOD", dn, emsg);
-		send_ldap_result( pb, lderr, NULL, emsg, 0, NULL );
-		goto free_and_return;
 	}
 
 	if (!pb->pb_conn->c_isreplication_session &&
@@ -269,19 +265,23 @@ do_modify( Slapi_PBlock *pb )
 		goto free_and_return;
 	}
 
-	/*
-	 * in LDAPv3 there can be optional control extensions on
-	 * the end of an LDAPMessage. we need to read them in and
-	 * pass them to the backend.
-	 */
-	if ( !ctrlp ) { 
-	if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 )
+	/* see if there were actually any mods to perform */
+	if ( slapi_mods_get_num_mods (&smods) == 0 )
 	{
-		op_shared_log_error_access (pb, "MOD", dn, "failed to decode LDAP controls");
-		send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+		int		lderr;
+		char	*emsg;
+
+		if ( ignored_some_mods ) {
+			lderr = LDAP_UNWILLING_TO_PERFORM;
+			emsg = "no modifiable attributes specified";
+		} else {
+			lderr = LDAP_PROTOCOL_ERROR;
+			emsg = "no modifications specified";
+		}
+		op_shared_log_error_access (pb, "MOD", dn, emsg);
+		send_ldap_result( pb, lderr, NULL, emsg, 0, NULL );
 		goto free_and_return;
 	}
-	}
 
 #ifdef LDAP_DEBUG
 	LDAPDebug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
@@ -441,8 +441,7 @@ static int modify_internal_pb (Slapi_PBlock *pb)
 			pw_change = op_shared_allow_pw_change (pb, *mod, &old_pw);
 			if (pw_change == -1)
 			{
-				opresult = LDAP_PARAM_ERROR;
-				slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+				/* The internal result code will already have been set by op_shared_allow_pw_change() */
 				return 0;
 			}
 		}

+ 516 - 0
ldap/servers/slapd/passwd_extop.c

@@ -0,0 +1,516 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Password Modify - LDAP Extended Operation.
+ * RFC 3062
+ *
+ *
+ * This plugin implements the "Password Modify - LDAP3" 
+ * extended operation for LDAP. The plugin function is called by
+ * the server if an LDAP client request contains the OID:
+ * "1.3.6.1.4.1.4203.1.11.1".
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <private/pprio.h>
+
+
+#include <prio.h>
+#include <ssl.h>
+#include "slap.h"
+#include "slapi-plugin.h"
+#include "fe.h"
+
+/* Type of connection for this operation;*/
+#define LDAP_EXTOP_PASSMOD_CONN_SECURE
+
+/* Uncomment the following line FOR TESTING: allows non-SSL connections to use the password change extended op */
+/* #undef LDAP_EXTOP_PASSMOD_CONN_SECURE */
+
+/* ber tags for the PasswdModifyRequestValue sequence */
+#define LDAP_EXTOP_PASSMOD_TAG_USERID	0x80U
+#define LDAP_EXTOP_PASSMOD_TAG_OLDPWD	0x81U
+#define LDAP_EXTOP_PASSMOD_TAG_NEWPWD	0x82U
+
+/* OID of the extended operation handled by this plug-in */
+#define EXOP_PASSWD_OID	"1.3.6.1.4.1.4203.1.11.1"
+
+
+Slapi_PluginDesc passwdopdesc = { "passwd_modify_plugin", "Netscape", "0.1",
+	"Password Modify extended operation plugin" };
+
+/* Check SLAPI_USERPWD_ATTR attribute of the directory entry 
+ * return 0, if the userpassword attribute contains the given pwd value
+ * return -1, if userPassword attribute is absent for given Entry
+ * return LDAP_INVALID_CREDENTIALS,if userPassword attribute and given pwd don't match
+ */
+static int passwd_check_pwd(Slapi_Entry *targetEntry, const char *pwd){
+	int rc = LDAP_SUCCESS;
+	Slapi_Attr *attr = NULL;
+	Slapi_Value cv;
+	Slapi_Value **bvals; 
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_check_pwd\n", 0, 0, 0 );
+	
+	slapi_value_init_string(&cv,pwd);
+	
+	if ( (rc = slapi_entry_attr_find( targetEntry, SLAPI_USERPWD_ATTR, &attr )) == 0 )
+	{ /* we have found the userPassword attribute and it has some value */
+		bvals = attr_get_present_values( attr );
+		if ( slapi_pw_find_sv( bvals, &cv ) != 0 )
+		{
+			rc = LDAP_INVALID_CREDENTIALS;
+		}
+	}
+
+	value_done(&cv);
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_check_pwd: %d\n", rc, 0, 0 );
+	
+	/* if the userPassword attribute is absent then rc is -1 */
+	return rc;
+}
+
+
+/* Searches the dn in directory, 
+ *  If found	 : fills in slapi_entry structure and returns 0
+ *  If NOT found : returns the search result as LDAP_NO_SUCH_OBJECT
+ */
+static int 
+passwd_modify_getEntry( const char *dn, Slapi_Entry **e2 ) {
+	int		search_result = 0;
+	Slapi_DN 	sdn;
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_getEntry\n", 0, 0, 0 );
+	slapi_sdn_init_dn_byref( &sdn, dn );
+	if ((search_result = slapi_search_internal_get_entry( &sdn, NULL, e2,
+ 					plugin_get_default_component_id())) != LDAP_SUCCESS ){
+	 LDAPDebug (LDAP_DEBUG_TRACE, "passwd_modify_getEntry: No such entry-(%s), err (%d)\n",
+					 dn, search_result, 0);
+	}
+
+	slapi_sdn_done( &sdn );
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_getEntry: %d\n", search_result, 0, 0 );
+	return search_result;
+}
+
+
+/* Construct Mods pblock and perform the modify operation 
+ * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT 
+ */
+static int passwd_apply_mods(const char *dn, Slapi_Mods *mods) 
+{
+	Slapi_PBlock pb;
+	Slapi_Operation *operation= NULL;
+	int ret=0;
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_apply_mods\n", 0, 0, 0 );
+
+	if (mods && (slapi_mods_get_num_mods(mods) > 0)) 
+	{
+		pblock_init(&pb);
+		slapi_modify_internal_set_pb (&pb, dn, 
+		  slapi_mods_get_ldapmods_byref(mods),
+		  NULL, /* Controls */
+		  NULL, /* UniqueID */
+		  pw_get_componentID(), /* PluginID */
+		  0); /* Flags */ 
+
+	 /* Plugin operations are INTERNAL by default, bypass it to enforce ACL checks */
+	 slapi_pblock_get (&pb, SLAPI_OPERATION, &operation);
+
+	 ret =slapi_modify_internal_pb (&pb);
+  
+	 slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+	 if (ret != LDAP_SUCCESS){
+	  LDAPDebug(LDAP_DEBUG_TRACE, "WARNING: passwordPolicy modify error %d on entry '%s'\n",
+			ret, dn, 0);
+	 }
+
+	pblock_done(&pb);
+ 	}
+ 
+ 	LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_apply_mods: %d\n", ret, 0, 0 );
+ 
+ 	return ret;
+}
+
+
+
+/* Modify the userPassword attribute field of the entry */
+static int passwd_modify_userpassword(Slapi_Entry *targetEntry, const char *newPasswd)
+{
+	char *dn = NULL;
+	int ret = 0;
+	Slapi_Mods smods;
+	
+    LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_userpassword\n", 0, 0, 0 );
+	
+	slapi_mods_init (&smods, 0);
+	dn = slapi_entry_get_ndn( targetEntry );
+	slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, SLAPI_USERPWD_ATTR, newPasswd);
+
+
+	ret = passwd_apply_mods(dn, &smods);
+ 
+	slapi_mods_done(&smods);
+	
+    LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_userpassword: %d\n", ret, 0, 0 );
+
+	return ret;
+}
+
+/* Password Modify Extended operation plugin function */
+int
+passwd_modify_extop( Slapi_PBlock *pb )
+{
+	char		*oid = NULL;
+	char 		*bindDN = NULL;
+	char		*dn = NULL;
+	char		*oldPasswd = NULL;
+	char		*newPasswd = NULL;
+	char		*errMesg = NULL;
+	char		**reqvals = NULL;
+	int             ret=0, rc=0;
+	unsigned long	tag=0, len=-1;
+	LDAPControl	**ctrlp = NULL; /* unused */
+	Slapi_Operation *operation = NULL; /* unused */
+	struct berval *extop_value = NULL;
+	BerElement	*ber = NULL;
+	Slapi_Entry *targetEntry=NULL;
+	Connection      *conn = NULL;
+	/* Slapi_DN sdn; */
+
+    	LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_extop\n", 0, 0, 0 );
+	/* Get the pb ready for sending Password Modify Extended Responses back to the client. 
+	 * The only requirement is to set the LDAP OID of the extended response to the EXOP_PASSWD_OID. */
+
+	if ( slapi_pblock_set( pb, SLAPI_EXT_OP_RET_OID, EXOP_PASSWD_OID ) != 0 ) {
+		errMesg = "Could not set extended response oid.\n";
+		rc = LDAP_OPERATIONS_ERROR;
+		slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
+				 errMesg );
+		goto free_and_return;
+	}
+
+	/* Before going any further, we'll make sure that the right extended operation plugin
+	 * has been called: i.e., the OID shipped whithin the extended operation request must 
+	 * match this very plugin's OID: EXOP_PASSWD_OID. */
+	if ( slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0 ) {
+		errMesg = "Could not get OID value from request.\n";
+		rc = LDAP_OPERATIONS_ERROR;
+		slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
+				 errMesg );
+		goto free_and_return;
+	} else {
+	        slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
+				 "Received extended operation request with OID %s\n", oid );
+	}
+	
+	if ( strcasecmp( oid, EXOP_PASSWD_OID ) != 0) {
+	        errMesg = "Request OID does not match Passwd OID.\n";
+		rc = LDAP_OPERATIONS_ERROR;
+		goto free_and_return;
+	} else {
+	        slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
+				 "Password Modify extended operation request confirmed.\n" );
+	}
+	
+	/* Now , at least we know that the request was indeed a Password Modify one. */
+
+#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
+	/* Allow password modify only for SSL/TLS established connections */
+	conn = pb->pb_conn;
+	if ( (conn->c_flags & CONN_FLAG_SSL) != CONN_FLAG_SSL) {
+		errMesg = "Operation requires a secure connection.\n";
+		rc = LDAP_CONFIDENTIALITY_REQUIRED;
+		goto free_and_return;
+	}
+#endif
+
+	/* Get the ber value of the extended operation */
+	slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+	
+	if ((ber = ber_init(extop_value)) == NULL)
+	{
+		errMesg = "PasswdModify Request decode failed.\n";
+		rc = LDAP_PROTOCOL_ERROR;
+		goto free_and_return;
+	}
+
+	/* Format of request to parse
+	*
+   	* PasswdModifyRequestValue ::= SEQUENCE {
+     	* userIdentity    [0]  OCTET STRING OPTIONAL
+     	* oldPasswd       [1]  OCTET STRING OPTIONAL
+     	* newPasswd       [2]  OCTET STRING OPTIONAL }
+	*/
+	slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+
+	/* ber parse code */
+	if ( ber_scanf( ber, "{") == LBER_ERROR )
+    	{
+    		LDAPDebug( LDAP_DEBUG_ANY,
+    		    "ber_scanf failed :{\n", 0, 0, 0 );
+    		errMesg = "ber_scanf failed\n";
+		rc = LDAP_PROTOCOL_ERROR;
+		goto free_and_return;
+    	} else {
+		tag = ber_peek_tag( ber, &len);
+	}
+
+	
+	/* identify userID field by tags */
+	if (tag == LDAP_EXTOP_PASSMOD_TAG_USERID )
+	{
+		if ( ber_scanf( ber, "a", &dn) == LBER_ERROR )
+    		{
+    		LDAPDebug( LDAP_DEBUG_ANY,
+    		    "ber_scanf failed :{\n", 0, 0, 0 );
+    		errMesg = "ber_scanf failed at userID parse.\n";
+		rc = LDAP_PROTOCOL_ERROR;
+		goto free_and_return;
+    		}
+		
+		tag = ber_peek_tag( ber, &len);
+	} 
+	
+	
+	/* identify oldPasswd field by tags */
+	if (tag == LDAP_EXTOP_PASSMOD_TAG_OLDPWD )
+	{
+		if ( ber_scanf( ber, "a", &oldPasswd ) == LBER_ERROR )
+    		{
+    		LDAPDebug( LDAP_DEBUG_ANY,
+    		    "ber_scanf failed :{\n", 0, 0, 0 );
+    		errMesg = "ber_scanf failed at oldPasswd parse.\n";
+		rc = LDAP_PROTOCOL_ERROR;
+		goto free_and_return;
+    		}
+		tag = ber_peek_tag( ber, &len);
+	} else {
+		errMesg = "Current passwd must be supplied by the user.\n";
+		rc = LDAP_PARAM_ERROR;
+		goto free_and_return;
+	}
+	
+	/* identify newPasswd field by tags */
+	if (tag ==  LDAP_EXTOP_PASSMOD_TAG_NEWPWD )
+	{
+		if ( ber_scanf( ber, "a", &newPasswd ) == LBER_ERROR )
+    		{
+    		LDAPDebug( LDAP_DEBUG_ANY,
+    		    "ber_scanf failed :{\n", 0, 0, 0 );
+    		errMesg = "ber_scanf failed at newPasswd parse.\n";
+		rc = LDAP_PROTOCOL_ERROR;
+		goto free_and_return;
+    		}
+	} else {
+		errMesg = "New passwd must be supplied by the user.\n";
+		rc = LDAP_PARAM_ERROR;
+		goto free_and_return;
+	}
+	
+	/* Uncomment for debugging, otherwise we don't want to leak the password values into the log... */
+	/* LDAPDebug( LDAP_DEBUG_ARGS, "passwd: dn (%s), oldPasswd (%s) ,newPasswd (%s)\n",
+					 dn, oldPasswd, newPasswd); */
+
+	 
+	 if (oldPasswd == NULL || *oldPasswd == '\0') {
+	 /* Refuse to handle this operation because current password is not provided */
+		errMesg = "Current passwd must be supplied by the user.\n";
+		rc = LDAP_PARAM_ERROR;
+		goto free_and_return;
+	 }
+	 
+	 /* We don't implement password generation, so if the request implies 
+	  * that they asked us to do that, we must refuse to process it  */
+	 if (newPasswd == NULL || *newPasswd == '\0') {
+	 /* Refuse to handle this operation because we don't allow password generation */
+		errMesg = "New passwd must be supplied by the user.\n";
+		rc = LDAP_PARAM_ERROR;
+		goto free_and_return;
+	 }
+	 
+	 /* Get Bind DN */
+	slapi_pblock_get( pb, SLAPI_CONN_DN, &bindDN );
+
+	/* 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;
+	 }
+	 
+	 /* Determine the target DN for this operation */
+	 /* Did they give us a DN ? */
+	 if (dn == NULL || *dn == '\0') {
+	 	/* Get the DN from the bind identity on this connection */
+		dn = bindDN;
+		LDAPDebug( LDAP_DEBUG_ANY,
+    		    "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 */
+	 ret = passwd_modify_getEntry(dn, &targetEntry);
+	 /* If we can't find the entry, then that's an error */
+	 if (ret) {
+	 	/* Couldn't find the entry, fail */
+		errMesg = "No such Entry exists.\n" ;
+		rc = LDAP_NO_SUCH_OBJECT ;
+		goto free_and_return;
+	 }
+	 
+	 /* First thing to do is to ask access control if the bound identity has
+	    rights to modify the userpassword attribute on this entry. If not, then
+		we fail immediately with insufficient access. This means that we don't
+		leak any useful information to the client such as current password
+		wrong, etc.
+	  */
+
+	operation_set_target_spec (pb->pb_op, slapi_entry_get_sdn(targetEntry));
+	slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot );
+
+	/* In order to perform the access control check , we need to select a backend (even though
+	 * we don't actually need it otherwise).
+	 */
+	{
+		Slapi_Backend *be = NULL;
+
+		be = slapi_mapping_tree_find_backend_for_sdn(slapi_entry_get_sdn(targetEntry));
+		if (NULL == be) {
+			errMesg = "Failed to find backend for target entry";
+			rc = LDAP_OPERATIONS_ERROR;
+			goto free_and_return;
+		}
+		slapi_pblock_set(pb, SLAPI_BACKEND, be);
+	}
+
+	ret = slapi_access_allowed ( pb, targetEntry, SLAPI_USERPWD_ATTR, NULL, SLAPI_ACL_WRITE );
+    if ( ret != LDAP_SUCCESS ) {
+		errMesg = "Insufficient access rights\n";
+		rc = LDAP_INSUFFICIENT_ACCESS;
+		goto free_and_return;	
+	}
+	 	 	 
+	/* Now we have the entry which we want to modify
+ 	 * They gave us a password (old), check it against the target entry
+	 * Is the old password valid ?
+	 */
+	ret = passwd_check_pwd(targetEntry, oldPasswd);
+	if (ret) {
+		/* No, then we fail this operation */
+		errMesg = "Invalid oldPasswd value.\n";
+		rc = ret;
+		goto free_and_return;
+	}
+	
+
+	/* Now we're ready to make actual password change */
+	ret = passwd_modify_userpassword(targetEntry, newPasswd);
+	if (ret != LDAP_SUCCESS) {
+		/* Failed to modify the password, e.g. because insufficient access allowed */
+		errMesg = "Failed to update password\n";
+		rc = ret;
+		goto free_and_return;
+	}
+	
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_extop: %d\n", rc, 0, 0 );
+	
+	/* Free anything that we allocated above */
+	free_and_return:
+	
+	if ( targetEntry != NULL ){
+		slapi_entry_free (targetEntry); 
+	}
+	
+	if ( ber != NULL ){
+		ber_free(ber, 1);
+		ber = NULL;
+	}
+	
+	
+	slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
+				 errMesg );
+	send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
+	
+
+	return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+
+}/* passwd_modify_extop */
+
+
+static char *passwd_oid_list[] = {
+	EXOP_PASSWD_OID,
+	NULL
+};
+
+
+static char *passwd_name_list[] = {
+	"passwd_modify_extop",
+	NULL
+};
+
+
+/* Initialization function */
+int passwd_modify_init( Slapi_PBlock *pb )
+{
+	char	**argv;
+	char	*oid;
+
+	/* Get the arguments appended to the plugin extendedop directive. The first argument 
+	 * (after the standard arguments for the directive) should contain the OID of the
+	 * extended operation.
+	 */ 
+
+	if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
+	        slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init", "Could not get argv\n" );
+		return( -1 );
+	}
+
+	/* Compare the OID specified in the configuration file against the Passwd OID. */
+
+	if ( argv == NULL || strcmp( argv[0], EXOP_PASSWD_OID ) != 0 ) {
+		slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init", 
+				 "OID is missing or is not %s\n", EXOP_PASSWD_OID );
+		return( -1 );
+	} else {
+		oid = slapi_ch_strdup( argv[0] );
+		slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init", 
+				 "Registering plug-in for Password Modify extended op %s.\n", oid );
+	}
+
+	/* Register the plug-in function as an extended operation
+	 * plug-in function that handles the operation identified by
+	 * OID 1.3.6.1.4.1.4203.1.11.1 .  Also specify the version of the server 
+	 * plug-in */ 
+	if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 || 
+	     slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&passwdopdesc ) != 0 ||
+	     slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *) passwd_modify_extop ) != 0 ||
+	     slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, passwd_oid_list ) != 0 ||
+	     slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, passwd_name_list ) != 0 ) {
+
+		slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init",
+				 "Failed to set plug-in version, function, and OID.\n" );
+		return( -1 );
+	}
+	
+	return( 0 );
+}
+
+int passwd_modify_register_plugin()
+{
+	slapi_register_plugin( "extendedop", 1 /* Enabled */, "passwd_modify_init", 
+			passwd_modify_init, "Password Modify extended operation",
+			passwd_oid_list, NULL );
+
+	return 0;
+}
+