summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2013-01-06 14:28:06 +0000
committerArthur de Jong <arthur@arthurdejong.org>2013-01-06 14:28:06 +0000
commiteb86f8754b4f55b1794ed3e208ee9f55272c9c88 (patch)
tree7aa6412f049e29c699444d74877a40e103c5dfd0
parent28aeaa463cbb9ae0fe556eca0b8e26d95d0b316d (diff)
request and parse password policy controls when doing user authentication in pynslcd
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-pam-ldapd@1905 ef36b2f9-881f-0410-afb5-c4e39611909c
-rw-r--r--pynslcd/pam.py41
1 files changed, 33 insertions, 8 deletions
diff --git a/pynslcd/pam.py b/pynslcd/pam.py
index 1099490..abde4de 100644
--- a/pynslcd/pam.py
+++ b/pynslcd/pam.py
@@ -21,6 +21,7 @@
import logging
import socket
+from ldap.controls.ppolicy import PasswordPolicyControl, PasswordPolicyError
from ldap.filter import escape_filter_chars as escape
import ldap
@@ -34,12 +35,33 @@ def try_bind(userdn, password):
# open a new connection
conn = ldap.initialize(cfg.uri)
# bind using the specified credentials
- conn.simple_bind_s(userdn, password)
+ pwctrl = PasswordPolicyControl()
+ res, data, msgid, ctrls = conn.simple_bind_s(userdn, password, serverctrls=[pwctrl])
+ # go over bind result server controls
+ for ctrl in ctrls:
+ if ctrl.controlType == PasswordPolicyControl.controlType:
+ # found a password policy control
+ logging.debug('PasswordPolicyControl found: error=%s (%s), timeBeforeExpiration=%s, graceAuthNsRemaining=%s',
+ 'None' if ctrl.error is None else PasswordPolicyError(ctrl.error).prettyPrint(),
+ ctrl.error, ctrl.timeBeforeExpiration, ctrl.graceAuthNsRemaining)
+ if ctrl.error == 0: # passwordExpired
+ return constants.NSLCD_PAM_AUTHTOK_EXPIRED, PasswordPolicyError(ctrl.error).prettyPrint()
+ elif ctrl.error == 1: # accountLocked
+ return constants.NSLCD_PAM_ACCT_EXPIRED, PasswordPolicyError(ctrl.error).prettyPrint()
+ elif ctrl.error == 2: # changeAfterReset
+ return constants.NSLCD_PAM_NEW_AUTHTOK_REQD, 'Password change is needed after reset'
+ elif ctrl.error:
+ return constants.NSLCD_PAM_PERM_DENIED, PasswordPolicyError(ctrl.error).prettyPrint()
+ elif ctrl.timeBeforeExpiration is not None:
+ return constants.NSLCD_PAM_NEW_AUTHTOK_REQD, 'Password will expire in %d seconds' % ctrl.timeBeforeExpiration
+ elif ctrl.graceAuthNsRemaining is not None:
+ return constants.NSLCD_PAM_NEW_AUTHTOK_REQD, 'Password expired, %d grace logins left' % ctrl.graceAuthNsRemaining
# perform search for own object (just to do any kind of search)
- res = conn.search_s(userdn, ldap.SCOPE_BASE, '(objectClass=*)', ['dn', ])
- for entry in res:
+ results = conn.search_s(userdn, ldap.SCOPE_BASE, '(objectClass=*)', ['dn', ])
+ for entry in results:
if entry[0] == userdn:
- return
+ return constants.NSLCD_PAM_SUCCESS, ''
+ # if our DN wasn't found raise an error to signal bind failure
raise ldap.NO_SUCH_OBJECT()
@@ -116,18 +138,21 @@ class PAMAuthenticationRequest(PAMRequest):
password = parameters['password']
# try authentication
try:
- try_bind(binddn, password)
+ authz, msg = try_bind(userdn, password)
except ldap.INVALID_CREDENTIALS, e:
try:
msg = e[0]['desc']
except:
msg = str(e)
logging.debug('bind failed: %s', msg)
- self.write(parameters['username'], constants.NSLCD_PAM_AUTH_ERR, msg)
+ self.write(parameters['username'], authc=constants.NSLCD_PAM_AUTH_ERR, msg=msg)
return
- logging.debug('bind successful')
+ if authz != constants.NSLCD_PAM_SUCCESS:
+ logging.warning('%s: %s: %s', userdn, parameters['username'], msg)
+ else:
+ logging.debug('bind successful')
# FIXME: perform shadow attribute checks with check_shadow()
- self.write(parameters['username'])
+ self.write(parameters['username'], authz=authz, msg=msg)
class PAMAuthorisationRequest(PAMRequest):