Account Policy Design

From Fedora Directory Server

Contents

Account Policy Plugin - Software Design Specification

Introduction

The Fedora Directory Server equips administrators with a set of features for managing account access, including password policy and account inactivation. However, beyond the ability to lock out an account based on password failures, and being able to specify accounts that should be inactivated, there is a lack of account policy functionality. An example is that roles and class of service can inactivate accounts that were created before a certain date using a filter on the createTimestamp, but it can't inactivate accounts that have existed for more than a certain amount of time because that requires a comparison between the creation time with the current time. Therefore we propose to create an account policy plugin to handle some of the more popular account policy features requested in the past.

Goals and Guidelines

Our goals include simple but flexible configuration. For basic usage the required configuration should be trivial with sensible defaults. However, high customization needs to be available. For example, at least until we've settled on a schema, we'll provide attribute mapping.

We also want to provide easy but flexible administration. We don't want to complicate administrators' lives unnecessarily when they want to add or remove a policy for a set of users. It should be simple to define a policy and target a set of users.

Finally we want to provide easy means for auditing the account states where applicable.

Detailed Design

General Plugin Configuration

When the plugin starts up it will check if it's being started with an argument (nsslapd-pluginarg0). If it has an argument, the value should point to a configuration entry which will be retrieved by DN. If the plugin does not have an argument some defaults will be used. If the entry pointed to be the DN can not be retrieved, plugin initialization will fail and the plugin will not start because assuming defaults when there a configuration entry is indicated would be a security risk. If the configuration entry is retrieved it will be parsed for configuration. Any options missing in the configuration entry will take on defaults. Here is an example configuration:

dn: cn=acct_policy_plugin,cn=plugins,cn=config
objectClass: top
objectClass: nsSlapdPlugin
objectClass: extensibleObject
cn: acct_policy_plugin
nsslapd-pluginPath: /path/to/libacctpolicy.so
nsslapd-pluginInitfunc: acct_policy_init
nsslapd-pluginType: object
nsslapd-pluginEnabled: on
nsslapd-plugin-depends-on-type: database
nsslapd-pluginId: acct-policy
nsslapd-pluginarg0: cn=acct_plugin_config,cn=acct_policy_plugin,cn=plugins,cn=config
dn: cn=acct_plugin_config,cn=acct_policy_plugin,cn=plugins,cn=config
objectClass: top
objectClass: extensibleObject
cn: acct_plugin_config
alwaysrecordlogin: yes
stateattrname: loginTimestamp
altstateattrname: createTimestamp
specattrname: acctPolicySubentry
limitattrname: accountInactivityLimit

Detailed Design of Account Inactivity

Introduction

This component of the account policy plugin will inactivate accounts based on their inactivity. It's implemented in two parts, a post-op BIND callback which maintains a timestamp of the last login in the binding entry, and pre-op BIND callback which compares the current time with the last login timestamp and makes an allow or deny decision.

Logging

The logging subcomponent will record a timestamp in the bind entry after successful binds. The timestamp will be stored in the bind entry in generalized Zulu format like "%Y%m%d%H%M%SZ" (trailing literal 'Z') in the operational attribute loginTimestamp. Unless the configuration has alwaysrecordlogin set to true, the plugin will only maintain the timestamps in bind entries that are covered by an inactivity policy; that is anyone who has an acctPolicySubentry attribute. Here is an example of a person who is covered by an account policy and has a login timestamp:

dn: uid=scarter,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
title: engineer
uid: scarter
sn: Carter
cn: Sam Carter
loginTimestamp: 20060527001051Z
acctPolicySubentry: cn=AccountPolicy,dc=example,dc=com

Enforcement Method

Enforcement is implemented as a SLAPI_PLUGIN_PRE_BIND_FN and performs the inactivity check using the loginTimestamp value and the current date, compared to the limit specified in the account policy that covers the bind entry. Example account policy:

dn: cn=AccountPolicy,dc=example,dc=com
objectClass: top
objectClass: ldapsubentry
objectClass: extensibleObject
objectClass: accountpolicy
# 86400 seconds per day * 30 days = 2592000 seconds
accountInactivityLimit: 2592000
cn: AccountPolicy

If successful the plugin returns 0 and the bind proceeds like normal. If the inactivity limit has been exceeded an LDAP error and a message are sent to the client and the plugin returns -1 which ends the bind attempt.

If the loginTimestamp is missing then the user has never logged in and the administrator didn't provision one. An alternate attribute is used in this case, the createTimestamp by default.

However, this method will not work well with older releases of Directory Server because bind preop functions are not currently called from SASL binds, which makes SASL bind a loophole around our inactivity enforcement.

Enforcement Method Alternative 1 (OBSOLETE, IGNORE)

There are three proposals for allowing or denying binds based on account inactivity. The first two methods use a virtual attribute provider but how the exported attribute results in denied binds is different.

In the first proposal every entry that is covered by an account inactivity policy has an acctPolicySubentry specifier which has a DN for value. The entry pointed at provides an inactivity limit. This is similar to how password policy is configured (1). Here is an example:

dn: uid=scarter,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
title: engineer
uid: scarter
sn: Carter
cn: Sam Carter
lastLoginTime: 20060527001051Z
acctPolicySubentry: cn=Acct Policy,cn=config
dn: cn=Acct Policy,cn=config
objectClass: top
objectClass: ldapsubentry
objectClass: AccountPolicy
accountInactivityLimit: 2592000

Then we will add a virtual attribute provider for the nsAccountLock attribute which will have the value of "true" if the difference between the current time and the timestamp of the last bind exceeds the accountInactivityLimit in the policy. AccountInactivityLimit is specified in seconds, so in the above example nsAccountLock would show up with a value of "true" a month after 2006-05-27 00:10:51.

Enforcement Method Alternative 2 (OBSOLETE, IGNORE)

Using the virtual attribute service provider interface we will expose a computed attribute called timeSinceLastLogin. The value of this attribute will be the delta between the last login time and the current time. Here is an example where Sam has not logged in for 15 days:

dn: uid=scarter,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
title: engineer
uid: scarter
sn: Carter
cn: Sam Carter
lastLoginTime: 20060527001051Z
timeSinceLogin: 1296000

With this virtual attribute available we can configure account locking using filter-based roles. Here is an example that sets a one-month inactivity policy for engineers:

dn: cn=Account Inactive Role,dc=example,dc=com
objectClass: top
objectClass: ldapsubentry
objectClass: nsroledefinition
objectClass: nscomplexroledefinition
objectClass: nsfilteredroledefinition
cn: Acount Inactive Role
nsRoleFilter: (&(timeSinceLogin>=2592000)(title=engineer))
dn: cn=Account Inactive CoS,dc=example,dc=com
objectClass: top
objectClass: ldapsubentry
objectClass: cossuperdefinition
objectClass: cosClassicDefinition
cn: Account Inactive CoS
costemplatedn: dc=example,dc=com
cosspecifier: nsRole
cosAttribute: nsAccountLock operational
dn: cn="cn=account inactive role,dc=example,dc=com",dc=example,dc=com
cn: cn=account inactive role,dc=example,dc=com
objectClass: top
objectClass: costemplate
objectClass: extensibleobject
nsAccountLock: true

Schema Changes

Experimental schema used in development and testing of the plugin.

#
# Schema for supporting the account policy plugin (60acctpolicy.ldif)
#
dn: cn=schema
#####
## accountInactivityLimit specifies inactivity limit in accountPolicy objects
## (DirectoryString syntax)
attributeTypes: ( accountInactivityLimit-oid NAME 'accountInactivityLimit'
  DESC 'Account Policy Plugin'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'Experimental' )
#####
## lastLoginTimestamp holds login state in user entries (GeneralizedTime syntax)
attributeTypes: ( lastLoginTimestamp-oid NAME 'lastLoginTimestamp'
  DESC 'Account Policy Plugin'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE USAGE directoryOperation
  X-ORIGIN 'Experimental' )
#####
## acctPolicySubentry points to an account policy (DN syntax)
attributeTypes: ( acctPolicySubentry-oid NAME 'acctPolicySubentry'
  DESC 'Account Policy Plugin'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE USAGE directoryOperation
  X-ORIGIN 'Experimental' )
#####
## accountPolicy is the objectclass of account policy subentries
objectClasses: ( accountPolicy-oid NAME 'accountPolicy'
  DESC 'Account Policy Plugin'
  SUP top MAY ( accountInactivityLimit ) X-ORIGIN 'Experimental' )

Outstanding Issues

  1. User Interface
    1. Will we need support in the GUI? Both enforcement methods require the use of Roles and CoS, maybe we can leverage the existing stuff. But the Account Inactivation feature has full UI that adds all the required roles and stuff, and so does Local PWP.
    2. If we add UI, should we also touch replication? When adding account inactivity policy we could search for agreements and offer to add lastLoginTime to the excluded attribute list, if they don't want to replace last login state.
  2. Should we inquire with the current maintainer of the password policy draft if we can sneak account inactivity into the same draft? [[1]] Although the password policy draft deals with some account related stuff like account lockout time after password failures, account inactivity falls into an area less gray and more dark, so maybe not. Is there an account policy draft?

Detailed Design of Account Expiration

Introduction

This component of the account policy plugin will inactivate accounts based on an expiration date.

Design

This works in a similar way to Account Inactivation. The account policy in effect for an entry would specify an expiration period for the account and upon login the plugin tests whether the current time minus the time in the createTimestamp is greater than the expiration period. createTimestamp is already automatically added when new entries are added.

dn: uid=scarter,ou=people,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
title: engineer
uid: scarter
sn: Carter
cn: Sam Carter
createTimestamp: 20060527001051Z
acctPolicySubentry: cn=AccountPolicy,dc=example,dc=com
dn: cn=AccountPolicy,dc=example,dc=com
objectClass: top
objectClass: ldapsubentry
objectClass: extensibleObject
objectClass: accountpolicy
# 86400 seconds per day * 30 days = 2592000 seconds
expirationPeriod: 2592000
cn: AccountPolicy

Schema Changes

TBD.

Outstanding Issues

Mostly same as the Account Inactivation issues.

Bibliography

(1) Prasanta Behera's password policy draft, 9th iteration (http://www.faqs.org/ftp/internet-drafts/draft-behera-ldap-password-policy-09.txt)