Commit 094848b6 authored by Ondřej Kuzník's avatar Ondřej Kuzník Committed by Quanah Gibson-Mount
Browse files

ITS#9279 Implement Netscape password policy controls in ppolicy

parent 0687e289
......@@ -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";
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment