diff --git a/doc/man/man5/slapo-ppolicy.5 b/doc/man/man5/slapo-ppolicy.5 index e7168d849d..401b182749 100644 --- a/doc/man/man5/slapo-ppolicy.5 +++ b/doc/man/man5/slapo-ppolicy.5 @@ -31,6 +31,11 @@ identity; all the operations, when performed with any other identity, may be subjected to constraints, like access control. This overlay requires a rootdn to be configured on the database. .P +During password update, an identity with +.B manage +access to the userPassword attribute is considered a password +administrator where relevant to the IETF Password Policy proposal. +.P Note that the IETF Password Policy proposal for LDAP makes sense when considering a single-valued password attribute, while the userPassword attribute allows multiple values. This implementation diff --git a/servers/slapd/overlays/ppolicy.c b/servers/slapd/overlays/ppolicy.c index f3bed2dac5..c9610de06a 100644 --- a/servers/slapd/overlays/ppolicy.c +++ b/servers/slapd/overlays/ppolicy.c @@ -2031,7 +2031,7 @@ ppolicy_modify( Operation *op, SlapReply *rs ) LDAPPasswordPolicyError pErr = PP_noError; LDAPControl *ctrl = NULL; LDAPControl **oldctrls = NULL; - int is_pwdexop = 0; + int is_pwdexop = 0, is_pwdadmin = 0; int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0, got_del_success = 0; int got_changed = 0, got_history = 0; @@ -2191,6 +2191,10 @@ ppolicy_modify( Operation *op, SlapReply *rs ) goto do_modify; } + if ( access_allowed( op, e, pp.ad, NULL, ACL_MANAGE, NULL ) ) { + is_pwdadmin = 1; + } + for ( ml = op->orm_modlist, pwmod = 0, mod_pw_only = 1, deladd = 0, delmod = NULL, @@ -2327,7 +2331,7 @@ ppolicy_modify( Operation *op, SlapReply *rs ) for(p=tl; p; p=p->next, hsize++); /* count history size */ } - if (be_isroot( op )) goto do_modify; + if (is_pwdadmin) goto do_modify; /* NOTE: according to draft-behera-ldap-password-policy * pwdAllowUserChange == FALSE must only prevent pwd changes @@ -2575,15 +2579,36 @@ do_modify: modtail = mods; } - /* Delete the pwdReset attribute, since it's being reset */ - if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) { - mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); - mods->sml_op = LDAP_MOD_DELETE; - mods->sml_desc = ad_pwdReset; - mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_next = NULL; - modtail->sml_next = mods; - modtail = mods; + if ( zapReset ) { + /* + * ITS#7084 Is this a modification by the password + * administrator? Then force a reset if configured. + * Otherwise clear it. + */ + if ( pp.pwdMustChange && is_pwdadmin ) { + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); + mods->sml_op = LDAP_MOD_REPLACE; + mods->sml_desc = ad_pwdReset; + mods->sml_flags = SLAP_MOD_INTERNAL; + mods->sml_numvals = 1; + mods->sml_values = (BerVarray) ch_calloc( sizeof( struct berval ), 2 ); + mods->sml_nvalues = (BerVarray) ch_calloc( sizeof( struct berval ), 2 ); + + ber_dupbv( &mods->sml_values[0], (struct berval *)&slap_true_bv ); + ber_dupbv( &mods->sml_nvalues[0], (struct berval *)&slap_true_bv ); + + mods->sml_next = NULL; + modtail->sml_next = mods; + modtail = mods; + } else if ( attr_find( e->e_attrs, ad_pwdReset ) ) { + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); + mods->sml_op = LDAP_MOD_DELETE; + mods->sml_desc = ad_pwdReset; + mods->sml_flags = SLAP_MOD_INTERNAL; + mods->sml_next = NULL; + modtail->sml_next = mods; + modtail = mods; + } } /* TODO: do we remove pwdLastSuccess or set it to 'now'? */