mirror of
https://git.openldap.org/openldap/openldap.git
synced 2026-02-18 18:18:06 -05:00
ITS#9279 Implement Netscape password policy controls in ppolicy
This commit is contained in:
parent
0687e289e4
commit
094848b6de
2 changed files with 85 additions and 5 deletions
|
|
@ -36,6 +36,9 @@ when considering a single-valued password attribute, while
|
|||
the userPassword attribute allows multiple values. This implementation
|
||||
enforces a single value for the userPassword attribute, despite
|
||||
its specification.
|
||||
.P
|
||||
In addition to supporting the IETF Password Policy, this module can
|
||||
send the Netscape Password validity controls when configured to do so.
|
||||
|
||||
.SH CONFIGURATION
|
||||
These
|
||||
|
|
@ -84,6 +87,12 @@ that sending the
|
|||
error code provides useful information
|
||||
to an attacker; sites that are sensitive to security issues should not
|
||||
enable this option.
|
||||
.TP
|
||||
.B ppolicy_send_netscape_controls
|
||||
If set, ppolicy will send the password policy expired (2.16.840.1.113730.3.4.4)
|
||||
and password policy expiring (2.16.840.1.113730.3.4.5) controls when
|
||||
appropriate. The controls are not sent for bind requests where the Password
|
||||
policy control has already been requested. Default is not to send the controls.
|
||||
|
||||
.SH OBJECT CLASS
|
||||
The
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ typedef struct pp_info {
|
|||
int use_lockout; /* send AccountLocked result? */
|
||||
int hash_passwords; /* transparently hash cleartext pwds */
|
||||
int forward_updates; /* use frontend for policy state updates */
|
||||
int send_netscape_controls; /* send netscape password controls */
|
||||
} pp_info;
|
||||
|
||||
/* Our per-connection info - note, it is not per-instance, it is
|
||||
|
|
@ -243,6 +244,13 @@ static ConfigTable ppolicycfg[] = {
|
|||
"( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' "
|
||||
"DESC 'Warn clients with AccountLocked' "
|
||||
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
|
||||
{ "ppolicy_send_netscape_controls", "on|off", 1, 2, 0,
|
||||
ARG_ON_OFF|ARG_OFFSET,
|
||||
(void *)offsetof(pp_info,send_netscape_controls),
|
||||
"( OLcfgOvAt:12.6 NAME 'olcPPolicySendNetscapeControls' "
|
||||
"DESC 'Send Netscape policy controls' "
|
||||
"EQUALITY booleanMatch "
|
||||
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
|
||||
{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
|
||||
};
|
||||
|
||||
|
|
@ -252,7 +260,8 @@ static ConfigOCs ppolicyocs[] = {
|
|||
"DESC 'Password Policy configuration' "
|
||||
"SUP olcOverlayConfig "
|
||||
"MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
|
||||
"olcPPolicyUseLockout $ olcPPolicyForwardUpdates ) )",
|
||||
"olcPPolicyUseLockout $ olcPPolicyForwardUpdates $ "
|
||||
"olcPPolicySendNetscapeControls ) )",
|
||||
Cft_Overlay, ppolicycfg },
|
||||
{ NULL, 0, NULL }
|
||||
};
|
||||
|
|
@ -376,6 +385,8 @@ account_locked( Operation *op, Entry *e,
|
|||
#define PPOLICY_GRACE 0x81L /* primitive + 1 */
|
||||
|
||||
static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
|
||||
static const char ppolicy_pwd_expired_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRED;
|
||||
static const char ppolicy_pwd_expiring_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRING;
|
||||
|
||||
static LDAPControl *
|
||||
create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err )
|
||||
|
|
@ -435,6 +446,42 @@ fail:
|
|||
return cp;
|
||||
}
|
||||
|
||||
static LDAPControl *
|
||||
create_passexpiry( Operation *op, int expired, int warn )
|
||||
{
|
||||
BerElementBuffer berbuf;
|
||||
BerElement *ber = (BerElement *) &berbuf;
|
||||
LDAPControl c = { 0 }, *cp;
|
||||
char buf[sizeof("-2147483648")];
|
||||
struct berval bv = { .bv_val = buf, .bv_len = sizeof(buf) };
|
||||
int rc;
|
||||
|
||||
BER_BVZERO( &c.ldctl_value );
|
||||
|
||||
bv.bv_len = snprintf( bv.bv_val, bv.bv_len, "%d", warn );
|
||||
|
||||
ber_init2( ber, NULL, LBER_USE_DER );
|
||||
ber_printf( ber, "O", &bv );
|
||||
|
||||
if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
|
||||
if ( expired ) {
|
||||
cp->ldctl_oid = (char *)ppolicy_pwd_expired_oid;
|
||||
} else {
|
||||
cp->ldctl_oid = (char *)ppolicy_pwd_expiring_oid;
|
||||
}
|
||||
cp->ldctl_iscritical = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
static LDAPControl **
|
||||
add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
|
||||
{
|
||||
|
|
@ -913,7 +960,9 @@ ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
|
|||
assert( rs->sr_ctrls[0] != NULL );
|
||||
|
||||
for ( n = 0; rs->sr_ctrls[n]; n++ ) {
|
||||
if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ) {
|
||||
if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ||
|
||||
rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expired_oid ||
|
||||
rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expiring_oid ) {
|
||||
op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx );
|
||||
rs->sr_ctrls[n] = (LDAPControl *)(-1);
|
||||
break;
|
||||
|
|
@ -944,6 +993,7 @@ ppolicy_bind_response( Operation *op, SlapReply *rs )
|
|||
{
|
||||
ppbind *ppb = op->o_callback->sc_private;
|
||||
slap_overinst *on = ppb->on;
|
||||
pp_info *pi = on->on_bi.bi_private;
|
||||
Modifications *mod = ppb->mod, *m;
|
||||
int pwExpired = 0;
|
||||
int ngut = -1, warn = -1, age, rc;
|
||||
|
|
@ -955,6 +1005,7 @@ ppolicy_bind_response( Operation *op, SlapReply *rs )
|
|||
char nowstr_usec[ LDAP_LUTIL_GENTIME_BUFSIZE+8 ];
|
||||
struct berval timestamp, timestamp_usec;
|
||||
BackendInfo *bi = op->o_bd->bd_info;
|
||||
LDAPControl *ctrl = NULL;
|
||||
Entry *e;
|
||||
|
||||
/* If we already know it's locked, just get on with it */
|
||||
|
|
@ -1233,7 +1284,6 @@ locked:
|
|||
Operation op2 = *op;
|
||||
SlapReply r2 = { REP_RESULT };
|
||||
slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
|
||||
pp_info *pi = on->on_bi.bi_private;
|
||||
LDAPControl c, *ca[2];
|
||||
|
||||
op2.o_tag = LDAP_REQ_MODIFY;
|
||||
|
|
@ -1273,14 +1323,20 @@ locked:
|
|||
}
|
||||
|
||||
if ( ppb->send_ctrl ) {
|
||||
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;
|
||||
}
|
||||
ctrl = create_passcontrol( op, warn, ngut, ppb->pErr );
|
||||
} else if ( pi->send_netscape_controls ) {
|
||||
if ( ppb->pErr != PP_noError || ngut > 0 ) {
|
||||
ctrl = create_passexpiry( op, 1, 0 );
|
||||
} else if ( warn > 0 ) {
|
||||
ctrl = create_passexpiry( op, 0, warn );
|
||||
}
|
||||
}
|
||||
if ( ctrl ) {
|
||||
ppb->oldctrls = add_passcontrol( op, rs, ctrl );
|
||||
op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
|
||||
}
|
||||
|
|
@ -2512,6 +2568,21 @@ int ppolicy_initialize()
|
|||
return code;
|
||||
}
|
||||
|
||||
/* We don't expect to receive these controls, only send them */
|
||||
code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRED,
|
||||
0, NULL, NULL, NULL );
|
||||
if ( code != LDAP_SUCCESS ) {
|
||||
Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code, 0, 0 );
|
||||
return code;
|
||||
}
|
||||
|
||||
code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRING,
|
||||
0, NULL, NULL, NULL );
|
||||
if ( code != LDAP_SUCCESS ) {
|
||||
Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code, 0, 0 );
|
||||
return code;
|
||||
}
|
||||
|
||||
ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
|
||||
|
||||
ppolicy.on_bi.bi_type = "ppolicy";
|
||||
|
|
|
|||
Loading…
Reference in a new issue