From: Ondřej Kuzník Date: Tue, 15 Apr 2025 16:45:44 +0000 (+0100) Subject: ITS#10254 Let slapo-ppolicy rehash user's password X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=dcd60d1d0ad236742e2a653c1f7acb1437955ba0;p=thirdparty%2Fopenldap.git ITS#10254 Let slapo-ppolicy rehash user's password --- diff --git a/servers/slapd/overlays/ppolicy.c b/servers/slapd/overlays/ppolicy.c index 76bddd007d..95c651c58f 100644 --- a/servers/slapd/overlays/ppolicy.c +++ b/servers/slapd/overlays/ppolicy.c @@ -171,6 +171,9 @@ typedef struct pass_policy { struct berval pwdCheckModuleArg; /* Optional argument to the password check module */ struct berval pwdDefaultHash; /* A per-policy default password hash */ + int pwdRehashOnBind; /* 1 = if the current password doesn't have the same + hash as our default, update the stored hash on a + successful simple bind */ } PassPolicy; typedef struct pw_hist { @@ -194,7 +197,8 @@ static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdMaxIdle, *ad_pwdLockoutDuration, *ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdCheckModuleArg, *ad_pwdUseCheckModule, *ad_pwdLockout, *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify, - *ad_pwdAttribute, *ad_pwdMaxRecordedFailure, *ad_pwdDefaultHash; + *ad_pwdAttribute, *ad_pwdMaxRecordedFailure, *ad_pwdDefaultHash, + *ad_pwdRehashOnBind; /* Policy objectclasses */ static ObjectClass *oc_pwdPolicyChecker, *oc_pwdPolicy, *oc_pwdHashingPolicy; @@ -475,6 +479,14 @@ static struct schema_info { "DESC 'Per policy default hash setting' " "SINGLE-VALUE )", &ad_pwdDefaultHash }, + { "( 1.3.6.1.4.1.4754.1.99.5 " + "NAME ( 'pwdRehashOnBind' ) " + "EQUALITY booleanMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 " + "DESC 'On successful Simple Bind, rehash password " + "with default hash if different' " + "SINGLE-VALUE )", + &ad_pwdRehashOnBind }, { NULL, NULL } }; @@ -511,7 +523,7 @@ static struct oc_info { "NAME 'pwdHashingPolicy' " "SUP pwdPolicy " "AUXILIARY " - "MAY pwdDefaultHash )", + "MAY ( pwdDefaultHash $ pwdRehashOnBind ) )", &oc_pwdHashingPolicy, }, NULL @@ -2444,6 +2456,10 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) a->a_vals[0].bv_val, pe->e_name.bv_val ); } } + + ad = ad_pwdRehashOnBind; + if ( (a = attr_find( pe->e_attrs, ad )) ) + pp->pwdRehashOnBind = bvmatch( &a->a_nvals[0], &slap_true_bv ); } ad = ad_pwdLockout; @@ -2475,6 +2491,12 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) pp->pwdMaxDelay = pp->pwdMinDelay; } + if ( pp->pwdRehashOnBind && BER_BVISNULL( &pp->pwdDefaultHash ) ) { + Debug( LDAP_DEBUG_ANY, "ppolicy_get: " + "pwdRehashOnBind is set but pwdDefaultHash not set.\n" ); + pp->pwdRehashOnBind = 0; + } + op->o_bd = bd; be_entry_release_r( op, pe ); op->o_bd = bd_orig; @@ -2848,6 +2870,7 @@ ppolicy_bind_response( Operation *op, SlapReply *rs ) char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ]; char nowstr_usec[ LDAP_LUTIL_GENTIME_BUFSIZE+8 ]; struct berval timestamp, timestamp_usec; + struct berval oldpw = BER_BVNULL, scheme = BER_BVNULL; BackendDB *be = op->o_bd; LDAPControl *ctrl = NULL; Entry *e; @@ -2867,10 +2890,13 @@ ppolicy_bind_response( Operation *op, SlapReply *rs ) } /* ITS#7089 Skip lockout checks/modifications if password attribute missing */ - if ( attr_find( e->e_attrs, ppb->pp.ad ) == NULL ) { + if ( (a = attr_find( e->e_attrs, ppb->pp.ad )) == NULL ) { goto done; } + oldpw = a->a_vals[0]; + password_scheme( &oldpw, &scheme ); + ldap_pvt_gettime(&now_tm); /* stored for later consideration */ lutil_tm2time(&now_tm, &now_usec); now = now_usec.tt_sec; @@ -3056,6 +3082,58 @@ ppolicy_bind_response( Operation *op, SlapReply *rs ) ppb->pErr = PP_changeAfterReset; } else { + /* + * Check if we're expected to rewrite the stored hash + */ + if ( ppb->pp.pwdRehashOnBind && op->o_tag == LDAP_REQ_BIND && + op->orb_method == LDAP_AUTH_SIMPLE && + !BER_BVISNULL( &ppb->pp.pwdDefaultHash ) && + ber_bvstrcasecmp( &scheme, &ppb->pp.pwdDefaultHash ) ) { + struct berval newpw = BER_BVNULL, newhash = BER_BVNULL; + + if ( op->o_tag == LDAP_REQ_COMPARE ) { + newpw = op->orc_ava->aa_value; + } else if ( op->o_tag == LDAP_REQ_BIND && + op->orb_method == LDAP_AUTH_SIMPLE ) { + newpw = op->orb_cred; + } + + if ( !BER_BVISNULL( &newpw ) ) { + const char *txt; + slap_passwd_hash_type( &newpw, &newhash, + ppb->pp.pwdDefaultHash.bv_val, &txt ); + if ( BER_BVISNULL( &newhash ) ) { + Debug( LDAP_DEBUG_ANY, "ppolicy_bind_response: " + "rehashing password for user %s failed: %s\n", + op->o_req_dn.bv_val, txt ); + } + } + + if ( !BER_BVISNULL( &newhash ) ) { + m = ch_calloc( sizeof(Modifications), 1 ); + m->sml_op = LDAP_MOD_ADD; + m->sml_flags = SLAP_MOD_INTERNAL; + m->sml_type = ppb->pp.ad->ad_cname; + m->sml_desc = ppb->pp.ad; + m->sml_next = mod; + m->sml_numvals = 1; + m->sml_values = ch_calloc( sizeof(struct berval), 2 ); + m->sml_values[0] = newhash; + + /* Before we add new, delete old value */ + mod = m; + m = (Modifications *)ch_calloc( sizeof( Modifications ), 1 ); + m->sml_op = LDAP_MOD_DELETE; + m->sml_flags = SLAP_MOD_INTERNAL; + m->sml_desc = ppb->pp.ad; + m->sml_type = ppb->pp.ad->ad_cname; + m->sml_numvals = 1; + m->sml_values = ch_calloc( sizeof( struct berval ), 2 ); + ber_dupbv( &m->sml_values[0], &oldpw ); + mod = m; + } + } + /* * the password does not need to be changed, so * we now check whether the password has expired.