ITS#9343 Allow a list of default policies

This commit is contained in:
Ondřej Kuzník 2022-02-08 16:46:59 +00:00 committed by Quanah Gibson-Mount
parent 1fac13d239
commit 950ff8a5f0
5 changed files with 255 additions and 24 deletions

View file

@ -55,9 +55,20 @@ after the
.B overlay
directive.
.TP
.B ppolicy_rules <LDAP URI> <policyDN>
Specify which pwdPolicy object to use when no specific policy is set on
a given user's entry. If there is no pwdPolicySubentry set, the URIs are
checked in order and the first one to match will apply. If one is selected
and the object at
.B policyDN
does not exist or is not a password policy, then no policies will be
enforced.
.TP
.B ppolicy_default <policyDN>
Specify the DN of the pwdPolicy object to use when no specific policy is
set on a given user's entry. If there is no specific policy for an entry
set on a given user's entry and none of the
.B ppolicy_rules
apply. If there is no specific policy for an entry
and no default is given, then no policies will be enforced.
.TP
.B ppolicy_forward_updates

View file

@ -53,9 +53,19 @@
typedef int (check_func)( char *passwd, struct berval *errmsg, Entry *ent, struct berval *arg );
#define ERRBUFSIZ 256
typedef struct policy_rule {
struct berval uri; /* straight from configuration, unparsed below */
struct berval base;
int scope;
Filter *filter;
struct berval policy_dn; /* DN of policy entry to select */
struct policy_rule *next;
} policy_rule;
/* Per-instance configuration information */
typedef struct pp_info {
struct berval def_policy; /* DN of default policy subentry */
struct policy_rule *policy_rules;
int use_lockout; /* send AccountLocked result? */
int hash_passwords; /* transparently hash cleartext pwds */
int forward_updates; /* use frontend for policy state updates */
@ -444,9 +454,10 @@ enum {
PPOLICY_USE_LOCKOUT,
PPOLICY_DISABLE_WRITE,
PPOLICY_CHECK_MODULE,
PPOLICY_DEFAULT_RULES,
};
static ConfigDriver ppolicy_cf_default, ppolicy_cf_checkmod;
static ConfigDriver ppolicy_cf_default, ppolicy_cf_rule, ppolicy_cf_checkmod;
static ConfigTable ppolicycfg[] = {
{ "ppolicy_default", "policyDN", 2, 2, 0,
@ -501,6 +512,13 @@ static ConfigTable ppolicycfg[] = {
"EQUALITY caseExactIA5Match "
"SYNTAX OMsIA5String "
"SINGLE-VALUE )", NULL, NULL },
{ "ppolicy_rules", "URL> <policyDN", 3, 3, 0,
ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT_RULES,
ppolicy_cf_rule,
"( OLcfgOvAt:12.8 NAME 'olcPPolicyRules' "
"DESC 'rules to apply the right ppolicy object for entry' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};
@ -512,7 +530,7 @@ static ConfigOCs ppolicyocs[] = {
"MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
"olcPPolicyUseLockout $ olcPPolicyForwardUpdates $ "
"olcPPolicyDisableWrite $ olcPPolicySendNetscapeControls $ "
"olcPPolicyCheckModule ) )",
"olcPPolicyCheckModule $ olcPPolicyRules ) )",
Cft_Overlay, ppolicycfg },
{ NULL, 0, NULL }
};
@ -568,6 +586,122 @@ ppolicy_cf_default( ConfigArgs *c )
return rc;
}
static int
ppolicy_cf_rule( ConfigArgs *c )
{
slap_overinst *on = (slap_overinst *)c->bi;
pp_info *pi = (pp_info *)on->on_bi.bi_private;
policy_rule *pr = NULL, **prp;
LDAPURLDesc *lud = NULL;
struct berval bv;
int i, rc = ARG_BAD_CONF;
assert( c->type == PPOLICY_DEFAULT_RULES );
Debug( LDAP_DEBUG_TRACE, "==> ppolicy_rules\n" );
if ( c->op == SLAP_CONFIG_EMIT ) {
if ( pi->policy_rules ) {
Attribute a = {
.a_desc = c->ca_desc->ad,
.a_vals = c->rvalue_vals,
};
for ( pr = pi->policy_rules; pr; pr = pr->next ) {
bv.bv_len = pr->uri.bv_len + pr->policy_dn.bv_len +
STRLENOF("\"\" \"\"");
bv.bv_val = ch_malloc( bv.bv_len + 1 );
snprintf( bv.bv_val, bv.bv_len + 1, "\"%s\" \"%s\"",
pr->uri.bv_val, pr->policy_dn.bv_val );
ber_bvarray_add( &a.a_vals, &bv );
a.a_numvals++;
}
ordered_value_renumber( &a );
c->rvalue_vals = a.a_vals;
return LDAP_SUCCESS;
}
return 1;
} else if ( c->op == LDAP_MOD_DELETE ) {
if ( pi->policy_rules ) {
for ( prp = &pi->policy_rules, i=0; *prp; i++ ) {
pr = *prp;
if ( c->valx == -1 || i == c->valx ) {
*prp = pr->next;
pr->next = NULL;
ch_free( pr->uri.bv_val );
ch_free( pr->base.bv_val );
ch_free( pr->policy_dn.bv_val );
filter_free( pr->filter );
ch_free( pr );
if ( i == c->valx )
break;
} else {
prp = &pr->next;
}
}
}
return LDAP_SUCCESS;
}
if ( ldap_url_parse_ext( c->argv[1], &lud, 0 ) != LDAP_SUCCESS ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"ppolicy_rules: bad policy URL");
return rc;
}
pr = ch_calloc( 1, sizeof(policy_rule) );
ber_str2bv( c->argv[1], 0, 1, &pr->uri );
pr->scope = lud->lud_scope;
ber_str2bv( lud->lud_dn, 0, 0, &bv );
if ( dnNormalize( 0, NULL, NULL, &bv, &pr->base, NULL ) ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"ppolicy_rules: bad URL base" );
rc = ARG_BAD_CONF;
goto done;
}
if ( lud->lud_filter ) {
pr->filter = str2filter( lud->lud_filter );
if ( !pr->filter ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"ppolicy_rules: bad filter" );
rc = ARG_BAD_CONF;
goto done;
}
}
ber_str2bv( c->argv[2], 0, 0, &bv );
if ( dnNormalize( 0, NULL, NULL, &bv, &pr->policy_dn, NULL ) ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"ppolicy_rules: bad policy DN" );
rc = ARG_BAD_CONF;
goto done;
}
rc = LDAP_SUCCESS;
for ( i = 0, prp = &pi->policy_rules;
*prp && ( c->valx < 0 || i < c->valx );
prp = &(*prp)->next, i++ )
/* advance to the desired position */ ;
pr->next = *prp;
*prp = pr;
done:
ldap_free_urldesc( lud );
if ( rc != LDAP_SUCCESS ) {
ch_free( pr->uri.bv_val );
ch_free( pr->policy_dn.bv_val );
filter_free( pr->filter );
ch_free( pr );
}
return rc;
}
#ifdef SLAPD_MODULES
static int
ppolicy_cf_checkmod( ConfigArgs *c )
@ -969,10 +1103,22 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
ad = ad_pwdPolicySubentry;
if ( (a = attr_find( e->e_attrs, ad )) == NULL ) {
policy_rule *pr = pi->policy_rules;
/*
* entry has no password policy assigned - use default
* entry has no password policy assigned - find the default one
*/
vals = &pi->def_policy;
for ( pr = pi->policy_rules; pr; pr = pr->next ) {
if ( !dnIsSuffixScope( &e->e_nname, &pr->base, pr->scope ) ) continue;
if ( pr->filter && test_filter( op, e, pr->filter ) != LDAP_COMPARE_TRUE ) continue;
/* We found a match */
break;
}
if ( pr ) {
vals = &pr->policy_dn;
} else {
vals = &pi->def_policy;
}
if ( !vals->bv_val )
goto defaultpol;
} else {
@ -3358,10 +3504,21 @@ ppolicy_db_destroy(
{
slap_overinst *on = (slap_overinst *) be->bd_info;
pp_info *pi = on->on_bi.bi_private;
policy_rule *pr = pi->policy_rules, *next;
on->on_bi.bi_private = NULL;
ldap_pvt_thread_mutex_destroy( &pi->pwdFailureTime_mutex );
free( pi->def_policy.bv_val );
while ( pr ) {
next = pr->next;
ch_free( pr->uri.bv_val );
ch_free( pr->base.bv_val );
ch_free( pr->policy_dn.bv_val );
filter_free( pr->filter );
ch_free( pr );
pr = next;
}
free( pi );
ov_count--;

View file

@ -36,6 +36,47 @@ pwdFailureCountInterval: 120
pwdSafeModify: TRUE
pwdLockout: TRUE
dn: cn=Idle Expiration Policy, ou=Policies, dc=example, dc=com
objectClass: top
objectClass: device
objectClass: pwdPolicy
cn: Idle Expiration Policy
pwdAttribute: 2.5.4.35
pwdLockoutDuration: 15
pwdInHistory: 6
pwdCheckQuality: 2
pwdExpireWarning: 10
pwdMaxIdle: 15
pwdMinLength: 5
pwdMaxLength: 13
pwdGraceAuthnLimit: 3
pwdAllowUserChange: TRUE
pwdMustChange: TRUE
pwdMaxFailure: 3
pwdFailureCountInterval: 120
pwdSafeModify: TRUE
pwdLockout: TRUE
dn: cn=Stricter Policy, ou=Policies, dc=example, dc=com
objectClass: top
objectClass: device
objectClass: pwdPolicy
cn: Stricter Policy
pwdAttribute: 2.5.4.35
pwdLockoutDuration: 15
pwdInHistory: 6
pwdCheckQuality: 2
pwdExpireWarning: 10
pwdMaxAge: 15
pwdMinLength: 5
pwdMaxLength: 13
pwdAllowUserChange: TRUE
pwdMustChange: TRUE
pwdMaxFailure: 3
pwdFailureCountInterval: 120
pwdSafeModify: TRUE
pwdLockout: TRUE
dn: uid=nd, ou=People, dc=example, dc=com
objectClass: top
objectClass: person

View file

@ -38,6 +38,8 @@ rootpw secret
lastbind on
overlay ppolicy
ppolicy_rules ldap:///uid=ndadmin,ou=People,dc=example,dc=com??base "cn=No Policy,ou=Policies,dc=example,dc=com"
ppolicy_rules "ldap:///???(description=idle)" "cn=Idle Expiration Policy, ou=Policies, dc=example, dc=com"
ppolicy_default "cn=Standard Policy,ou=Policies,dc=example,dc=com"
ppolicy_use_lockout

View file

@ -244,6 +244,16 @@ fi
echo "Testing failed logins when password/policy missing..."
$LDAPSEARCH -e ppolicy -H $URI1 \
-D "$PWADMIN" -w hasnopolicy \
-b "$BASEDN" -s base > $SEARCHOUT 2>&1
RC=$?
if test $RC = 0 ; then
echo "Password accepted ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit 1
fi
$LDAPSEARCH -e ppolicy -H $URI1 \
-D "uid=test, ou=People,$BASEDN" -w hasnopolicy \
-b "$BASEDN" -s base > $SEARCHOUT 2>&1
@ -470,15 +480,13 @@ if test $RC = 0 ; then
fi
echo "Testing idle password expiration"
echo "Reconfiguring policy to replace expiration with idle expiration..."
echo "Switching to a policy with idle expiration..."
$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
$TESTOUT 2>&1 << EOMODS
dn: cn=Standard Policy, ou=Policies, dc=example, dc=com
dn: $USER
changetype: modify
delete: pwdMaxAge
-
add: pwdMaxIdle
pwdMaxIdle: 15
add: description
description: idle
EOMODS
RC=$?
@ -508,15 +516,13 @@ if test $RC != 49 ; then
exit 1
fi
echo "Reverting policy changes..."
echo "Reverting to Standard policy..."
$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
$TESTOUT 2>&1 << EOMODS
dn: cn=Standard Policy, ou=Policies, dc=example, dc=com
dn: $USER
changetype: modify
delete: pwdMaxIdle
-
add: pwdMaxAge
pwdMaxAge: 30
delete: description
description: idle
EOMODS
RC=$?
@ -698,15 +704,29 @@ if test $RC != 0 ; then
fi
echo "Reconfiguring policy to remove grace logins..."
$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF >> \
$TESTOUT 2>&1 << EOMODS
dn: cn=Standard Policy, ou=Policies, dc=example, dc=com
dn: olcOverlay={0}ppolicy,olcDatabase={1}$BACKEND,cn=config
changetype: modify
delete: pwdGraceAuthnLimit
-
replace: pwdMaxAge
pwdMaxAge: 15
-
add: olcPPolicyRules
olcPPolicyRules: {0}"ldap:///dc=example,dc=com???(!(description=grace))"
"cn=Stricter Policy, ou=Policies, dc=example, dc=com"
EOMODS
RC=$?
if test $RC != 0 ; then
echo "ldapmodify failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
fi
$LDAPMODIFY -v -D cn=config -H $URI2 -y $CONFIGPWF >> \
$TESTOUT 2>&1 << EOMODS
dn: olcOverlay={0}ppolicy,olcDatabase={1}$BACKEND,cn=config
changetype: modify
add: olcPPolicyRules
olcPPolicyRules: {0}"ldap:///dc=example,dc=com???(!(description=grace))"
"cn=Stricter Policy, ou=Policies, dc=example, dc=com"
EOMODS
RC=$?