mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-28 10:39:34 -05:00
ITS#9343 Allow a list of default policies
This commit is contained in:
parent
1fac13d239
commit
950ff8a5f0
5 changed files with 255 additions and 24 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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--;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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=$?
|
||||
|
|
|
|||
Loading…
Reference in a new issue