Subject: | Account lockout code duplication between KDB module |
Account lockout is implemented via two DAL functions:
check_policy_as() to deny an AS request if the principal is locked
out, and audit_as_req() to update any lockout state after a
successful or failed AS-REQ. A KDB module can make these decisions
any way it wants, or deliberately not implement account lockout. But
in-tree KDB modules and modules which want to behave similarly will:
* retrieve "disable_last_success" and "disable_lockout" boolean
relations from the DB config section in the profile.
* look up the policy associated with the principal (unless
disable_lockout is set)
* determine if the account is locked out as a function of:
- the policy pw_max_fail and pw_lockout duration fields
- the principal fail_auth_count and last_failed fields
- the principal last administrative unlock time (from tl-data)
- the current timestamp
* in audit_as_req(), update the principal fields as a function of:
- whether the principal is already locked out
- the success or failure of the AS operation
- the disable_lockout and disable_last_success configuration
- the principal last administrative unlock time
- the policy pw_failcnt_interval field
- the current timestamp
This behavior yields a tangled mess of DB-specific operations and
common decision-making logic:
* Enable/disable configuration is common.
* Retrieving the policy name reference from the principal entry is
common (and currently irritating to implement; see issue #7951).
* Fetching the policy object is DB-specific, in the sense that re-
entering libkdb5 to do it would cause a recursive lock of
krb5_db2_mutex in DB2.
* Computing whether the principal is locked out is common.
* Computing the updates required is common.
* Making the updates is DB-specific. (For instance, in the
forthcoming LMDB module we want to do it with a transactional update
to the lockout record, without touching the primary principal DB.)
Right now each KDB module implements all of the above. Moving the
common decision-making logic into exposed libkdb5 functions might be
a benefit. Possibly interfaces might include:
* conf section name -> disable_last_success, disable_lockout booleans
* principal entry -> policy name reference (or more likely ->
osa_princ_ent_t; this is issue #7951)
* principal entry, policy entry, timestamp -> is_locked_out boolean
* principal entry, policy entry, timestamp, disable_last_success,
disable_lockout, success/failure status -> set_last_success,
set_last_failure, zero_fail_count booleans (set_last_failure also
means the failure count should be incremented, possibly after zeroing
it)
It is an open question whether the added libkdb5 API surface is worth
the amount of decision-making logic these interfaces would factor
out, especially for the last one.
check_policy_as() to deny an AS request if the principal is locked
out, and audit_as_req() to update any lockout state after a
successful or failed AS-REQ. A KDB module can make these decisions
any way it wants, or deliberately not implement account lockout. But
in-tree KDB modules and modules which want to behave similarly will:
* retrieve "disable_last_success" and "disable_lockout" boolean
relations from the DB config section in the profile.
* look up the policy associated with the principal (unless
disable_lockout is set)
* determine if the account is locked out as a function of:
- the policy pw_max_fail and pw_lockout duration fields
- the principal fail_auth_count and last_failed fields
- the principal last administrative unlock time (from tl-data)
- the current timestamp
* in audit_as_req(), update the principal fields as a function of:
- whether the principal is already locked out
- the success or failure of the AS operation
- the disable_lockout and disable_last_success configuration
- the principal last administrative unlock time
- the policy pw_failcnt_interval field
- the current timestamp
This behavior yields a tangled mess of DB-specific operations and
common decision-making logic:
* Enable/disable configuration is common.
* Retrieving the policy name reference from the principal entry is
common (and currently irritating to implement; see issue #7951).
* Fetching the policy object is DB-specific, in the sense that re-
entering libkdb5 to do it would cause a recursive lock of
krb5_db2_mutex in DB2.
* Computing whether the principal is locked out is common.
* Computing the updates required is common.
* Making the updates is DB-specific. (For instance, in the
forthcoming LMDB module we want to do it with a transactional update
to the lockout record, without touching the primary principal DB.)
Right now each KDB module implements all of the above. Moving the
common decision-making logic into exposed libkdb5 functions might be
a benefit. Possibly interfaces might include:
* conf section name -> disable_last_success, disable_lockout booleans
* principal entry -> policy name reference (or more likely ->
osa_princ_ent_t; this is issue #7951)
* principal entry, policy entry, timestamp -> is_locked_out boolean
* principal entry, policy entry, timestamp, disable_last_success,
disable_lockout, success/failure status -> set_last_success,
set_last_failure, zero_fail_count booleans (set_last_failure also
means the failure count should be incremented, possibly after zeroing
it)
It is an open question whether the added libkdb5 API surface is worth
the amount of decision-making logic these interfaces would factor
out, especially for the last one.