From d95b88da2158dc51df8f7b6ce4ddc76ab321642a Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Sun, 3 Sep 2006 10:53:38 +0000 Subject: [PATCH] plug slapo-ppolicy related leaks (ITS#4665) --- servers/slapd/overlays/ppolicy.c | 297 +++++++++++++++++++------------ servers/slapd/passwd.c | 5 + 2 files changed, 192 insertions(+), 110 deletions(-) diff --git a/servers/slapd/overlays/ppolicy.c b/servers/slapd/overlays/ppolicy.c index d078a6352d..b694be7ba8 100644 --- a/servers/slapd/overlays/ppolicy.c +++ b/servers/slapd/overlays/ppolicy.c @@ -278,9 +278,13 @@ ppolicy_cf_default( ConfigArgs *c ) /* fallthrough to LDAP_MOD_ADD */ case LDAP_MOD_ADD: Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n", 0, 0, 0); - if ( pi->def_policy.bv_val ) + if ( pi->def_policy.bv_val ) { ber_memfree ( pi->def_policy.bv_val ); + } pi->def_policy = c->value_ndn; + ber_memfree( c->value_dn.bv_val ); + BER_BVZERO( &c->value_dn ); + BER_BVZERO( &c->value_ndn ); rc = 0; break; default: @@ -362,25 +366,29 @@ create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err ) LDAPControl *c; struct berval bv; - if ((c = ch_calloc( sizeof( LDAPControl ), 1 )) == NULL) return NULL; + c = ch_calloc( sizeof( LDAPControl ), 1 ); + if ( c == NULL ) { + return NULL; + } c->ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYRESPONSE; c->ldctl_iscritical = 0; - c->ldctl_value.bv_val = NULL; - c->ldctl_value.bv_len = 0; + BER_BVZERO( &c->ldctl_value ); ber_init2( ber, NULL, LBER_USE_DER ); - ber_printf(ber, "{" /*}*/ ); + ber_printf( ber, "{" /*}*/ ); - if (exptime >= 0) { + if ( exptime >= 0 ) { ber_init2( b2, NULL, LBER_USE_DER ); ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime ); ber_flatten2( b2, &bv, 1 ); + (void)ber_free_buf(b2); ber_printf( ber, "tO", PPOLICY_WARNING, &bv ); ch_free( bv.bv_val ); - } else if (grace > 0) { + } else if ( grace > 0 ) { ber_init2( b2, NULL, LBER_USE_DER ); ber_printf( b2, "ti", PPOLICY_GRACE, grace ); ber_flatten2( b2, &bv, 1 ); + (void)ber_free_buf(b2); ber_printf( ber, "tO", PPOLICY_WARNING, &bv ); ch_free( bv.bv_val ); } @@ -392,13 +400,41 @@ create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err ) if (ber_flatten2( ber, &(c->ldctl_value), 1 ) == LBER_DEFAULT) { ch_free(c); - (void)ber_free_buf(ber); - return NULL; + c = NULL; } (void)ber_free_buf(ber); return c; } +static LDAPControl ** +add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl ) +{ + LDAPControl **ctrls, **oldctrls = rs->sr_ctrls; + int n; + + n = 0; + if ( oldctrls ) { + for ( ; oldctrls[n]; n++ ) + ; + } + n += 2; + + ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx ); + + n = 0; + if ( oldctrls ) { + for ( ; oldctrls[n]; n++ ) { + ctrls[n] = oldctrls[n]; + } + } + ctrls[n] = ctrl; + ctrls[n+1] = NULL; + + rs->sr_ctrls = ctrls; + + return oldctrls; +} + static void ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) { @@ -644,50 +680,75 @@ parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw ); - if ( oid ) *oid = 0; + if ( oid ) { + *oid = 0; + } *oldtime = (time_t)-1; - oldpw->bv_val = NULL; - oldpw->bv_len = 0; + BER_BVZERO( oldpw ); ber_dupbv( &nv, bv ); /* first get the time field */ - for(i=0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++); - if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */ + for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ ) + ; + if ( i == nv.bv_len ) { + goto exit_failure; /* couldn't locate the '#' separator */ + } nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */ ptr = nv.bv_val; *oldtime = parse_time( ptr ); - if (*oldtime == (time_t)-1) goto exit_failure; + if (*oldtime == (time_t)-1) { + goto exit_failure; + } /* get the OID field */ - for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++); - if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */ + for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ ) + ; + if ( i == nv.bv_len ) { + goto exit_failure; /* couldn't locate the '#' separator */ + } nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */ - if ( oid ) *oid = ber_strdup( ptr ); + if ( oid ) { + *oid = ber_strdup( ptr ); + } /* get the length field */ - for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++); - if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */ + for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ ) + ; + if ( i == nv.bv_len ) { + goto exit_failure; /* couldn't locate the '#' separator */ + } nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */ oldpw->bv_len = strtol( ptr, NULL, 10 ); - if (errno == ERANGE) goto exit_failure; + if (errno == ERANGE) { + goto exit_failure; + } /* lastly, get the octets of the string */ - for(j=i, ptr = &(nv.bv_val[i]);i < nv.bv_len; i++); - if (i-j != oldpw->bv_len) goto exit_failure; /* length is wrong */ + for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ ) + ; + if ( i - j != oldpw->bv_len) { + goto exit_failure; /* length is wrong */ + } npw.bv_val = ptr; npw.bv_len = oldpw->bv_len; ber_dupbv( oldpw, &npw ); + ber_memfree( nv.bv_val ); return LDAP_SUCCESS; -exit_failure: - if (oid && *oid) { ber_memfree(*oid); *oid = NULL; } - if (oldpw->bv_val) { - ber_memfree( oldpw->bv_val); oldpw->bv_val = NULL; - oldpw->bv_len = 0; + +exit_failure:; + if ( oid && *oid ) { + ber_memfree(*oid); + *oid = NULL; } - ber_memfree(nv.bv_val); + if ( oldpw->bv_val ) { + ber_memfree( oldpw->bv_val); + BER_BVZERO( oldpw ); + } + ber_memfree( nv.bv_val ); + return LDAP_OTHER; } @@ -775,13 +836,50 @@ free_pwd_history_list( pw_hist **l ) typedef struct ppbind { slap_overinst *on; int send_ctrl; + LDAPControl **oldctrls; Modifications *mod; LDAPPasswordPolicyError pErr; PassPolicy pp; } ppbind; +static void +ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls ) +{ + int n; + + assert( rs->sr_ctrls != NULL ); + assert( rs->sr_ctrls[0] != NULL ); + + for ( n = 0; rs->sr_ctrls[n]; n++ ) { + if ( rs->sr_ctrls[n]->ldctl_oid == LDAP_CONTROL_PASSWORDPOLICYRESPONSE ) { + ch_free( rs->sr_ctrls[n]->ldctl_value.bv_val ); + ch_free( rs->sr_ctrls[n] ); + rs->sr_ctrls[n] = (LDAPControl *)(-1); + break; + } + } + + if ( rs->sr_ctrls[n] == NULL ) { + /* missed? */ + } + + op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx ); + + rs->sr_ctrls = oldctrls; +} + static int -ppolicy_bind_resp( Operation *op, SlapReply *rs ) +ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs ) +{ + ppbind *ppb = op->o_callback->sc_private; + if ( ppb->send_ctrl ) { + ctrls_cleanup( op, rs, ppb->oldctrls ); + } + return SLAP_CB_CONTINUE; +} + +static int +ppolicy_bind_response( Operation *op, SlapReply *rs ) { ppbind *ppb = op->o_callback->sc_private; slap_overinst *on = ppb->on; @@ -1042,17 +1140,16 @@ locked: } if ( ppb->send_ctrl ) { - LDAPControl **ctrls = NULL; + LDAPControl *ctrl = NULL; pp_info *pi = on->on_bi.bi_private; /* Do we really want to tell that the account is locked? */ if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) { ppb->pErr = PP_noError; } - ctrls = ch_calloc( sizeof( LDAPControl *) , 2 ); - ctrls[0] = create_passcontrol( warn, ngut, ppb->pErr ); - ctrls[1] = NULL; - rs->sr_ctrls = ctrls; + ctrl = create_passcontrol( warn, ngut, ppb->pErr ); + ppb->oldctrls = add_passcontrol( op, rs, ctrl ); + op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup; } op->o_bd->bd_info = bi; return SLAP_CB_CONTINUE; @@ -1091,7 +1188,7 @@ ppolicy_bind( Operation *op, SlapReply *rs ) /* Setup a callback so we can munge the result */ - cb->sc_response = ppolicy_bind_resp; + cb->sc_response = ppolicy_bind_response; cb->sc_next = op->o_callback->sc_next; cb->sc_private = ppb; op->o_callback->sc_next = cb; @@ -1147,6 +1244,7 @@ ppolicy_restrict( } if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) { + LDAPControl **oldctrls; /* if the current authcDN doesn't match the one we recorded, * then an intervening Bind has succeeded and the restriction * no longer applies. (ITS#4516) @@ -1161,16 +1259,16 @@ ppolicy_restrict( Debug( LDAP_DEBUG_TRACE, "connection restricted to password changing only\n", 0, 0, 0); if ( send_ctrl ) { - LDAPControl **ctrls = NULL; - - ctrls = ch_calloc( sizeof( LDAPControl *) , 2 ); - ctrls[0] = create_passcontrol( -1, -1, PP_changeAfterReset ); - ctrls[1] = NULL; - rs->sr_ctrls = ctrls; + LDAPControl *ctrl = NULL; + ctrl = create_passcontrol( -1, -1, PP_changeAfterReset ); + oldctrls = add_passcontrol( op, rs, ctrl ); } op->o_bd->bd_info = (BackendInfo *)on->on_info; send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" ); + if ( send_ctrl ) { + ctrls_cleanup( op, rs, oldctrls ); + } return rs->sr_err; } @@ -1224,16 +1322,17 @@ ppolicy_add( } rc = check_password_quality( bv, &pp, &pErr, op->ora_e ); if (rc != LDAP_SUCCESS) { + LDAPControl **oldctrls = NULL; op->o_bd->bd_info = (BackendInfo *)on->on_info; if ( send_ctrl ) { - LDAPControl **ctrls = NULL; - - ctrls = ch_calloc( sizeof( LDAPControl *) , 2 ); - ctrls[0] = create_passcontrol( -1, -1, pErr ); - ctrls[1] = NULL; - rs->sr_ctrls = ctrls; + LDAPControl *ctrl = NULL; + ctrl = create_passcontrol( -1, -1, pErr ); + oldctrls = add_passcontrol( op, rs, ctrl ); } send_ldap_error( op, rs, rc, "Password fails quality checking policy" ); + if ( send_ctrl ) { + ctrls_cleanup( op, rs, oldctrls ); + } return rs->sr_err; } } @@ -1316,6 +1415,7 @@ ppolicy_modify( Operation *op, SlapReply *rs ) struct berval newpw = BER_BVNULL, oldpw = BER_BVNULL, *bv, cr[2]; LDAPPasswordPolicyError pErr = PP_noError; + LDAPControl **oldctrls = NULL; op->o_bd->bd_info = (BackendInfo *)on->on_info; rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); @@ -1335,7 +1435,7 @@ ppolicy_modify( Operation *op, SlapReply *rs ) a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime ); a_fail = attr_find( e->e_attrs, ad_pwdFailureTime ); - for( prev = &op->oq_modify.rs_modlist, ml = *prev; ml; ml = *prev ) { + for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) { if ( ml->sml_desc == slap_schema.si_ad_userPassword ) got_pw = 1; @@ -1439,7 +1539,7 @@ ppolicy_modify( Operation *op, SlapReply *rs ) ppolicy_get( op, e, &pp ); - for ( ml = op->oq_modify.rs_modlist, + for ( ml = op->orm_modlist, pwmod = 0, mod_pw_only = 1, deladd = 0, delmod = NULL, addmod = NULL, @@ -1487,7 +1587,7 @@ ppolicy_modify( Operation *op, SlapReply *rs ) } } - } else if (! is_at_operational( ml->sml_desc->ad_type )) { + } else if ( !is_at_operational( ml->sml_desc->ad_type ) ) { mod_pw_only = 0; /* modifying something other than password */ } @@ -1578,16 +1678,14 @@ ppolicy_modify( Operation *op, SlapReply *rs ) * let the matching value get found later */ if (pp.pwdSafeModify && oldpw.bv_val ) { - ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); + ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 ); ml->sml_op = LDAP_MOD_DELETE; ml->sml_flags = SLAP_MOD_INTERNAL; ml->sml_desc = pp.ad; ml->sml_type = pp.ad->ad_cname; ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); ber_dupbv( &ml->sml_values[0], &oldpw ); - ml->sml_values[1].bv_len = 0; - ml->sml_values[1].bv_val = NULL; - ml->sml_nvalues = NULL; + BER_BVZERO( &ml->sml_values[1] ); ml->sml_next = op->orm_modlist; op->orm_modlist = ml; delmod = ml; @@ -1653,19 +1751,18 @@ ppolicy_modify( Operation *op, SlapReply *rs ) * replace the delete value with the (possibly hashed) * value which is currently in the password. */ - for(i=0; delmod->sml_values[i].bv_val; i++) { - free(delmod->sml_values[i].bv_val); - delmod->sml_values[i].bv_len = 0; + for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) { + free( delmod->sml_values[i].bv_val ); + BER_BVZERO( &delmod->sml_values[i] ); } - free(delmod->sml_values); + free( delmod->sml_values ); delmod->sml_values = ch_calloc( sizeof(struct berval), 2 ); - delmod->sml_values[1].bv_len = 0; - delmod->sml_values[1].bv_val = NULL; - ber_dupbv(&(delmod->sml_values[0]), &(pa->a_nvals[0])); + BER_BVZERO( &delmod->sml_values[1] ); + ber_dupbv( &(delmod->sml_values[0]), &(pa->a_nvals[0]) ); } } - bv = newpw.bv_val ? &newpw : addmod->sml_values; + bv = newpw.bv_val ? &newpw : &addmod->sml_values[0]; if (pp.pwdCheckQuality > 0) { rc = check_password_quality( bv, &pp, &pErr, e ); @@ -1746,62 +1843,48 @@ do_modify: timestamp.bv_len = sizeof(timebuf); slap_timestamp( &now, ×tamp ); - mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); - mods->sml_type.bv_val = NULL; + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_desc = ad_pwdChangedTime; if (pwmop != LDAP_MOD_DELETE) { mods->sml_op = LDAP_MOD_REPLACE; mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); - mods->sml_nvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); ber_dupbv( &mods->sml_values[0], ×tamp ); - ber_dupbv( &mods->sml_nvalues[0], ×tamp ); - mods->sml_values[1].bv_len = 0; - mods->sml_values[1].bv_val = NULL; - assert( mods->sml_values[0].bv_val != NULL ); + BER_BVZERO( &mods->sml_values[1] ); + assert( !BER_BVISNULL( &mods->sml_values[0] ) ); + } else { mods->sml_op = LDAP_MOD_DELETE; - mods->sml_values = NULL; } mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_nvalues = NULL; mods->sml_next = NULL; modtail->sml_next = mods; modtail = mods; if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) { - mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; - mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_type.bv_val = NULL; mods->sml_desc = ad_pwdGraceUseTime; - mods->sml_values = NULL; - mods->sml_nvalues = NULL; + mods->sml_flags = SLAP_MOD_INTERNAL; mods->sml_next = NULL; modtail->sml_next = mods; modtail = mods; } if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) { - mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; - mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_type.bv_val = NULL; mods->sml_desc = ad_pwdAccountLockedTime; - mods->sml_values = NULL; - mods->sml_nvalues = NULL; + mods->sml_flags = SLAP_MOD_INTERNAL; mods->sml_next = NULL; modtail->sml_next = mods; modtail = mods; } if (attr_find(e->e_attrs, ad_pwdFailureTime )) { - mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; - mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_type.bv_val = NULL; mods->sml_desc = ad_pwdFailureTime; - mods->sml_values = NULL; - mods->sml_nvalues = NULL; + mods->sml_flags = SLAP_MOD_INTERNAL; mods->sml_next = NULL; modtail->sml_next = mods; modtail = mods; @@ -1809,13 +1892,10 @@ do_modify: /* Delete the pwdReset attribute, since it's being reset */ if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) { - mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; - mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_type.bv_val = NULL; mods->sml_desc = ad_pwdReset; - mods->sml_values = NULL; - mods->sml_nvalues = NULL; + mods->sml_flags = SLAP_MOD_INTERNAL; mods->sml_next = NULL; modtail->sml_next = mods; modtail = mods; @@ -1839,19 +1919,15 @@ do_modify: * This is easily evaluated, since the linked list is * created in ascending time order. */ - mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_type.bv_val = NULL; mods->sml_desc = ad_pwdHistory; - mods->sml_nvalues = NULL; mods->sml_values = ch_calloc( sizeof( struct berval ), - hsize - pp.pwdInHistory + 2 ); - mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_val = NULL; - mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_len = 0; + hsize - pp.pwdInHistory + 2 ); + BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] ); for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) { - mods->sml_values[i].bv_val = NULL; - mods->sml_values[i].bv_len = 0; + BER_BVZERO( &mods->sml_values[i] ); ber_dupbv( &(mods->sml_values[i]), &p->bv ); } mods->sml_next = NULL; @@ -1885,6 +1961,7 @@ do_modify: mods->sml_next = NULL; modtail->sml_next = mods; modtail = mods; + } else { Debug( LDAP_DEBUG_TRACE, "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 ); @@ -1909,7 +1986,8 @@ do_modify: */ if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && - (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) { + (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) + { struct berval hpw, bv; slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt ); @@ -1921,13 +1999,11 @@ do_modify: rs->sr_text = txt; goto return_results; } - bv.bv_val = addmod->sml_values[0].bv_val; - bv.bv_len = addmod->sml_values[0].bv_len; + bv = addmod->sml_values[0]; /* clear and discard the clear password */ memset(bv.bv_val, 0, bv.bv_len); ber_memfree(bv.bv_val); - addmod->sml_values[0].bv_val = hpw.bv_val; - addmod->sml_values[0].bv_len = hpw.bv_len; + addmod->sml_values[0] = hpw; } } op->o_bd->bd_info = (BackendInfo *)on->on_info; @@ -1939,14 +2015,15 @@ return_results: op->o_bd->bd_info = (BackendInfo *)on->on_info; be_entry_release_r( op, e ); if ( send_ctrl ) { - LDAPControl **ctrls = NULL; + LDAPControl *ctrl = NULL; - ctrls = ch_calloc( sizeof( LDAPControl *) , 2 ); - ctrls[0] = create_passcontrol( -1, -1, pErr ); - ctrls[1] = NULL; - rs->sr_ctrls = ctrls; + ctrl = create_passcontrol( -1, -1, pErr ); + oldctrls = add_passcontrol( op, rs, ctrl ); } send_ldap_result( op, rs ); + if ( send_ctrl ) { + ctrls_cleanup( op, rs, oldctrls ); + } return rs->sr_err; } diff --git a/servers/slapd/passwd.c b/servers/slapd/passwd.c index ddd3b27ef2..ebb98e23c0 100644 --- a/servers/slapd/passwd.c +++ b/servers/slapd/passwd.c @@ -282,8 +282,13 @@ old_good: * if it cares... */ rs->sr_err = op->o_bd->be_modify( op, rs ); + + /* be_modify() might have shuffled modifications */ + qpw->rs_mods = op->orm_modlist; + if ( rs->sr_err == LDAP_SUCCESS ) { rs->sr_rspdata = rsp; + } else if ( rsp ) { ber_bvfree( rsp ); rsp = NULL; -- 2.39.5