Description
PAM (Pluggable Authentication Modules) uses pam_authenticate() to verify credentials, but that does not replace account management policy checks. Ignoring the result of pam_acct_mgmt() after authentication can miss account expiry or access restrictions.
Potential impact
- Users prohibited by account management policies may be allowed to log in.
- Account or password expiry conditions may not be handled correctly.
- The actual impact depends on the PAM modules and account policies configured for the service.
Remediation
- After authentication succeeds, call
pam_acct_mgmt()to check account status and access conditions. Do not grant access if this check fails. PAM_NEW_AUTHTOK_REQDmeans that the password must be changed. Deny access until an authorized password-change procedure is completed.- Configure the required authentication and account management modules and least-privilege policies. Log login successes and failures without recording passwords.
Examples
These excerpts illustrate the PAM binding flow. Bindings for pam_start and handle_conv, password delivery and pam_end cleanup are omitted. A real implementation must check initialization and clean up successfully started transactions on every exit path.
Before
python
from pam import PamHandle, PamConv
from ctypes import CDLL, c_int, byref
from ctypes.util import find_library
libpam = CDLL(find_library("pam"))
pam_authenticate = libpam.pam_authenticate
pam_authenticate.argtypes = [PamHandle, c_int]
pam_authenticate.restype = c_int
def authenticate(username, password, service='login'):
handle = PamHandle()
conv = PamConv(handle_conv, 0)
retval = pam_start(service, username, byref(conv), byref(handle))
if retval != 0:
return False
# Authenticate without checking account policy
return pam_authenticate(handle, 0) == 0
After
python
from pam import PamHandle, PamConv
from ctypes import CDLL, c_int, byref
from ctypes.util import find_library
libpam = CDLL(find_library("pam"))
pam_authenticate = libpam.pam_authenticate
pam_authenticate.argtypes = [PamHandle, c_int]
pam_authenticate.restype = c_int
pam_acct_mgmt = libpam.pam_acct_mgmt
pam_acct_mgmt.argtypes = [PamHandle, c_int]
pam_acct_mgmt.restype = c_int
def authenticate(username, password, service='login'):
handle = PamHandle()
conv = PamConv(handle_conv, 0)
retval = pam_start(service, username, byref(conv), byref(handle))
if retval != 0:
return False
# Also check account status
return pam_authenticate(handle, 0) == 0 and pam_acct_mgmt(handle, 0) == 0
Explanation:
- Before: Checks only authentication, without checking the account management policy result.
- After: Returns
Trueonly if authentication and the account status check both succeed.