andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone

Blame 0226-Ticket-417-458-47522-Password-Administrator-Backport.patch

dc8c34
From fa6fba4d7716cb2ce4cd5446e0af790d765d71e2 Mon Sep 17 00:00:00 2001
dc8c34
From: Mark Reynolds <mreynolds@redhat.com>
dc8c34
Date: Wed, 5 Mar 2014 15:55:31 -0500
dc8c34
Subject: [PATCH] Ticket 417, 458, 47522 - Password Administrator Backport
dc8c34
dc8c34
Description:  Backported all the fixes needed to implement the feature.
dc8c34
dc8c34
https://fedorahosted.org/389/ticket/417
dc8c34
https://fedorahosted.org/389/ticket/458
dc8c34
https://fedorahosted.org/389/ticket/47522
dc8c34
dc8c34
Bug 985270 - [RFE] Add Password adminstrators to RHDS 9 as in
dc8c34
http://directory.fedoraproject.org/wiki/Password_Administrator (edit)
dc8c34
dc8c34
Reviewed by: nhosoi(Thanks!)
dc8c34
(cherry picked from commit 096d8958a81ee57d3486d8260430cbfab81a0bbc)
dc8c34
---
dc8c34
 ldap/schema/02common.ldif         |   3 +-
dc8c34
 ldap/servers/slapd/entry.c        |  45 ++++++++++----
dc8c34
 ldap/servers/slapd/libglobs.c     |  24 +++++++-
dc8c34
 ldap/servers/slapd/modify.c       |  25 ++++----
dc8c34
 ldap/servers/slapd/pblock.c       |   6 ++
dc8c34
 ldap/servers/slapd/proto-slap.h   |   1 +
dc8c34
 ldap/servers/slapd/pw.c           | 123 ++++++++++++++++++++++++++++++++++++--
dc8c34
 ldap/servers/slapd/pw.h           |   1 +
dc8c34
 ldap/servers/slapd/slap.h         |   3 +
dc8c34
 ldap/servers/slapd/slapi-plugin.h |  32 ++++++++++
dc8c34
 10 files changed, 233 insertions(+), 30 deletions(-)
dc8c34
dc8c34
diff --git a/ldap/schema/02common.ldif b/ldap/schema/02common.ldif
dc8c34
index ffec7ce..92feb49 100644
dc8c34
--- a/ldap/schema/02common.ldif
dc8c34
+++ b/ldap/schema/02common.ldif
dc8c34
@@ -95,6 +95,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2081 NAME ( 'passwordMaxRepeats' 'pwdMax
dc8c34
 attributeTypes: ( 2.16.840.1.113730.3.1.2082 NAME ( 'passwordMinCategories' 'pwdMinCategories' ) DESC 'Netscape defined password policy attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
dc8c34
 attributeTypes: ( 2.16.840.1.113730.3.1.2083 NAME ( 'passwordMinTokenLength' 'pwdMinTokenLength' ) DESC 'Netscape defined password policy attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
dc8c34
 attributeTypes: ( 2.16.840.1.113730.3.1.2140 NAME ( 'passwordTrackUpdateTime' ) DESC 'Netscape defined password policy attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
dc8c34
+attributeTypes: ( 2.16.840.1.113730.3.1.2153 NAME ( 'passwordAdminDN' 'pwdAdminDN' ) DESC 'Netscape defined password policy attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
dc8c34
 attributeTypes: ( 2.16.840.1.113730.3.1.198 NAME 'memberURL' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Netscape Directory Server' )
dc8c34
 attributeTypes: ( 2.16.840.1.113730.3.1.199 NAME 'memberCertificateDescription' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Netscape Directory Server' )
dc8c34
 attributeTypes: ( 2.16.840.1.113730.3.1.207 NAME 'vlvBase' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'Netscape Directory Server' )
dc8c34
@@ -164,7 +165,7 @@ objectClasses: ( 2.16.840.1.113730.3.2.7 NAME 'nsLicenseUser' DESC 'Netscape def
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.1 NAME 'changeLogEntry' DESC 'LDAP changelog objectclass' SUP top MUST ( targetdn $ changeTime $ changenumber $ changeType ) MAY ( changes $ newrdn $ deleteoldrdn $ newsuperior ) X-ORIGIN 'Changelog Internet Draft' )
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.6 NAME 'referral' DESC 'LDAP referrals objectclass' SUP top MAY ( ref ) X-ORIGIN 'LDAPv3 referrals Internet Draft' )
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.12 NAME 'passwordObject' DESC 'Netscape defined password policy objectclass' SUP top MAY ( pwdpolicysubentry $ passwordExpirationTime $ passwordExpWarned $ passwordRetryCount $ retryCountResetTime $ accountUnlockTime $ passwordHistory $ passwordAllowChangeTime $ passwordGraceUserTime ) X-ORIGIN 'Netscape Directory Server' )
dc8c34
-objectClasses: ( 2.16.840.1.113730.3.2.13 NAME 'passwordPolicy' DESC 'Netscape defined password policy objectclass' SUP top MAY ( passwordMaxAge $ passwordExp $ passwordMinLength $ passwordKeepHistory $ passwordInHistory $ passwordChange $ passwordWarning $ passwordLockout $ passwordMaxFailure $ passwordResetDuration $ passwordUnlock $ passwordLockoutDuration $ passwordCheckSyntax $ passwordMustChange $ passwordStorageScheme $ passwordMinAge $ passwordResetFailureCount $ passwordGraceLimit $ passwordMinDigits $ passwordMinAlphas $ passwordMinUppers $ passwordMinLowers $ passwordMinSpecials $ passwordMin8bit $ passwordMaxRepeats $ passwordMinCategories $ passwordMinTokenLength $ passwordTrackUpdateTime ) X-ORIGIN 'Netscape Directory Server' )
dc8c34
+objectClasses: ( 2.16.840.1.113730.3.2.13 NAME 'passwordPolicy' DESC 'Netscape defined password policy objectclass' SUP top MAY ( passwordMaxAge $ passwordExp $ passwordMinLength $ passwordKeepHistory $ passwordInHistory $ passwordChange $ passwordWarning $ passwordLockout $ passwordMaxFailure $ passwordResetDuration $ passwordUnlock $ passwordLockoutDuration $ passwordCheckSyntax $ passwordMustChange $ passwordStorageScheme $ passwordMinAge $ passwordResetFailureCount $ passwordGraceLimit $ passwordMinDigits $ passwordAdminDN $ passwordMinAlphas $ passwordMinUppers $ passwordMinLowers $ passwordMinSpecials $ passwordMin8bit $ passwordMaxRepeats $ passwordMinCategories $ passwordMinTokenLength $ passwordTrackUpdateTime ) X-ORIGIN 'Netscape Directory Server' )
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.30 NAME 'glue' DESC 'Netscape defined objectclass' SUP top X-ORIGIN 'Netscape Directory Server' )
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.32 NAME 'netscapeMachineData' DESC 'Netscape defined objectclass' SUP top X-ORIGIN 'Netscape Directory Server' )
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.38 NAME 'vlvSearch' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ vlvBase $ vlvScope $ vlvFilter ) MAY ( multiLineDescription ) X-ORIGIN 'Netscape Directory Server' )
dc8c34
diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c
dc8c34
index 785256f..8b7766e 100644
dc8c34
--- a/ldap/servers/slapd/entry.c
dc8c34
+++ b/ldap/servers/slapd/entry.c
dc8c34
@@ -2630,25 +2630,46 @@ slapi_entry_delete_string(Slapi_Entry *e, const char *type, const char *value)
dc8c34
 char **
dc8c34
 slapi_entry_attr_get_charray( const Slapi_Entry* e, const char *type)
dc8c34
 {
dc8c34
-    char **parray = NULL;
dc8c34
-    Slapi_Attr* attr = NULL;
dc8c34
+	int ignore;
dc8c34
+	return slapi_entry_attr_get_charray_ext(e, type, &ignore);
dc8c34
+}
dc8c34
+
dc8c34
+/*
dc8c34
+ * The extension also gathers the number of values.
dc8c34
+ * The caller must free with slapi_ch_array_free
dc8c34
+ */
dc8c34
+char **
dc8c34
+slapi_entry_attr_get_charray_ext( const Slapi_Entry* e, const char *type, int *numVals)
dc8c34
+{
dc8c34
+	char **parray = NULL;
dc8c34
+	Slapi_Attr* attr = NULL;
dc8c34
+	int count = 0;
dc8c34
+
dc8c34
+	if(numVals == NULL){
dc8c34
+		return NULL;
dc8c34
+	}
dc8c34
+
dc8c34
 	slapi_entry_attr_find(e, type, &attr);
dc8c34
-	if(attr!=NULL)
dc8c34
-	{
dc8c34
+	if(attr!=NULL){
dc8c34
 		int hint;
dc8c34
 		Slapi_Value *v = NULL;
dc8c34
+
dc8c34
 		for (hint = slapi_attr_first_value(attr, &v);
dc8c34
 			 hint != -1;
dc8c34
 			 hint = slapi_attr_next_value(attr, hint, &v))
dc8c34
 		{
dc8c34
 			const struct berval *bvp = slapi_value_get_berval(v);
dc8c34
 			char *p = slapi_ch_malloc(bvp->bv_len + 1);
dc8c34
+
dc8c34
 			memcpy(p, bvp->bv_val, bvp->bv_len);
dc8c34
 			p[bvp->bv_len]= '\0';
dc8c34
 			charray_add(&parray, p);
dc8c34
+			count++;
dc8c34
 		}
dc8c34
 	}
dc8c34
-    return parray;
dc8c34
+	*numVals = count;
dc8c34
+
dc8c34
+	return parray;
dc8c34
 }
dc8c34
 
dc8c34
 char *
dc8c34
@@ -2660,16 +2681,18 @@ slapi_entry_attr_get_charptr( const Slapi_Entry* e, const char *type)
dc8c34
 	if(attr!=NULL)
dc8c34
 	{
dc8c34
 		Slapi_Value *v;
dc8c34
-                const struct berval *bvp;
dc8c34
+		const struct berval *bvp;
dc8c34
+
dc8c34
 		slapi_valueset_first_value( &attr->a_present_values, &v);
dc8c34
-                bvp = slapi_value_get_berval(v);
dc8c34
-        p= slapi_ch_malloc(bvp->bv_len + 1);
dc8c34
-        memcpy(p, bvp->bv_val, bvp->bv_len);
dc8c34
-        p[bvp->bv_len]= '\0';
dc8c34
+		bvp = slapi_value_get_berval(v);
dc8c34
+		p= slapi_ch_malloc(bvp->bv_len + 1);
dc8c34
+		memcpy(p, bvp->bv_val, bvp->bv_len);
dc8c34
+		p[bvp->bv_len]= '\0';
dc8c34
 	}
dc8c34
-    return p;
dc8c34
+	return p;
dc8c34
 }
dc8c34
 
dc8c34
+
dc8c34
 int
dc8c34
 slapi_entry_attr_get_int( const Slapi_Entry* e, const char *type)
dc8c34
 {
dc8c34
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
dc8c34
index 8103133..b7dadcd 100644
dc8c34
--- a/ldap/servers/slapd/libglobs.c
dc8c34
+++ b/ldap/servers/slapd/libglobs.c
dc8c34
@@ -248,6 +248,9 @@ static struct config_get_and_set {
dc8c34
 	{CONFIG_PWPOLICY_LOCAL_ATTRIBUTE, config_set_pwpolicy_local,
dc8c34
 		NULL, 0,
dc8c34
 		(void**)&global_slapdFrontendConfig.pwpolicy_local, CONFIG_ON_OFF, NULL},
dc8c34
+	{CONFIG_PW_ADMIN_DN_ATTRIBUTE, config_set_pw_admin_dn,
dc8c34
+		NULL, 0,
dc8c34
+		(void**)&global_slapdFrontendConfig.pw_policy.pw_admin, CONFIG_STRING, NULL},
dc8c34
 	{CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE, NULL,
dc8c34
 		log_set_maxdiskspace, SLAPD_AUDIT_LOG,
dc8c34
 		(void**)&global_slapdFrontendConfig.auditlog_maxdiskspace, CONFIG_INT, NULL},
dc8c34
@@ -685,8 +688,7 @@ static struct config_get_and_set {
dc8c34
 	{CONFIG_DISK_THRESHOLD, config_set_disk_threshold,
dc8c34
 		NULL, 0,
dc8c34
 		(void**)&global_slapdFrontendConfig.disk_threshold,
dc8c34
-		CONFIG_LONG_LONG, (ConfigGetFunc)config_get_disk_threshold,
dc8c34
-		DEFAULT_DISK_THRESHOLD},
dc8c34
+		CONFIG_LONG_LONG, (ConfigGetFunc)config_get_disk_threshold},
dc8c34
 	{CONFIG_DISK_GRACE_PERIOD, config_set_disk_grace_period,
dc8c34
 		NULL, 0,
dc8c34
 		(void**)&global_slapdFrontendConfig.disk_grace_period,
dc8c34
@@ -1122,9 +1124,11 @@ FrontendConfig_init () {
dc8c34
   cfg->disk_grace_period = 60; /* 1 hour */
dc8c34
   cfg->disk_logging_critical = LDAP_OFF;
dc8c34
   cfg->sasl_max_bufsize = SLAPD_DEFAULT_SASL_MAXBUFSIZE;
dc8c34
-
dc8c34
+  cfg->pw_policy.pw_admin = NULL;
dc8c34
+  cfg->pw_policy.pw_admin_user = NULL;
dc8c34
   cfg->listen_backlog_size = DAEMON_LISTEN_SIZE;
dc8c34
   cfg->ignore_time_skew = LDAP_OFF;
dc8c34
+
dc8c34
 #if defined(LINUX)
dc8c34
   cfg->malloc_mxfast = DEFAULT_MALLOC_UNSET;
dc8c34
   cfg->malloc_trim_threshold = DEFAULT_MALLOC_UNSET;
dc8c34
@@ -2837,6 +2841,20 @@ config_set_dn_validate_strict( const char *attrname, char *value, char *errorbuf
dc8c34
 }
dc8c34
 
dc8c34
 int
dc8c34
+config_set_pw_admin_dn( const char *attrname, char *value, char *errorbuf, int apply ) {
dc8c34
+  int retVal =  LDAP_SUCCESS;
dc8c34
+  slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
dc8c34
+
dc8c34
+  if ( apply ) {
dc8c34
+        CFG_LOCK_WRITE(slapdFrontendConfig);
dc8c34
+        slapi_sdn_free(&slapdFrontendConfig->pw_policy.pw_admin);
dc8c34
+        slapdFrontendConfig->pw_policy.pw_admin = slapi_sdn_new_dn_byval(value);
dc8c34
+        CFG_UNLOCK_WRITE(slapdFrontendConfig);
dc8c34
+  }
dc8c34
+  return retVal;
dc8c34
+}
dc8c34
+
dc8c34
+int
dc8c34
 config_set_ds4_compatible_schema( const char *attrname, char *value, char *errorbuf, int apply ) {
dc8c34
   int retVal = LDAP_SUCCESS;
dc8c34
   slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
dc8c34
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
dc8c34
index 817f17c..90c9f8c 100644
dc8c34
--- a/ldap/servers/slapd/modify.c
dc8c34
+++ b/ldap/servers/slapd/modify.c
dc8c34
@@ -1227,11 +1227,9 @@ static int op_shared_allow_pw_change (Slapi_PBlock *pb, LDAPMod *mod, char **old
dc8c34
 		slapi_pblock_set( pb, SLAPI_BACKEND, slapi_be_select( &sdn ) );
dc8c34
 
dc8c34
 		/* Check if ACIs allow password to be changed */
dc8c34
-		if ( (res = slapi_acl_check_mods(pb, e, mods, &errtxt)) != LDAP_SUCCESS) {
dc8c34
-			if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
dc8c34
-			{
dc8c34
-				if (proxydn)
dc8c34
-				{
dc8c34
+		if ( !pw_is_pwp_admin(pb, pwpolicy) && (res = slapi_acl_check_mods(pb, e, mods, &errtxt)) != LDAP_SUCCESS){
dc8c34
+			if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS)){
dc8c34
+				if (proxydn){
dc8c34
 					proxystr = slapi_ch_smprintf(" authzid=\"%s\"", proxydn);
dc8c34
 				}
dc8c34
 
dc8c34
@@ -1243,16 +1241,23 @@ static int op_shared_allow_pw_change (Slapi_PBlock *pb, LDAPMod *mod, char **old
dc8c34
 
dc8c34
 			/* Write access is denied to userPassword by ACIs */
dc8c34
 			if ( pwresponse_req == 1 ) {
dc8c34
-                               	slapi_pwpolicy_make_response_control ( pb, -1, -1,
dc8c34
-						LDAP_PWPOLICY_PWDMODNOTALLOWED );
dc8c34
-                       	}
dc8c34
-
dc8c34
-                       	send_ldap_result(pb, res, NULL, errtxt, 0, NULL);
dc8c34
+				slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDMODNOTALLOWED );
dc8c34
+            }
dc8c34
+            send_ldap_result(pb, res, NULL, errtxt, 0, NULL);
dc8c34
 			slapi_ch_free_string(&errtxt);
dc8c34
 			rc = -1;
dc8c34
 			goto done;
dc8c34
 		}
dc8c34
 
dc8c34
+		/*
dc8c34
+		 * If this mod is being performed by a password administrator/rootDN,
dc8c34
+		 * just return success.
dc8c34
+		 */
dc8c34
+		if(pw_is_pwp_admin(pb, pwpolicy)){
dc8c34
+			rc = 1;
dc8c34
+			goto done;
dc8c34
+		}
dc8c34
+
dc8c34
 		/* Check if password policy allows users to change their passwords.*/
dc8c34
 		if (!pb->pb_op->o_isroot && slapi_sdn_compare(&sdn, &pb->pb_op->o_sdn)==0 &&
dc8c34
 			!pb->pb_conn->c_needpw && !pwpolicy->pw_change)
dc8c34
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
dc8c34
index 9b59e7b..849c20e 100644
dc8c34
--- a/ldap/servers/slapd/pblock.c
dc8c34
+++ b/ldap/servers/slapd/pblock.c
dc8c34
@@ -1803,6 +1803,12 @@ slapi_pblock_get( Slapi_PBlock *pblock, int arg, void *value )
dc8c34
 		}
dc8c34
 		break;
dc8c34
 
dc8c34
+	case SLAPI_REQUESTOR_SDN:
dc8c34
+		if(pblock->pb_op != NULL){
dc8c34
+			(*(Slapi_DN **)value) = &pblock->pb_op->o_sdn;
dc8c34
+		}
dc8c34
+		break;
dc8c34
+
dc8c34
 	case SLAPI_OPERATION_AUTHTYPE:
dc8c34
 		if (pblock->pb_op != NULL)
dc8c34
 		{
dc8c34
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
dc8c34
index 954cfd2..8c9f0fb 100644
dc8c34
--- a/ldap/servers/slapd/proto-slap.h
dc8c34
+++ b/ldap/servers/slapd/proto-slap.h
dc8c34
@@ -347,6 +347,7 @@ int config_set_return_exact_case(const char *attrname,  char *value, char *error
dc8c34
 int config_set_result_tweak(const char *attrname,  char *value, char *errorbuf, int apply );
dc8c34
 int config_set_referral_mode(const char *attrname, char *url, char *errorbuf, int apply);
dc8c34
 int config_set_conntablesize(const char *attrname, char *url, char *errorbuf, int apply);
dc8c34
+int config_set_pw_admin_dn( const char *attrname, char *value, char *errorbuf, int apply );
dc8c34
 int config_set_maxbersize(const char *attrname,  char *value, char *errorbuf, int apply );
dc8c34
 int config_set_maxsasliosize(const char *attrname,  char *value, char *errorbuf, int apply );
dc8c34
 int config_set_versionstring(const char *attrname,  char *versionstring, char *errorbuf, int apply );
dc8c34
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
dc8c34
index 6f3d436..cf400a1 100644
dc8c34
--- a/ldap/servers/slapd/pw.c
dc8c34
+++ b/ldap/servers/slapd/pw.c
dc8c34
@@ -73,6 +73,8 @@ static int update_pw_history( Slapi_PBlock *pb, const Slapi_DN *sdn, char *old_p
dc8c34
 static int check_trivial_words (Slapi_PBlock *, Slapi_Entry *, Slapi_Value **,
dc8c34
 		char *attrtype, int toklen, Slapi_Mods *smods );
dc8c34
 static int pw_boolean_str2value (const char *str);
dc8c34
+static void pw_get_admin_users(passwdPolicy *pwp);
dc8c34
+
dc8c34
 /* static LDAPMod* pw_malloc_mod (char* name, char* value, int mod_op); */
dc8c34
 
dc8c34
 
dc8c34
@@ -588,7 +590,7 @@ update_pw_info ( Slapi_PBlock *pb , char *old_pw) {
dc8c34
 	char *timestr;
dc8c34
 	time_t 		pw_exp_date;
dc8c34
 	time_t      cur_time;
dc8c34
-	const char 	*dn;
dc8c34
+	const char 	*target_dn, *bind_dn;
dc8c34
 	Slapi_DN *sdn = NULL;
dc8c34
 	passwdPolicy *pwpolicy = NULL;
dc8c34
 	int internal_op = 0;
dc8c34
@@ -598,10 +600,11 @@ update_pw_info ( Slapi_PBlock *pb , char *old_pw) {
dc8c34
 	internal_op = slapi_operation_is_flag_set(operation, SLAPI_OP_FLAG_INTERNAL);
dc8c34
 
dc8c34
 	cur_time = current_time();
dc8c34
+	slapi_pblock_get( pb, SLAPI_REQUESTOR_NDN, &bind_dn);
dc8c34
 	slapi_pblock_get( pb, SLAPI_TARGET_SDN, &sdn );
dc8c34
-	dn = slapi_sdn_get_dn(sdn);
dc8c34
+	target_dn = slapi_sdn_get_dn(sdn);
dc8c34
 	
dc8c34
-	pwpolicy = new_passwdPolicy(pb, dn);
dc8c34
+	pwpolicy = new_passwdPolicy(pb, target_dn);
dc8c34
 
dc8c34
 	/* update passwordHistory */
dc8c34
 	if ( old_pw != NULL && pwpolicy->pw_history == 1 ) {
dc8c34
@@ -643,7 +646,8 @@ update_pw_info ( Slapi_PBlock *pb , char *old_pw) {
dc8c34
 	 * we stuff the actual user who initiated the password change in pb_conn.  We check
dc8c34
 	 * for this special case to ensure we reset the expiration date properly. */
dc8c34
 	if ((internal_op && pwpolicy->pw_must_change && (!pb->pb_conn || slapi_dn_isroot(pb->pb_conn->c_dn))) ||
dc8c34
-		(!internal_op && pwpolicy->pw_must_change && (pb->pb_requestor_isroot == 1))) {
dc8c34
+	    (!internal_op && pwpolicy->pw_must_change &&
dc8c34
+	    ((target_dn && bind_dn && strcasecmp(target_dn, bind_dn)) && pw_is_pwp_admin(pb, pwpolicy)))){
dc8c34
 		pw_exp_date = NO_TIME;
dc8c34
 	} else if ( pwpolicy->pw_exp == 1 ) {
dc8c34
 		Slapi_Entry *pse = NULL;
dc8c34
@@ -836,7 +840,7 @@ check_pw_syntax_ext ( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals,
dc8c34
 			 * case for the password modify extended operation. */
dc8c34
 			if (slapi_is_encoded((char *)slapi_value_get_string(vals[i]))) {
dc8c34
 				if ((!is_replication && ((internal_op && pb->pb_conn && !slapi_dn_isroot(pb->pb_conn->c_dn)) ||
dc8c34
-					(!internal_op && !pb->pb_requestor_isroot)))) {
dc8c34
+					(!internal_op && !pw_is_pwp_admin(pb, pwpolicy))))) {
dc8c34
 					PR_snprintf( errormsg, BUFSIZ,
dc8c34
 						"invalid password syntax - passwords with storage scheme are not allowed");
dc8c34
 					if ( pwresponse_req == 1 ) {
dc8c34
@@ -1527,6 +1531,97 @@ pw_add_allowchange_aci(Slapi_Entry *e, int pw_prohibit_change) {
dc8c34
 	slapi_ch_free((void **) &aci_pw);
dc8c34
 }
dc8c34
 
dc8c34
+int
dc8c34
+pw_is_pwp_admin(Slapi_PBlock *pb, passwdPolicy *pwp)
dc8c34
+{
dc8c34
+	Slapi_DN *bind_sdn = NULL;
dc8c34
+	int i;
dc8c34
+
dc8c34
+	/* first check if it's root */
dc8c34
+	if(pb->pb_requestor_isroot){
dc8c34
+			return 1;
dc8c34
+	}
dc8c34
+	/* now check if it's a Password Policy Administrator */
dc8c34
+	slapi_pblock_get(pb, SLAPI_REQUESTOR_SDN, &bind_sdn);
dc8c34
+	if(bind_sdn == NULL){
dc8c34
+			return 0;
dc8c34
+	}
dc8c34
+	for(i = 0; pwp->pw_admin_user && pwp->pw_admin_user[i]; i++){
dc8c34
+			if(slapi_sdn_compare(bind_sdn, pwp->pw_admin_user[i]) == 0){
dc8c34
+					return 1;
dc8c34
+			}
dc8c34
+	}
dc8c34
+
dc8c34
+	return 0;
dc8c34
+}
dc8c34
+
dc8c34
+static void
dc8c34
+pw_get_admin_users(passwdPolicy *pwp)
dc8c34
+{
dc8c34
+	Slapi_PBlock *pb = NULL;
dc8c34
+	const Slapi_DN *sdn = pwp->pw_admin;
dc8c34
+	char **uniquemember_vals = NULL;
dc8c34
+	char **member_vals = NULL;
dc8c34
+	const char *binddn = slapi_sdn_get_dn(sdn);
dc8c34
+	int uniquemember_count = 0;
dc8c34
+	int member_count = 0;
dc8c34
+	int nentries = 0;
dc8c34
+	int count = 0;
dc8c34
+	int res;
dc8c34
+	int i;
dc8c34
+
dc8c34
+	if(binddn == NULL){
dc8c34
+		return;
dc8c34
+	}
dc8c34
+	pb = slapi_pblock_new();
dc8c34
+	/*
dc8c34
+	 *  Check if the DN exists and has "group" objectclasses
dc8c34
+	 */
dc8c34
+	slapi_search_internal_set_pb(pb, binddn, LDAP_SCOPE_BASE,"(|(objectclass=groupofuniquenames)(objectclass=groupofnames))",
dc8c34
+		NULL, 0, NULL, NULL, (void *) plugin_get_default_component_id(), 0);
dc8c34
+	slapi_search_internal_pb(pb);
dc8c34
+	slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res;;
dc8c34
+	if (res != LDAP_SUCCESS) {
dc8c34
+		slapi_pblock_destroy(pb);
dc8c34
+		LDAPDebug(LDAP_DEBUG_ANY, "pw_get_admin_users: search failed for %s: error %d - Password Policy Administrators can not be set\n",
dc8c34
+				slapi_sdn_get_dn(sdn), res, 0);
dc8c34
+		return;
dc8c34
+	}
dc8c34
+	/*
dc8c34
+	 *  Ok, we know we have a valid DN, and nentries will tell us if its a group or a user
dc8c34
+	 */
dc8c34
+	slapi_pblock_get(pb, SLAPI_NENTRIES, &nentries);
dc8c34
+	if ( nentries > 0 ){
dc8c34
+		/*
dc8c34
+		 *  It's a group DN, gather all the members
dc8c34
+		 */
dc8c34
+		Slapi_Entry **entries = NULL;
dc8c34
+
dc8c34
+		slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
dc8c34
+		uniquemember_vals = slapi_entry_attr_get_charray_ext(entries[0], "uniquemember", &uniquemember_count);
dc8c34
+		member_vals = slapi_entry_attr_get_charray_ext(entries[0], "member", &member_count);
dc8c34
+		pwp->pw_admin_user = (Slapi_DN **)slapi_ch_calloc((uniquemember_count + member_count + 1), sizeof(Slapi_DN *));
dc8c34
+		if(uniquemember_count > 0){
dc8c34
+			for(i = 0; i < uniquemember_count; i++){
dc8c34
+				pwp->pw_admin_user[count++] = slapi_sdn_new_dn_passin(uniquemember_vals[i]);
dc8c34
+			}
dc8c34
+		}
dc8c34
+		if(member_count > 0){
dc8c34
+			for(i = 0; i < member_count; i++){
dc8c34
+				pwp->pw_admin_user[count++] = slapi_sdn_new_dn_passin(member_vals[i]);
dc8c34
+			}
dc8c34
+		}
dc8c34
+		slapi_ch_free((void**)&uniquemember_vals);
dc8c34
+		slapi_ch_free((void**)&member_vals);
dc8c34
+	} else {
dc8c34
+		/* It's a single user */
dc8c34
+		pwp->pw_admin_user = (Slapi_DN **)slapi_ch_calloc(2, sizeof(Slapi_DN *));
dc8c34
+		pwp->pw_admin_user[0] = slapi_sdn_dup(sdn);
dc8c34
+	}
dc8c34
+	slapi_free_search_results_internal(pb);
dc8c34
+	slapi_pblock_destroy(pb);
dc8c34
+}
dc8c34
+
dc8c34
 /* This function creates a passwdPolicy structure, loads it from either
dc8c34
  * slapdFrontendconfig or the entry pointed by pwdpolicysubentry and
dc8c34
  * returns the structure.
dc8c34
@@ -1831,6 +1926,13 @@ new_passwdPolicy(Slapi_PBlock *pb, const char *dn)
dc8c34
 						pw_boolean_str2value(slapi_value_get_string(*sval));
dc8c34
 					}
dc8c34
 				}
dc8c34
+				else
dc8c34
+				if (!strcasecmp(attr_name, "passwordAdminDN")) {
dc8c34
+						if ((sval = attr_get_present_values(attr))) {
dc8c34
+								pwdpolicy->pw_admin = slapi_sdn_new_dn_byval(slapi_value_get_string(*sval));
dc8c34
+								pw_get_admin_users(pwdpolicy);
dc8c34
+						}
dc8c34
+				}
dc8c34
 			} /* end of for() loop */
dc8c34
 			if (pw_entry) {
dc8c34
 				slapi_entry_free(pw_entry);
dc8c34
@@ -1851,6 +1953,8 @@ done:
dc8c34
 	*pwdscheme = *slapdFrontendConfig->pw_storagescheme;
dc8c34
 	pwdscheme->pws_name = strdup( slapdFrontendConfig->pw_storagescheme->pws_name );
dc8c34
 	pwdpolicy->pw_storagescheme = pwdscheme;
dc8c34
+	pwdpolicy->pw_admin = slapi_sdn_dup(slapdFrontendConfig->pw_policy.pw_admin);
dc8c34
+	pw_get_admin_users(pwdpolicy);
dc8c34
 
dc8c34
 	return pwdpolicy;
dc8c34
 
dc8c34
@@ -1861,6 +1965,15 @@ delete_passwdPolicy( passwdPolicy **pwpolicy)
dc8c34
 {
dc8c34
 	if (pwpolicy && *pwpolicy) {
dc8c34
 		free_pw_scheme( (*(*pwpolicy)).pw_storagescheme );
dc8c34
+		slapi_sdn_free(&(*(*pwpolicy)).pw_admin);
dc8c34
+		if((*(*pwpolicy)).pw_admin_user){
dc8c34
+				int i = 0;
dc8c34
+				while((*(*pwpolicy)).pw_admin_user[i]){
dc8c34
+						slapi_sdn_free(&(*(*pwpolicy)).pw_admin_user[i]);
dc8c34
+						i++;
dc8c34
+				}
dc8c34
+				slapi_ch_free((void **)&(*(*pwpolicy)).pw_admin_user);
dc8c34
+		}
dc8c34
 		slapi_ch_free((void **)pwpolicy);
dc8c34
 	}
dc8c34
 }
dc8c34
diff --git a/ldap/servers/slapd/pw.h b/ldap/servers/slapd/pw.h
dc8c34
index a470fdd..9bb5cc7 100644
dc8c34
--- a/ldap/servers/slapd/pw.h
dc8c34
+++ b/ldap/servers/slapd/pw.h
dc8c34
@@ -86,6 +86,7 @@ int pw_encodevals_ext( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals
dc8c34
 int checkPrefix(char *cipher, char *schemaName, char **encrypt);
dc8c34
 struct passwordpolicyarray *new_passwdPolicy ( Slapi_PBlock *pb, const char *dn );
dc8c34
 void delete_passwdPolicy( struct passwordpolicyarray **pwpolicy);
dc8c34
+int pw_is_pwp_admin(Slapi_PBlock *pb, struct passwordpolicyarray *pwp);
dc8c34
 
dc8c34
 /* function for checking the values of fine grained password policy attributes */
dc8c34
 int check_pw_duration_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf );
dc8c34
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
dc8c34
index ca30d2a..33cfeb4 100644
dc8c34
--- a/ldap/servers/slapd/slap.h
dc8c34
+++ b/ldap/servers/slapd/slap.h
dc8c34
@@ -2018,6 +2018,7 @@ typedef struct _slapdEntryPoints {
dc8c34
 #define CONFIG_SASL_MAXBUFSIZE "nsslapd-sasl-max-buffer-size"
dc8c34
 #define CONFIG_LISTEN_BACKLOG_SIZE	"nsslapd-listen-backlog-size"
dc8c34
 #define CONFIG_IGNORE_TIME_SKEW "nsslapd-ignore-time-skew"
dc8c34
+#define CONFIG_PW_ADMIN_DN_ATTRIBUTE "passwordAdminDN"
dc8c34
 
dc8c34
 /* getenv alternative */
dc8c34
 #define CONFIG_MALLOC_MXFAST "nsslapd-malloc-mxfast"
dc8c34
@@ -2081,6 +2082,8 @@ typedef struct passwordpolicyarray {
dc8c34
   int pw_is_legacy;
dc8c34
   int pw_track_update_time;
dc8c34
   struct pw_scheme *pw_storagescheme;
dc8c34
+  Slapi_DN *pw_admin;
dc8c34
+  Slapi_DN **pw_admin_user;
dc8c34
 } passwdPolicy;
dc8c34
 
dc8c34
 typedef struct _slapdFrontendConfig {
dc8c34
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
dc8c34
index 9e85dc0..c13a1a8 100644
dc8c34
--- a/ldap/servers/slapd/slapi-plugin.h
dc8c34
+++ b/ldap/servers/slapd/slapi-plugin.h
dc8c34
@@ -1618,6 +1618,38 @@ int slapi_entry_attr_delete( Slapi_Entry *e, const char *type );
dc8c34
  * be \c NULL terminated so that they can be used safely in a string context. If there
dc8c34
  * are no values, \c NULL will be returned. Because the array is \c NULL terminated,
dc8c34
  * the usage should be similar to the sample shown below:
dc8c34
+ *
dc8c34
+ * \code
dc8c34
+ *     char **ary = slapi_entry_attr_get_charray(e, someattr);
dc8c34
+ *     int ii;
dc8c34
+ *     for (ii = 0; ary && ary[ii]; ++ii) {
dc8c34
+ *        char *strval = ary[ii];
dc8c34
+ *        ...
dc8c34
+ *     }
dc8c34
+ *     slapi_ch_array_free(ary);
dc8c34
+ * \endcode
dc8c34
+ *
dc8c34
+ * \param e Entry from which you want to get the values.
dc8c34
+ * \param type Attribute type from which you want to get the values.
dc8c34
+ * \param numVals The number of attribute values will be stored in this variable.
dc8c34
+ * \return A copy of all the values of the attribute.
dc8c34
+ * \return \c NULL if the entry does not contain the attribute or if the attribute
dc8c34
+ *         has no values.
dc8c34
+ * \warning When you are done working with the values, free them from memory by calling
dc8c34
+ *          the slapi_ch_array_free() function.
dc8c34
+ * \see slapi_entry_attr_get_charptr()
dc8c34
+ */
dc8c34
+char **slapi_entry_attr_get_charray_ext( const Slapi_Entry* e, const char *type, int *numVals);
dc8c34
+
dc8c34
+/**
dc8c34
+ * Gets the values of a multi-valued attribute of an entry.
dc8c34
+ *
dc8c34
+ * This function is very similar to slapi_entry_attr_get_charptr(), except that it
dc8c34
+ * returns a <tt>char **</tt> array for multi-valued attributes. The array and all
dc8c34
+ * values are copies. Even if the attribute values are not strings, they will still
dc8c34
+ * be \c NULL terminated so that they can be used safely in a string context. If there
dc8c34
+ * are no values, \c NULL will be returned. Because the array is \c NULL terminated,
dc8c34
+ * the usage should be similar to the sample shown below:
dc8c34
  * 
dc8c34
  * \code
dc8c34
  *     char **ary = slapi_entry_attr_get_charray(e, someattr);
dc8c34
-- 
dc8c34
1.8.1.4
dc8c34