From b1e84397112cf53b17d9c52277daf613b65442de Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 23 Mar 2018 12:28:31 +0100 Subject: [PATCH] Fix parsing of PPolicyControl ASN.1 structure Password policy control decoder failed to handle graceAuthNsRemaining correctly. The warning.getComponentByName('timeBeforeExpiration') call materialized the time before expiration CHOICE element. The fixed implementation uses pyasn1's dict interface to check which CHOICE element is set. Closes: https://github.com/python-ldap/python-ldap/issues/192 See: https://github.com/python-ldap/python-ldap/issues/193 Signed-off-by: Christian Heimes --- Lib/ldap/controls/ppolicy.py | 35 +++++++++++++++----------------- Tests/t_ldap_controls_ppolicy.py | 33 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 Tests/t_ldap_controls_ppolicy.py diff --git a/Lib/ldap/controls/ppolicy.py b/Lib/ldap/controls/ppolicy.py index aa761f34..67efe3ad 100644 --- a/Lib/ldap/controls/ppolicy.py +++ b/Lib/ldap/controls/ppolicy.py @@ -11,13 +11,13 @@ ] # Imports from python-ldap 2.4+ -import ldap.controls -from ldap.controls import RequestControl,ResponseControl,ValueLessRequestControl,KNOWN_RESPONSE_CONTROLS +from ldap.controls import ( + ResponseControl, ValueLessRequestControl, KNOWN_RESPONSE_CONTROLS +) # Imports from pyasn1 from pyasn1.type import tag,namedtype,namedval,univ,constraint -from pyasn1.codec.ber import encoder,decoder -from pyasn1_modules.rfc2251 import LDAPDN +from pyasn1.codec.der import decoder class PasswordPolicyWarning(univ.Choice): @@ -70,25 +70,22 @@ def __init__(self,criticality=False): def decodeControlValue(self,encodedControlValue): ppolicyValue,_ = decoder.decode(encodedControlValue,asn1Spec=PasswordPolicyResponseValue()) + self.timeBeforeExpiration = None + self.graceAuthNsRemaining = None + self.error = None + warning = ppolicyValue.getComponentByName('warning') - if not warning.hasValue(): - self.timeBeforeExpiration,self.graceAuthNsRemaining = None,None - else: - timeBeforeExpiration = warning.getComponentByName('timeBeforeExpiration') - if timeBeforeExpiration.hasValue(): - self.timeBeforeExpiration = int(timeBeforeExpiration) - else: - self.timeBeforeExpiration = None - graceAuthNsRemaining = warning.getComponentByName('graceAuthNsRemaining') - if graceAuthNsRemaining.hasValue(): - self.graceAuthNsRemaining = int(graceAuthNsRemaining) - else: - self.graceAuthNsRemaining = None + if warning.hasValue(): + if 'timeBeforeExpiration' in warning: + self.timeBeforeExpiration = int( + warning.getComponentByName('timeBeforeExpiration')) + if 'graceAuthNsRemaining' in warning: + self.graceAuthNsRemaining = int( + warning.getComponentByName('graceAuthNsRemaining')) + error = ppolicyValue.getComponentByName('error') if error.hasValue(): self.error = int(error) - else: - self.error = None KNOWN_RESPONSE_CONTROLS[PasswordPolicyControl.controlType] = PasswordPolicyControl diff --git a/Tests/t_ldap_controls_ppolicy.py b/Tests/t_ldap_controls_ppolicy.py new file mode 100644 index 00000000..8644e563 --- /dev/null +++ b/Tests/t_ldap_controls_ppolicy.py @@ -0,0 +1,33 @@ +import os +import unittest + +# Switch off processing .ldaprc or ldap.conf before importing _ldap +os.environ['LDAPNOINIT'] = '1' + +from ldap.controls import ppolicy + + +PP_GRACEAUTH = b'0\x84\x00\x00\x00\t\xa0\x84\x00\x00\x00\x03\x81\x01\x02' +PP_TIMEBEFORE = b'0\x84\x00\x00\x00\t\xa0\x84\x00\x00\x00\x03\x80\x012' + + +class TestControlsPPolicy(unittest.TestCase): + def assertPPolicy(self, pp, timeBeforeExpiration=None, + graceAuthNsRemaining=None, error=None): + self.assertEqual(pp.timeBeforeExpiration, timeBeforeExpiration) + self.assertEqual(pp.graceAuthNsRemaining, graceAuthNsRemaining) + self.assertEqual(pp.error, error) + + def test_ppolicy_graceauth(self): + pp = ppolicy.PasswordPolicyControl() + pp.decodeControlValue(PP_GRACEAUTH) + self.assertPPolicy(pp, graceAuthNsRemaining=2) + + def test_ppolicy_timebefore(self): + pp = ppolicy.PasswordPolicyControl() + pp.decodeControlValue(PP_TIMEBEFORE) + self.assertPPolicy(pp, timeBeforeExpiration=50) + + +if __name__ == '__main__': + unittest.main()