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.