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 */
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,
"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 }
};
"MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
"olcPPolicyUseLockout $ olcPPolicyForwardUpdates $ "
"olcPPolicyDisableWrite $ olcPPolicySendNetscapeControls $ "
- "olcPPolicyCheckModule ) )",
+ "olcPPolicyCheckModule $ olcPPolicyRules ) )",
Cft_Overlay, ppolicycfg },
{ NULL, 0, NULL }
};
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 )
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 {
{
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--;
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
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=$?
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=$?
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=$?