X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Foverlays%2Fppolicy.c;h=e13c33a86629945f9d6e7f571356bfbbbcbf12f7;hb=10566c8be384c6435476ec54e842382841cb84b6;hp=f8b73356e617320141819e613266bb44b577f61c;hpb=fd0bdcb6c68c3eb28281f5ea4a5083be66822972;p=openldap diff --git a/servers/slapd/overlays/ppolicy.c b/servers/slapd/overlays/ppolicy.c index f8b73356e6..e13c33a866 100644 --- a/servers/slapd/overlays/ppolicy.c +++ b/servers/slapd/overlays/ppolicy.c @@ -1,7 +1,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2004-2014 The OpenLDAP Foundation. + * Copyright 2004-2017 The OpenLDAP Foundation. * Portions Copyright 2004-2005 Howard Chu, Symas Corporation. * Portions Copyright 2004 Hewlett-Packard Company. * All rights reserved. @@ -45,6 +45,10 @@ #define MODULE_NAME_SZ 256 #endif +#ifndef PPOLICY_DEFAULT_MAXRECORDED_FAILURE +#define PPOLICY_DEFAULT_MAXRECORDED_FAILURE 5 +#endif + /* Per-instance configuration information */ typedef struct pp_info { struct berval def_policy; /* DN of default policy subentry */ @@ -79,6 +83,7 @@ typedef struct pass_policy { int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */ int pwdLockoutDuration; /* time in seconds a password is locked out for */ int pwdMaxFailure; /* number of failed binds allowed before lockout */ + int pwdMaxRecordedFailure; /* number of failed binds to store */ int pwdFailureCountInterval; /* number of seconds before failure counts are zeroed */ int pwdMustChange; /* 0 = users can use admin set password @@ -179,7 +184,7 @@ static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdInHistory, *ad_pwdGraceAuthNLimit, *ad_pwdExpireWarning, *ad_pwdLockoutDuration, *ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdLockout, *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify, - *ad_pwdAttribute; + *ad_pwdAttribute, *ad_pwdMaxRecordedFailure; #define TAB(name) { #name, &ad_##name } @@ -191,6 +196,7 @@ static struct schema_info pwd_UsSchema[] = { TAB(pwdCheckQuality), TAB(pwdMinLength), TAB(pwdMaxFailure), + TAB(pwdMaxRecordedFailure), TAB(pwdGraceAuthNLimit), TAB(pwdExpireWarning), TAB(pwdLockout), @@ -283,7 +289,7 @@ ppolicy_cf_default( ConfigArgs *c ) rc = 0; break; case SLAP_CONFIG_ADD: - /* fallthrough to LDAP_MOD_ADD */ + /* fallthru 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 ) { @@ -382,6 +388,7 @@ create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyErr BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2; LDAPControl c = { 0 }, *cp; struct berval bv; + int rc; BER_BVZERO( &c.ldctl_value ); @@ -391,15 +398,23 @@ create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyErr if ( exptime >= 0 ) { ber_init2( b2, NULL, LBER_USE_DER ); ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime ); - ber_flatten2( b2, &bv, 1 ); + rc = ber_flatten2( b2, &bv, 1 ); (void)ber_free_buf(b2); + if (rc == -1) { + cp = NULL; + goto fail; + } ber_printf( ber, "tO", PPOLICY_WARNING, &bv ); ch_free( bv.bv_val ); } else if ( grace > 0 ) { ber_init2( b2, NULL, LBER_USE_DER ); ber_printf( b2, "ti", PPOLICY_GRACE, grace ); - ber_flatten2( b2, &bv, 1 ); + rc = ber_flatten2( b2, &bv, 1 ); (void)ber_free_buf(b2); + if (rc == -1) { + cp = NULL; + goto fail; + } ber_printf( ber, "tO", PPOLICY_WARNING, &bv ); ch_free( bv.bv_val ); } @@ -418,6 +433,7 @@ create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyErr cp->ldctl_value.bv_val = (char *)&cp[1]; cp->ldctl_value.bv_len = c.ldctl_value.bv_len; AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len ); +fail: (void)ber_free_buf(ber); return cp; @@ -452,6 +468,20 @@ add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl ) return oldctrls; } +static void +ppolicy_get_default( PassPolicy *pp ) +{ + memset( pp, 0, sizeof(PassPolicy) ); + + pp->ad = slap_schema.si_ad_userPassword; + + /* Users can change their own password by default */ + pp->pwdAllowUserChange = 1; + if ( !pp->pwdMaxRecordedFailure ) + pp->pwdMaxRecordedFailure = PPOLICY_DEFAULT_MAXRECORDED_FAILURE; +} + + static void ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) { @@ -465,12 +495,7 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) const char *text; #endif - memset( pp, 0, sizeof(PassPolicy) ); - - pp->ad = slap_schema.si_ad_userPassword; - - /* Users can change their own password by default */ - pp->pwdAllowUserChange = 1; + ppolicy_get_default( pp ); if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) { /* @@ -517,6 +542,9 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxFailure ) ) && lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 ) goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxRecordedFailure ) ) + && lutil_atoi( &pp->pwdMaxRecordedFailure, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; if ( ( a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit ) ) && lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 ) goto defaultpol; @@ -545,6 +573,11 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify ))) pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv ); + if ( pp->pwdMaxRecordedFailure < pp->pwdMaxFailure ) + pp->pwdMaxRecordedFailure = pp->pwdMaxFailure; + if ( !pp->pwdMaxRecordedFailure ) + pp->pwdMaxRecordedFailure = PPOLICY_DEFAULT_MAXRECORDED_FAILURE; + op->o_bd->bd_info = (BackendInfo *)on->on_info; be_entry_release_r( op, pe ); op->o_bd->bd_info = (BackendInfo *)on; @@ -552,8 +585,17 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) return; defaultpol: + if ( pe ) { + op->o_bd->bd_info = (BackendInfo *)on->on_info; + be_entry_release_r( op, pe ); + op->o_bd->bd_info = (BackendInfo *)on; + } + Debug( LDAP_DEBUG_TRACE, "ppolicy_get: using default policy\n", 0, 0, 0 ); + + ppolicy_get_default( pp ); + return; } @@ -989,6 +1031,50 @@ ppolicy_bind_response( Operation *op, SlapReply *rs ) * which are not due to expire. */ } + /* Do we have too many timestamps? If so, delete some values. + * We don't bother to sort the values here. OpenLDAP keeps the + * values in order by default. Fundamentally, relying on the + * information here is wrong anyway; monitoring systems should + * be tracking Bind failures in syslog, not here. + */ + if (a->a_numvals >= ppb->pp.pwdMaxRecordedFailure) { + int j = ppb->pp.pwdMaxRecordedFailure-1; + /* If more than 2x, cheaper to perform a Replace */ + if (a->a_numvals >= 2 * ppb->pp.pwdMaxRecordedFailure) { + struct berval v, nv; + + /* Change the mod we constructed above */ + m->sml_op = LDAP_MOD_REPLACE; + m->sml_numvals = ppb->pp.pwdMaxRecordedFailure; + v = m->sml_values[0]; + nv = m->sml_nvalues[0]; + ch_free(m->sml_values); + ch_free(m->sml_nvalues); + m->sml_values = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 ); + for (i=0; isml_values[i], &a->a_vals[a->a_numvals-j+i]); + ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[a->a_numvals-j+i]); + } + m->sml_values[i] = v; + m->sml_nvalues[i] = nv; + } else { + /* else just delete some */ + m = ch_calloc( sizeof(Modifications), 1 ); + m->sml_op = LDAP_MOD_DELETE; + m->sml_type = ad_pwdFailureTime->ad_cname; + m->sml_desc = ad_pwdFailureTime; + m->sml_numvals = a->a_numvals - j; + m->sml_values = ch_calloc( sizeof(struct berval), m->sml_numvals+1 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), m->sml_numvals+1 ); + for (i=0; isml_numvals; i++) { + ber_dupbv(&m->sml_values[i], &a->a_vals[i]); + ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[i]); + } + m->sml_next = mod; + mod = m; + } + } } if ((ppb->pp.pwdMaxFailure > 0) && @@ -1273,7 +1359,7 @@ ppolicy_bind( Operation *op, SlapReply *rs ) static int ppolicy_connection_destroy( BackendDB *bd, Connection *conn ) { - if ( !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) { + if ( pwcons && !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) { ch_free( pwcons[conn->c_conn_idx].dn.bv_val ); BER_BVZERO( &pwcons[conn->c_conn_idx].dn ); } @@ -1555,6 +1641,8 @@ ppolicy_modify( Operation *op, SlapReply *rs ) LDAPControl *ctrl = NULL; LDAPControl **oldctrls = NULL; int is_pwdexop = 0; + int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0; + int got_changed = 0, got_history = 0; op->o_bd->bd_info = (BackendInfo *)on->on_info; rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); @@ -1567,7 +1655,6 @@ ppolicy_modify( Operation *op, SlapReply *rs ) */ 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 ); @@ -1586,19 +1673,25 @@ ppolicy_modify( Operation *op, SlapReply *rs ) int drop = 0; if ( ml->sml_desc == ad_pwdGraceUseTime ) { - got_del_grace = 1; - if ( !a_grace ) + if ( !a_grace || got_del_grace ) { drop = 1; + } else { + got_del_grace = 1; + } } else if ( ml->sml_desc == ad_pwdAccountLockedTime ) { - got_del_lock = 1; - if ( !a_lock ) + if ( !a_lock || got_del_lock ) { drop = 1; + } else { + got_del_lock = 1; + } } else if ( ml->sml_desc == ad_pwdFailureTime ) { - got_del_fail = 1; - if ( !a_fail ) + if ( !a_fail || got_del_fail ) { drop = 1; + } else { + got_del_fail = 1; + } } if ( drop ) { *prev = ml->sml_next; @@ -1750,6 +1843,20 @@ ppolicy_modify( Operation *op, SlapReply *rs ) (ml->sml_op == LDAP_MOD_REPLACE)) zapReset = 0; } + if ( ml->sml_op == LDAP_MOD_DELETE ) { + if ( ml->sml_desc == ad_pwdGraceUseTime ) { + got_del_grace = 1; + } else if ( ml->sml_desc == ad_pwdAccountLockedTime ) { + got_del_lock = 1; + } else if ( ml->sml_desc == ad_pwdFailureTime ) { + got_del_fail = 1; + } + } + if ( ml->sml_desc == ad_pwdChangedTime ) { + got_changed = 1; + } else if (ml->sml_desc == ad_pwdHistory ) { + got_history = 1; + } } if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) { @@ -1986,32 +2093,34 @@ do_modify: * up to date. */ - timestamp.bv_val = timebuf; - timestamp.bv_len = sizeof(timebuf); - slap_timestamp( &now, ×tamp ); + if (!got_changed) { + timestamp.bv_val = timebuf; + timestamp.bv_len = sizeof(timebuf); + slap_timestamp( &now, ×tamp ); - mods = NULL; - if (pwmop != LDAP_MOD_DELETE) { - mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); - mods->sml_op = LDAP_MOD_REPLACE; - mods->sml_numvals = 1; - mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); - ber_dupbv( &mods->sml_values[0], ×tamp ); - BER_BVZERO( &mods->sml_values[1] ); - assert( !BER_BVISNULL( &mods->sml_values[0] ) ); - } else if (attr_find(e->e_attrs, ad_pwdChangedTime )) { - mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); - mods->sml_op = LDAP_MOD_DELETE; - } - if (mods) { - mods->sml_desc = ad_pwdChangedTime; - mods->sml_flags = SLAP_MOD_INTERNAL; - mods->sml_next = NULL; - modtail->sml_next = mods; - modtail = mods; + mods = NULL; + if (pwmop != LDAP_MOD_DELETE) { + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); + mods->sml_op = LDAP_MOD_REPLACE; + mods->sml_numvals = 1; + mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); + ber_dupbv( &mods->sml_values[0], ×tamp ); + BER_BVZERO( &mods->sml_values[1] ); + assert( !BER_BVISNULL( &mods->sml_values[0] ) ); + } else if (attr_find(e->e_attrs, ad_pwdChangedTime )) { + mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); + mods->sml_op = LDAP_MOD_DELETE; + } + if (mods) { + mods->sml_desc = ad_pwdChangedTime; + mods->sml_flags = SLAP_MOD_INTERNAL; + mods->sml_next = NULL; + modtail->sml_next = mods; + modtail = mods; + } } - if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) { + if (!got_del_grace && attr_find(e->e_attrs, ad_pwdGraceUseTime )) { mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; mods->sml_desc = ad_pwdGraceUseTime; @@ -2021,7 +2130,7 @@ do_modify: modtail = mods; } - if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) { + if (!got_del_lock && attr_find(e->e_attrs, ad_pwdAccountLockedTime )) { mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; mods->sml_desc = ad_pwdAccountLockedTime; @@ -2031,7 +2140,7 @@ do_modify: modtail = mods; } - if (attr_find(e->e_attrs, ad_pwdFailureTime )) { + if (!got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) { mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 ); mods->sml_op = LDAP_MOD_DELETE; mods->sml_desc = ad_pwdFailureTime; @@ -2052,7 +2161,7 @@ do_modify: modtail = mods; } - if (pp.pwdInHistory > 0) { + if (!got_history && pp.pwdInHistory > 0) { if (hsize >= pp.pwdInHistory) { /* * We use the >= operator, since we are going to add @@ -2305,6 +2414,8 @@ ppolicy_db_init( pwcons++; } + ov_count++; + return 0; } @@ -2314,33 +2425,42 @@ ppolicy_db_open( ConfigReply *cr ) { - ov_count++; return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST ); } static int -ppolicy_close( +ppolicy_db_close( BackendDB *be, ConfigReply *cr ) { - slap_overinst *on = (slap_overinst *) be->bd_info; - pp_info *pi = on->on_bi.bi_private; - #ifdef SLAP_CONFIG_DELETE overlay_unregister_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST ); #endif /* SLAP_CONFIG_DELETE */ - /* Perhaps backover should provide bi_destroy hooks... */ + return 0; +} + +static int +ppolicy_db_destroy( + BackendDB *be, + ConfigReply *cr +) +{ + slap_overinst *on = (slap_overinst *) be->bd_info; + pp_info *pi = on->on_bi.bi_private; + + on->on_bi.bi_private = NULL; + free( pi->def_policy.bv_val ); + free( pi ); + ov_count--; if ( ov_count <=0 && pwcons ) { - pwcons--; - free( pwcons ); + pw_conn *pwc = pwcons; pwcons = NULL; + pwc--; + ch_free( pwc ); } - free( pi->def_policy.bv_val ); - free( pi ); - return 0; } @@ -2382,7 +2502,8 @@ int ppolicy_initialize() ppolicy.on_bi.bi_type = "ppolicy"; ppolicy.on_bi.bi_db_init = ppolicy_db_init; ppolicy.on_bi.bi_db_open = ppolicy_db_open; - ppolicy.on_bi.bi_db_close = ppolicy_close; + ppolicy.on_bi.bi_db_close = ppolicy_db_close; + ppolicy.on_bi.bi_db_destroy = ppolicy_db_destroy; ppolicy.on_bi.bi_op_add = ppolicy_add; ppolicy.on_bi.bi_op_bind = ppolicy_bind;