📄 ppolicy.c
字号:
* get provided. By default, this is what we do * * But if the hash_passwords flag is set, we hash * any cleartext password attribute values via the * default password hashing scheme. */ if ((pi->hash_passwords) && (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) { struct berval hpw; slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt ); if (hpw.bv_val == NULL) { /* * hashing didn't work. Emit an error. */ rs->sr_err = LDAP_OTHER; rs->sr_text = txt; send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" ); return rs->sr_err; } memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len); ber_memfree( pa->a_vals[0].bv_val ); pa->a_vals[0].bv_val = hpw.bv_val; pa->a_vals[0].bv_len = hpw.bv_len; } /* If password aging is in effect, set the pwdChangedTime */ if ( pp.pwdMaxAge || pp.pwdMinAge ) { struct berval timestamp; char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; time_t now = slap_get_time(); timestamp.bv_val = timebuf; timestamp.bv_len = sizeof(timebuf); slap_timestamp( &now, ×tamp ); attr_merge_one( op->ora_e, ad_pwdChangedTime, ×tamp, ×tamp ); } } return SLAP_CB_CONTINUE;}static intppolicy_mod_cb( Operation *op, SlapReply *rs ){ slap_callback *sc = op->o_callback; op->o_callback = sc->sc_next; if ( rs->sr_err == LDAP_SUCCESS ) { ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); } op->o_tmpfree( sc, op->o_tmpmemctx ); return SLAP_CB_CONTINUE;}static intppolicy_modify( Operation *op, SlapReply *rs ){ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; pp_info *pi = on->on_bi.bi_private; int i, rc, mod_pw_only, pwmod, pwmop = -1, deladd, hsize = 0; PassPolicy pp; Modifications *mods = NULL, *modtail = NULL, *ml, *delmod, *addmod; Attribute *pa, *ha, at; const char *txt; pw_hist *tl = NULL, *p; int zapReset, send_ctrl = 0; Entry *e; 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 ); op->o_bd->bd_info = (BackendInfo *)on; if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE; /* If this is a replica, we may need to tweak some of the * master's modifications. Otherwise, just pass it through. */ if ( be_shadow_update( op )) { Modifications **prev; int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0; Attribute *a_grace, *a_lock, *a_fail; a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime ); a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime ); a_fail = attr_find( e->e_attrs, ad_pwdFailureTime ); for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) { if ( ml->sml_desc == slap_schema.si_ad_userPassword ) got_pw = 1; /* If we're deleting an attr that didn't exist, * drop this delete op */ if ( ml->sml_op == LDAP_MOD_DELETE ) { int drop = 0; if ( ml->sml_desc == ad_pwdGraceUseTime ) { got_del_grace = 1; if ( !a_grace ) drop = 1; } else if ( ml->sml_desc == ad_pwdAccountLockedTime ) { got_del_lock = 1; if ( !a_lock ) drop = 1; } else if ( ml->sml_desc == ad_pwdFailureTime ) { got_del_fail = 1; if ( !a_fail ) drop = 1; } if ( drop ) { *prev = ml->sml_next; ml->sml_next = NULL; slap_mods_free( ml, 1 ); continue; } } prev = &ml->sml_next; } /* If we're resetting the password, make sure grace, accountlock, * and failure also get removed. */ if ( got_pw ) { if ( a_grace && !got_del_grace ) { ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); ml->sml_op = LDAP_MOD_DELETE; ml->sml_flags = SLAP_MOD_INTERNAL; ml->sml_type.bv_val = NULL; ml->sml_desc = ad_pwdGraceUseTime; ml->sml_values = NULL; ml->sml_nvalues = NULL; ml->sml_next = NULL; *prev = ml; prev = &ml->sml_next; } if ( a_lock && !got_del_lock ) { ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); ml->sml_op = LDAP_MOD_DELETE; ml->sml_flags = SLAP_MOD_INTERNAL; ml->sml_type.bv_val = NULL; ml->sml_desc = ad_pwdAccountLockedTime; ml->sml_values = NULL; ml->sml_nvalues = NULL; ml->sml_next = NULL; *prev = ml; } if ( a_fail && !got_del_fail ) { ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); ml->sml_op = LDAP_MOD_DELETE; ml->sml_flags = SLAP_MOD_INTERNAL; ml->sml_type.bv_val = NULL; ml->sml_desc = ad_pwdFailureTime; ml->sml_values = NULL; ml->sml_nvalues = NULL; ml->sml_next = NULL; *prev = ml; } } op->o_bd->bd_info = (BackendInfo *)on->on_info; be_entry_release_r( op, e ); return SLAP_CB_CONTINUE; } /* Did we receive a password policy request control? */ if ( op->o_ctrlflag[ppolicy_cid] ) { send_ctrl = 1; } /* See if this is a pwdModify exop. If so, we can * access the plaintext passwords from that request. */ { slap_callback *sc; for ( sc = op->o_callback; sc; sc=sc->sc_next ) { if ( sc->sc_response == slap_replog_cb && sc->sc_private ) { req_pwdexop_s *qpw = sc->sc_private; newpw = qpw->rs_new; oldpw = qpw->rs_old; break; } } } ppolicy_get( op, e, &pp ); for ( ml = op->orm_modlist, pwmod = 0, mod_pw_only = 1, deladd = 0, delmod = NULL, addmod = NULL, zapReset = 1; ml != NULL; modtail = ml, ml = ml->sml_next ) { if ( ml->sml_desc == pp.ad ) { pwmod = 1; pwmop = ml->sml_op; if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) && (ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] )) { deladd = 1; delmod = ml; } if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) || (ml->sml_op == LDAP_MOD_REPLACE))) { deladd = 2; } if ((ml->sml_op == LDAP_MOD_ADD) || (ml->sml_op == LDAP_MOD_REPLACE)) { addmod = ml; /* FIXME: there's no easy way to ensure * that add does not cause multiple * userPassword values; one way (that * would be consistent with the single * password constraint) would be to turn * add into replace); another would be * to disallow add. * * Let's check at least that a single value * is being added */ assert( addmod->sml_values != NULL ); assert( !BER_BVISNULL( &addmod->sml_values[ 0 ] ) ); if ( !BER_BVISNULL( &addmod->sml_values[ 1 ] ) ) { rs->sr_err = LDAP_CONSTRAINT_VIOLATION; rs->sr_text = "Password policy only allows one password value"; goto return_results; } } } else if ( !is_at_operational( ml->sml_desc->ad_type ) ) { mod_pw_only = 0; /* modifying something other than password */ } /* * If there is a request to explicitly add a pwdReset * attribute, then we suppress the normal behaviour on * password change, which is to remove the pwdReset * attribute. * * This enables an administrator to assign a new password * and place a "must reset" flag on the entry, which will * stay until the user explicitly changes his/her password. */ if (ml->sml_desc == ad_pwdReset ) { if ((ml->sml_op == LDAP_MOD_ADD) || (ml->sml_op == LDAP_MOD_REPLACE)) zapReset = 0; } } if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) { if ( dn_match( &op->o_conn->c_ndn, &pwcons[op->o_conn->c_conn_idx].dn )) { Debug( LDAP_DEBUG_TRACE, "connection restricted to password changing only\n", 0, 0, 0 ); rs->sr_err = LDAP_INSUFFICIENT_ACCESS; rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password"; pErr = PP_changeAfterReset; goto return_results; } else { ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); } } /* * if we have a "safe password modify policy", then we need to check if we're doing * a delete (with the old password), followed by an add (with the new password). * * If we don't have this, then we fail with an error. We also skip all the checks if * the root user is bound. Root can do anything, including avoid the policies. */ if (!pwmod) goto do_modify; /* * Did we get a valid add mod? */ if (!addmod) { rs->sr_err = LDAP_OTHER; rs->sr_text = "Internal Error"; Debug( LDAP_DEBUG_TRACE, "cannot locate modification supplying new password\n", 0, 0, 0 ); goto return_results; } /* * Build the password history list in ascending time order * We need this, even if the user is root, in order to maintain * the pwdHistory operational attributes properly. */ if (pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) { struct berval oldpw; time_t oldtime; for(i=0; ha->a_nvals[i].bv_val; i++) { rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL, &oldtime, &oldpw ); if (rc != LDAP_SUCCESS) continue; /* invalid history entry */ if (oldpw.bv_val) { add_to_pwd_history( &tl, oldtime, &oldpw, &(ha->a_nvals[i]) ); oldpw.bv_val = NULL; oldpw.bv_len = 0; } } for(p=tl; p; p=p->next, hsize++); /* count history size */ } if (be_isroot( op )) goto do_modify; /* This is a pwdModify exop that provided the old pw. * We need to create a Delete mod for this old pw and * let the matching value get found later */ if (pp.pwdSafeModify && oldpw.bv_val ) { 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 ); BER_BVZERO( &ml->sml_values[1] ); ml->sml_next = op->orm_modlist; op->orm_modlist = ml; delmod = ml; deladd = 2; } if (pp.pwdSafeModify && deladd != 2) { Debug( LDAP_DEBUG_TRACE, "change password must use DELETE followed by ADD/REPLACE\n", 0, 0, 0 ); rs->sr_err = LDAP_INSUFFICIENT_ACCESS; rs->sr_text = "Must supply old password to be changed as well as new one"; pErr = PP_mustSupplyOldPassword; goto return_results; } if (!pp.pwdAllowUserChange) { rs->sr_err = LDAP_INSUFFICIENT_ACCESS; rs->sr_text = "User alteration of password is not allowed"; pErr = PP_passwordModNotAllowed; goto return_results; } if (pp.pwdMinAge > 0) { time_t pwtime = (time_t)-1, now; int age; if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL) pwtime = parse_time( pa->a_nvals[0].bv_val ); now = slap_get_time(); age = (int)(now - pwtime); if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) { rs->sr_err = LDAP_CONSTRAINT_VIOLATION; rs->sr_text = "Password is too young to change"; pErr = PP_passwordTooYoung; goto return_results; } } /* pa is used in password history check below, be sure it's set */ if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) { /* * we have a password to check */ const char *txt; bv = oldpw.bv_val ? &oldpw : delmod->sml_values; /* FIXME: no access checking? */ rc = slap_passwd_check( op, NULL, pa, bv, &txt ); if (rc != LDAP_SUCCESS) { Debug( LDAP_DEBUG_TRACE, "old password check failed: %s\n", txt, 0, 0 ); rs->sr_err = LDAP_UNWILLING_TO_PERFORM; rs->sr_text = "Must supply correct old password to change to new one"; pErr = PP_mustSupplyOldPassword; goto return_results; } else { int i; /* * replace the delete value with the (possibly hashed) * value which is currently in the password. */ 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 ); delmod->sml_values = ch_calloc( sizeof(struct berval), 2 ); BER_BVZERO( &delmod->sml_values[1] ); ber_dupbv( &(delmod->sml_values[0]), &(pa->a_nvals[0]) ); } } bv = newpw.bv_val ? &newpw : &addmod->sml_values[0]; if (pp.pwdCheckQuality > 0) { rc = check_password_quality( bv, &pp, &pErr, e ); if (rc != LDAP_SUCCESS) { rs->sr_err = rc; rs->sr_text = "Password fails quality checking policy"; goto return_results; } } if (pa) { /* * Last check - the password history. */ /* FIXME: no access checking? */ if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) { /* * This is bad - it means that the user is attempting * to set the password to the same as the old one. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -