mirror of
https://git.openldap.org/openldap/openldap.git
synced 2026-01-21 14:25:24 -05:00
ITS#9156 Implement pwdMaxIdle
This commit is contained in:
parent
8c10b0481a
commit
419b9ad202
7 changed files with 222 additions and 4 deletions
|
|
@ -1429,6 +1429,12 @@ createTimestamp attributes for entries. It also controls
|
|||
the entryCSN and entryUUID attributes, which are needed
|
||||
by the syncrepl provider. By default, olcLastMod is TRUE.
|
||||
.TP
|
||||
.B olcLastBind: TRUE | FALSE
|
||||
Controls whether
|
||||
.B slapd
|
||||
will automatically maintain the pwdLastSuccess attribute for
|
||||
entries. By default, olcLastBind is FALSE.
|
||||
.TP
|
||||
.B olcLimits: <selector> <limit> [<limit> [...]]
|
||||
Specify time and size limits based on the operation's initiator or
|
||||
base DN.
|
||||
|
|
|
|||
|
|
@ -1364,6 +1364,12 @@ createTimestamp attributes for entries. It also controls
|
|||
the entryCSN and entryUUID attributes, which are needed
|
||||
by the syncrepl provider. By default, lastmod is on.
|
||||
.TP
|
||||
.B lastbind on | off
|
||||
Controls whether
|
||||
.B slapd
|
||||
will automatically maintain the pwdLastSuccess attribute for
|
||||
entries. By default, lastbind is off.
|
||||
.TP
|
||||
.B limits <selector> <limit> [<limit> [...]]
|
||||
Specify time and size limits based on the operation's initiator or
|
||||
base DN.
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ enum {
|
|||
CFG_MODLOAD,
|
||||
CFG_MODPATH,
|
||||
CFG_LASTMOD,
|
||||
CFG_LASTBIND,
|
||||
CFG_AZPOLICY,
|
||||
CFG_AZREGEXP,
|
||||
CFG_AZDUC,
|
||||
|
|
@ -442,6 +443,10 @@ static ConfigTable config_back_cf_table[] = {
|
|||
&config_generic, "( OLcfgDbAt:0.4 NAME 'olcLastMod' "
|
||||
"EQUALITY booleanMatch "
|
||||
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
|
||||
{ "lastbind", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_LASTBIND,
|
||||
&config_generic, "( OLcfgDbAt:0.22 NAME 'olcLastBind' "
|
||||
"EQUALITY booleanMatch "
|
||||
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
|
||||
{ "ldapsyntax", "syntax", 2, 0, 0,
|
||||
ARG_PAREN|ARG_MAGIC|CFG_SYNTAX,
|
||||
&config_generic, "( OLcfgGlAt:85 NAME 'olcLdapSyntaxes' "
|
||||
|
|
@ -981,7 +986,7 @@ static ConfigOCs cf_ocs[] = {
|
|||
"SUP olcConfig STRUCTURAL "
|
||||
"MUST olcDatabase "
|
||||
"MAY ( olcDisabled $ olcHidden $ olcSuffix $ olcSubordinate $ olcAccess $ "
|
||||
"olcAddContentAcl $ olcLastMod $ olcLimits $ "
|
||||
"olcAddContentAcl $ olcLastMod $ olcLastBind $ olcLimits $ "
|
||||
"olcMaxDerefDepth $ olcPlugin $ olcReadOnly $ olcReplica $ "
|
||||
"olcReplicaArgsFile $ olcReplicaPidFile $ olcReplicationInterval $ "
|
||||
"olcReplogFile $ olcRequires $ olcRestrict $ olcRootDN $ olcRootPW $ "
|
||||
|
|
@ -1321,6 +1326,9 @@ config_generic(ConfigArgs *c) {
|
|||
case CFG_LASTMOD:
|
||||
c->value_int = (SLAP_NOLASTMOD(c->be) == 0);
|
||||
break;
|
||||
case CFG_LASTBIND:
|
||||
c->value_int = (SLAP_NOLASTMOD(c->be) == 0);
|
||||
break;
|
||||
case CFG_SYNC_SUBENTRY:
|
||||
c->value_int = (SLAP_SYNC_SUBENTRY(c->be) != 0);
|
||||
break;
|
||||
|
|
@ -1435,6 +1443,7 @@ config_generic(ConfigArgs *c) {
|
|||
case CFG_AZPOLICY:
|
||||
case CFG_DEPTH:
|
||||
case CFG_LASTMOD:
|
||||
case CFG_LASTBIND:
|
||||
case CFG_MONITORING:
|
||||
case CFG_SASLSECP:
|
||||
case CFG_SSTR_IF_MAX:
|
||||
|
|
@ -2276,6 +2285,13 @@ sortval_reject:
|
|||
SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_NOLASTMOD;
|
||||
break;
|
||||
|
||||
case CFG_LASTBIND:
|
||||
if (c->value_int)
|
||||
SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_LASTBIND;
|
||||
else
|
||||
SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_LASTBIND;
|
||||
break;
|
||||
|
||||
case CFG_MIRRORMODE:
|
||||
if(c->value_int && !SLAP_SHADOW(c->be)) {
|
||||
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> database is not a shadow",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <ac/string.h>
|
||||
#include <ac/socket.h>
|
||||
|
||||
#include "lutil.h"
|
||||
#include "slap.h"
|
||||
|
||||
int
|
||||
|
|
@ -399,6 +400,113 @@ cleanup:;
|
|||
return rs->sr_err;
|
||||
}
|
||||
|
||||
int
|
||||
fe_op_lastbind( Operation *op )
|
||||
{
|
||||
Operation op2 = *op;
|
||||
SlapReply r2 = { REP_RESULT };
|
||||
slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
|
||||
LDAPControl c, *ca[2];
|
||||
Modifications *m;
|
||||
Entry *e;
|
||||
Attribute *a;
|
||||
char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
|
||||
struct berval timestamp;
|
||||
time_t bindtime = (time_t)-1;
|
||||
int rc;
|
||||
|
||||
rc = be_entry_get_rw( op, &op->o_conn->c_ndn, NULL, NULL, 0, &e );
|
||||
if ( rc != LDAP_SUCCESS ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get authTimestamp attribute, if it exists */
|
||||
if ( (a = attr_find( e->e_attrs, slap_schema.si_ad_pwdLastSuccess )) != NULL ) {
|
||||
struct lutil_tm tm;
|
||||
struct lutil_timet tt;
|
||||
|
||||
if ( lutil_parsetime( a->a_nvals[0].bv_val, &tm ) == 0 ) {
|
||||
lutil_tm2time( &tm, &tt );
|
||||
bindtime = tt.tt_sec;
|
||||
}
|
||||
Debug( LDAP_DEBUG_ANY, "fe_op_lastbind: "
|
||||
"old pwdLastSuccess value=%s %lds ago\n",
|
||||
a->a_nvals[0].bv_val, bindtime == (time_t)-1 ? -1 : op->o_time - bindtime );
|
||||
|
||||
/*
|
||||
* TODO: If the recorded bind time is within configurable precision,
|
||||
* it doesn't need to be updated (save a write for nothing)
|
||||
*/
|
||||
if ( bindtime != (time_t)-1 && op->o_time <= bindtime ) {
|
||||
be_entry_release_r( op, e );
|
||||
return LDAP_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
/* update the authTimestamp in the user's entry with the current time */
|
||||
timestamp.bv_val = nowstr;
|
||||
timestamp.bv_len = sizeof(nowstr);
|
||||
slap_timestamp( &op->o_time, ×tamp );
|
||||
|
||||
m = ch_calloc( sizeof(Modifications), 1 );
|
||||
m->sml_op = LDAP_MOD_REPLACE;
|
||||
m->sml_flags = 0;
|
||||
m->sml_type = slap_schema.si_ad_pwdLastSuccess->ad_cname;
|
||||
m->sml_desc = slap_schema.si_ad_pwdLastSuccess;
|
||||
m->sml_numvals = 1;
|
||||
m->sml_values = ch_calloc( sizeof(struct berval), 2 );
|
||||
m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
|
||||
|
||||
ber_dupbv( &m->sml_values[0], ×tamp );
|
||||
ber_dupbv( &m->sml_nvalues[0], ×tamp );
|
||||
|
||||
be_entry_release_r( op, e );
|
||||
|
||||
op2.o_tag = LDAP_REQ_MODIFY;
|
||||
op2.o_req_dn = op->o_conn->c_dn;
|
||||
op2.o_req_ndn = op->o_conn->c_ndn;
|
||||
op2.o_callback = &cb;
|
||||
op2.orm_modlist = m;
|
||||
op2.orm_no_opattrs = 0;
|
||||
op2.o_dn = op->o_bd->be_rootdn;
|
||||
op2.o_ndn = op->o_bd->be_rootndn;
|
||||
|
||||
/*
|
||||
* TODO: this is core+frontend, not everything works the same way?
|
||||
*/
|
||||
/*
|
||||
* Code for forwarding of updates adapted from ppolicy.c of slapo-ppolicy
|
||||
*
|
||||
* If this server is a shadow and forward_updates is true,
|
||||
* use the frontend to perform this modify. That will trigger
|
||||
* the update referral, which can then be forwarded by the
|
||||
* chain overlay. Obviously the updateref and chain overlay
|
||||
* must be configured appropriately for this to be useful.
|
||||
*/
|
||||
if ( SLAP_SHADOW( op->o_bd ) ) {
|
||||
/* Must use Relax control since these are no-user-mod */
|
||||
op2.o_relax = SLAP_CONTROL_CRITICAL;
|
||||
op2.o_ctrls = ca;
|
||||
ca[0] = &c;
|
||||
ca[1] = NULL;
|
||||
BER_BVZERO( &c.ldctl_value );
|
||||
c.ldctl_iscritical = 1;
|
||||
c.ldctl_oid = LDAP_CONTROL_RELAX;
|
||||
} else {
|
||||
/* If not forwarding, don't update opattrs and don't replicate */
|
||||
if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
|
||||
op2.orm_no_opattrs = 1;
|
||||
op2.o_dont_replicate = 1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = op->o_bd->be_modify( &op2, &r2 );
|
||||
slap_mods_free( m, 1 );
|
||||
|
||||
done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
fe_op_bind_success( Operation *op, SlapReply *rs )
|
||||
{
|
||||
|
|
@ -436,6 +544,10 @@ fe_op_bind_success( Operation *op, SlapReply *rs )
|
|||
|
||||
ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
|
||||
|
||||
if ( SLAP_LASTBIND( op->o_bd ) ) {
|
||||
fe_op_lastbind( op );
|
||||
}
|
||||
|
||||
/* send this here to avoid a race condition */
|
||||
send_ldap_result( op, rs );
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ typedef struct pass_policy {
|
|||
AttributeDescription *ad; /* attribute to which the policy applies */
|
||||
int pwdMinAge; /* minimum time (seconds) until passwd can change */
|
||||
int pwdMaxAge; /* time in seconds until pwd will expire after change */
|
||||
int pwdMaxIdle; /* number of seconds since last successful bind before
|
||||
passwd gets locked out */
|
||||
int pwdInHistory; /* number of previous passwords kept */
|
||||
int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
|
||||
2 = check mandatory; fail if not possible */
|
||||
|
|
@ -207,6 +209,7 @@ static struct schema_info {
|
|||
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
|
||||
"SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
|
||||
&ad_pwdEndTime },
|
||||
/* Defined in schema_prep.c now
|
||||
{ "( 1.3.6.1.4.1.42.2.27.8.1.29 "
|
||||
"NAME ( 'pwdLastSuccess' ) "
|
||||
"DESC 'The timestamp of the last successful authentication' "
|
||||
|
|
@ -215,6 +218,7 @@ static struct schema_info {
|
|||
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
|
||||
"SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
|
||||
&ad_pwdLastSuccess },
|
||||
*/
|
||||
{ "( 1.3.6.1.4.1.42.2.27.8.1.33 "
|
||||
"NAME ( 'pwdAccountTmpLockoutEnd' ) "
|
||||
"DESC 'Temporary lockout end' "
|
||||
|
|
@ -576,6 +580,24 @@ account_locked( Operation *op, Entry *e,
|
|||
}
|
||||
}
|
||||
|
||||
/* Only check if database maintains lastbind */
|
||||
if ( pp->pwdMaxIdle && SLAP_LASTBIND( op->o_bd ) ) {
|
||||
time_t lastbindtime = (time_t)-1;
|
||||
|
||||
la = attr_find( e->e_attrs, ad_pwdLastSuccess );
|
||||
if ( la == NULL ) {
|
||||
la = attr_find( e->e_attrs, ad_pwdChangedTime );
|
||||
}
|
||||
if ( la != NULL ) {
|
||||
lastbindtime = parse_time( la->a_nvals[0].bv_val );
|
||||
}
|
||||
|
||||
if ( lastbindtime != (time_t)-1 &&
|
||||
op->o_time > lastbindtime + pp->pwdMaxIdle ) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
|
||||
BerVarray vals = la->a_nvals;
|
||||
|
||||
|
|
@ -773,6 +795,9 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
|
|||
if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxAge ) )
|
||||
&& lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 )
|
||||
goto defaultpol;
|
||||
if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxIdle ) )
|
||||
&& lutil_atoi( &pp->pwdMaxIdle, a->a_vals[0].bv_val ) != 0 )
|
||||
goto defaultpol;
|
||||
if ( ( a = attr_find( pe->e_attrs, ad_pwdInHistory ) )
|
||||
&& lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 )
|
||||
goto defaultpol;
|
||||
|
|
@ -1935,7 +1960,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_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;
|
||||
|
||||
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
||||
|
|
@ -1949,11 +1975,12 @@ ppolicy_modify( Operation *op, SlapReply *rs )
|
|||
*/
|
||||
if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) ) {
|
||||
Modifications **prev;
|
||||
Attribute *a_grace, *a_lock, *a_fail;
|
||||
Attribute *a_grace, *a_lock, *a_fail, *a_success;
|
||||
|
||||
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 );
|
||||
a_success = attr_find( e->e_attrs, ad_pwdLastSuccess );
|
||||
|
||||
for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) {
|
||||
|
||||
|
|
@ -1988,6 +2015,13 @@ ppolicy_modify( Operation *op, SlapReply *rs )
|
|||
got_del_fail = 1;
|
||||
}
|
||||
}
|
||||
if ( ml->sml_desc == ad_pwdLastSuccess ) {
|
||||
if ( !a_success || got_del_success ) {
|
||||
drop = ml->sml_op == LDAP_MOD_DELETE;
|
||||
} else {
|
||||
got_del_success = 1;
|
||||
}
|
||||
}
|
||||
if ( drop ) {
|
||||
*prev = ml->sml_next;
|
||||
ml->sml_next = NULL;
|
||||
|
|
@ -1999,7 +2033,7 @@ ppolicy_modify( Operation *op, SlapReply *rs )
|
|||
}
|
||||
|
||||
/* If we're resetting the password, make sure grace, accountlock,
|
||||
* and failure also get removed.
|
||||
* success, and failure also get removed.
|
||||
*/
|
||||
if ( got_pw ) {
|
||||
if ( a_grace && !got_del_grace ) {
|
||||
|
|
@ -2039,6 +2073,18 @@ ppolicy_modify( Operation *op, SlapReply *rs )
|
|||
ml->sml_next = NULL;
|
||||
*prev = ml;
|
||||
}
|
||||
if ( a_success && !got_del_success ) {
|
||||
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_pwdLastSuccess;
|
||||
ml->sml_numvals = 0;
|
||||
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 );
|
||||
|
|
@ -2145,6 +2191,8 @@ ppolicy_modify( Operation *op, SlapReply *rs )
|
|||
got_del_lock = 1;
|
||||
} else if ( ml->sml_desc == ad_pwdFailureTime ) {
|
||||
got_del_fail = 1;
|
||||
} else if ( ml->sml_desc == ad_pwdLastSuccess ) {
|
||||
got_del_success = 1;
|
||||
}
|
||||
}
|
||||
if ( ml->sml_desc == ad_pwdChangedTime ) {
|
||||
|
|
@ -2463,6 +2511,17 @@ do_modify:
|
|||
modtail = mods;
|
||||
}
|
||||
|
||||
/* TODO: do we remove pwdLastSuccess or set it to 'now'? */
|
||||
if (!got_del_success && attr_find(e->e_attrs, ad_pwdLastSuccess )){
|
||||
mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
|
||||
mods->sml_op = LDAP_MOD_DELETE;
|
||||
mods->sml_flags = SLAP_MOD_INTERNAL;
|
||||
mods->sml_desc = ad_pwdLastSuccess;
|
||||
mods->sml_next = NULL;
|
||||
modtail->sml_next = mods;
|
||||
modtail = mods;
|
||||
}
|
||||
|
||||
/* Delete all pwdInHistory attribute */
|
||||
if (!got_history && pp.pwdInHistory == 0 &&
|
||||
attr_find(e->e_attrs, ad_pwdHistory )){
|
||||
|
|
@ -2769,6 +2828,7 @@ int ppolicy_initialize()
|
|||
SLAP_AT_MANAGEABLE;
|
||||
}
|
||||
}
|
||||
ad_pwdLastSuccess = slap_schema.si_ad_pwdLastSuccess;
|
||||
{
|
||||
Syntax *syn;
|
||||
MatchingRule *mr;
|
||||
|
|
|
|||
|
|
@ -1019,6 +1019,19 @@ static struct slap_schema_ad_map {
|
|||
NULL, NULL, NULL, NULL, NULL,
|
||||
offsetof(struct slap_internal_schema, si_ad_pKCS8PrivateKey) },
|
||||
|
||||
{ "pwdLastSuccess", "( 1.3.6.1.4.1.42.2.27.8.1.29 NAME 'pwdLastSuccess' "
|
||||
"DESC 'The timestamp of the last successful authentication' "
|
||||
"EQUALITY generalizedTimeMatch "
|
||||
"ORDERING generalizedTimeOrderingMatch "
|
||||
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
|
||||
"SINGLE-VALUE "
|
||||
"NO-USER-MODIFICATION "
|
||||
"USAGE directoryOperation )",
|
||||
NULL, 0,
|
||||
NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL,
|
||||
offsetof(struct slap_internal_schema, si_ad_pwdLastSuccess) },
|
||||
|
||||
{ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -987,6 +987,9 @@ struct slap_internal_schema {
|
|||
/* privateKeys */
|
||||
AttributeDescription *si_ad_pKCS8PrivateKey;
|
||||
|
||||
/* ppolicy lastbind equivalent */
|
||||
AttributeDescription *si_ad_pwdLastSuccess;
|
||||
|
||||
/* Undefined Attribute Type */
|
||||
AttributeType *si_at_undefined;
|
||||
|
||||
|
|
@ -1867,10 +1870,12 @@ struct BackendDB {
|
|||
#define SLAP_DBFLAG_SYNC_SUBENTRY 0x40000U /* use subentry for context */
|
||||
#define SLAP_DBFLAG_MULTI_SHADOW 0x80000U /* uses mirrorMode/multi-master */
|
||||
#define SLAP_DBFLAG_DISABLED 0x100000U
|
||||
#define SLAP_DBFLAG_LASTBIND 0x200000U
|
||||
slap_mask_t be_flags;
|
||||
#define SLAP_DBFLAGS(be) ((be)->be_flags)
|
||||
#define SLAP_NOLASTMOD(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_NOLASTMOD)
|
||||
#define SLAP_LASTMOD(be) (!SLAP_NOLASTMOD(be))
|
||||
#define SLAP_LASTBIND(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_LASTBIND)
|
||||
#define SLAP_DBHIDDEN(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_HIDDEN)
|
||||
#define SLAP_DBDISABLED(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_DISABLED)
|
||||
#define SLAP_DB_ONE_SUFFIX(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_ONE_SUFFIX)
|
||||
|
|
|
|||
Loading…
Reference in a new issue