From ad2aa3bd75074bb88dde0822ff92726652a421dc Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Mon, 21 Jan 2013 14:31:41 -0500
Subject: [PATCH] Ticket 417 - RFE - forcing passwordmustchange attribute by non-cn=directory manager

RFE Description:  Allow users, other than the root DN, to be able to reset passwords
                  and force change.  Currently only root DN can perform this action.

Fix Description:  Now someone can reset a password and "force change after reset" with
                  any account that is not same as the account that's being changed. So
                  if you have permission to change the password of a different entry,
                  then "force change after reset" will be applied.

                  Note: Other ldap vedors behave this exact same way.

                  Also did some code cleanup.

https://fedorahosted.org/389/ticket/417

Reviewed by: ?
---
 ldap/servers/slapd/pw.c |   95 ++++++++++++++++++++++-------------------------
 1 files changed, 44 insertions(+), 51 deletions(-)

diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index 9135a52..8f7bd18 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -582,35 +582,33 @@ pw_rever_encode(Slapi_Value **vals, char * attr_name)
 /* SLAPI_ENTRY_POST_OP must be set */
 
 int
-update_pw_info ( Slapi_PBlock *pb , char *old_pw) {
-
-	Slapi_Mods	smods;
-	char *timestr;
-	time_t 		pw_exp_date;
-	time_t      cur_time;
-	const char 	*dn;
+update_pw_info ( Slapi_PBlock *pb , char *old_pw)
+{
+	Slapi_Operation *operation = NULL;
 	Slapi_DN *sdn = NULL;
+	Slapi_Mods	smods;
 	passwdPolicy *pwpolicy = NULL;
+	time_t pw_exp_date;
+	time_t cur_time;
+	const char 	*target_dn, *bind_dn;
+	char *timestr;
 	int internal_op = 0;
-	Slapi_Operation *operation = NULL;
 
 	slapi_pblock_get(pb, SLAPI_OPERATION, &operation);
+	slapi_pblock_get( pb, SLAPI_TARGET_SDN, &sdn );
+	slapi_pblock_get( pb, SLAPI_REQUESTOR_NDN, &bind_dn);
 	internal_op = slapi_operation_is_flag_set(operation, SLAPI_OP_FLAG_INTERNAL);
-
+	target_dn = slapi_sdn_get_ndn(sdn);
+	pwpolicy = new_passwdPolicy(pb, target_dn);
 	cur_time = current_time();
-	slapi_pblock_get( pb, SLAPI_TARGET_SDN, &sdn );
-	dn = slapi_sdn_get_dn(sdn);
+	slapi_mods_init(&smods, 0);
 	
-	pwpolicy = new_passwdPolicy(pb, dn);
-
 	/* update passwordHistory */
 	if ( old_pw != NULL && pwpolicy->pw_history == 1 ) {
 		update_pw_history(pb, sdn, old_pw);
 		slapi_ch_free ( (void**)&old_pw );
 	}
 
-	slapi_mods_init(&smods, 0);
-	
 	/* Update the "pwdUpdateTime" attribute */
 	if ( pwpolicy->pw_track_update_time ){
 		timestr = format_genTime(cur_time);
@@ -620,68 +618,63 @@ update_pw_info ( Slapi_PBlock *pb , char *old_pw) {
 
 	/* update password allow change time */
 	if ( pwpolicy->pw_minage != 0) {
-		timestr = format_genTime( time_plus_sec( cur_time, 
-			pwpolicy->pw_minage ));
+		timestr = format_genTime( time_plus_sec( cur_time, pwpolicy->pw_minage ));
 		slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordAllowChangeTime", timestr);
 		slapi_ch_free((void **)&timestr);
 	}
 
-		/* Fix for Bug 560707
-		   Removed the restriction that the lock variables (retry count) will
-		   be set only when root resets the passwd.
-		   Now admins will also have these privileges.
-		*/
-        if (pwpolicy->pw_lockout) {
-                set_retry_cnt_mods (pb, &smods, 0 );
-        }
+	/*
+	 * Fix for Bug 560707
+	 * Removed the restriction that the lock variables (retry count) will
+	 * be set only when root resets the password.
+	 * Now admins will also have these privileges.
+	 */
+	if (pwpolicy->pw_lockout) {
+		set_retry_cnt_mods (pb, &smods, 0 );
+	}
 
 	/* Clear the passwordgraceusertime from the user entry */
 	slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordgraceusertime", "0");
 
-	/* If the password is reset by root, mark it the first time logon.  If this is an internal
+	/*
+	 * If the password is reset by a different user, mark it the first time logon.  If this is an internal
 	 * operation, we have a special case for the password modify extended operation where
 	 * we stuff the actual user who initiated the password change in pb_conn.  We check
-	 * for this special case to ensure we reset the expiration date properly. */
-	if ((internal_op && pwpolicy->pw_must_change && (!pb->pb_conn || slapi_dn_isroot(pb->pb_conn->c_dn))) ||
-		(!internal_op && pwpolicy->pw_must_change && (pb->pb_requestor_isroot == 1))) {
+	 * for this special case to ensure we reset the expiration date properly.
+	 */
+	if ((internal_op && pwpolicy->pw_must_change && (!pb->pb_conn || strcmp(target_dn, pb->pb_conn->c_dn))) ||
+	    (!internal_op && pwpolicy->pw_must_change && (target_dn && bind_dn && strcmp(target_dn, bind_dn))))
+	{
 		pw_exp_date = NO_TIME;
 	} else if ( pwpolicy->pw_exp == 1 ) {
 		Slapi_Entry *pse = NULL;
 
 		/* update password expiration date */
-		pw_exp_date = time_plus_sec ( cur_time, 
-			pwpolicy->pw_maxage );
-		
+		pw_exp_date = time_plus_sec ( cur_time, pwpolicy->pw_maxage );
 		slapi_pblock_get(pb,SLAPI_ENTRY_POST_OP,&pse);
-
 		if (pse) {
 		    char *prev_exp_date_str; 
-		  
-		    /* if the password expiry time is SLAPD_END_TIME, 
+		    /*
+		     * if the password expiry time is SLAPD_END_TIME,
 		     * don't roll it back 
 		     */
 		    prev_exp_date_str = slapi_entry_attr_get_charptr(pse,"passwordExpirationTime");
-		  
 		    if (prev_exp_date_str) {
 		        time_t prev_exp_date;
 
-			prev_exp_date = parse_genTime(prev_exp_date_str);
-			
-			if (prev_exp_date == NO_TIME || 
-			    prev_exp_date == NOT_FIRST_TIME) {
-			  /* ignore as will replace */
-			}  else if (prev_exp_date == SLAPD_END_TIME) {
-			    /* Special entries' passwords never expire */
-			  slapi_ch_free((void**)&prev_exp_date_str);
-			  pw_apply_mods(sdn, &smods);
-			  slapi_mods_done(&smods);
-			  return 0;
-			}
-			
-			slapi_ch_free((void**)&prev_exp_date_str);	
+				prev_exp_date = parse_genTime(prev_exp_date_str);
+				if (prev_exp_date == NO_TIME || prev_exp_date == NOT_FIRST_TIME) {
+					/* ignore as will replace */
+				} else if (prev_exp_date == SLAPD_END_TIME) {
+					/* Special entries' passwords never expire */
+					slapi_ch_free((void**)&prev_exp_date_str);
+					pw_apply_mods(sdn, &smods);
+					slapi_mods_done(&smods);
+					return 0;
+				}
+				slapi_ch_free((void**)&prev_exp_date_str);
 		    }
 		} /* post op entry */
-
 	} else if (pwpolicy->pw_must_change) {
 		/*
 		 * pw is not changed by root, and must change pw first time 
-- 
1.7.1

