From 9c5f9dd1eaaa8d7755224ae5d3864234aeb7e5ca Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Thu, 10 Nov 2022 18:54:44 +0100 Subject: [PATCH] Using LdapPasswordHandler in bin/set-ldap-password --- lib/pp_admintools/app/set_ldap_password.py | 177 ++++----------------- lib/pp_admintools/handler/ldap_password.py | 13 +- 2 files changed, 44 insertions(+), 146 deletions(-) diff --git a/lib/pp_admintools/app/set_ldap_password.py b/lib/pp_admintools/app/set_ldap_password.py index e40f06c..b2aa9e0 100644 --- a/lib/pp_admintools/app/set_ldap_password.py +++ b/lib/pp_admintools/app/set_ldap_password.py @@ -17,15 +17,6 @@ import getpass from ldap3 import MODIFY_REPLACE from ldap3.core.exceptions import LDAPBindError -import passlib.apps - -HAS_CRACKLIB = False -try: - import cracklib - HAS_CRACKLIB = True -except ImportError: - pass - # Own modules # from fb_tools.common import to_bool, is_sequence, pp from fb_tools.common import is_sequence @@ -38,7 +29,11 @@ from .ldap import LdapAppError, FatalLDAPError from .ldap import BaseLdapApplication from .ldap import PasswordFileOptionAction -__version__ = '0.6.6' +from ..handler.ldap_password import WrongPwdSchemaError +from ..handler.ldap_password import LdapPasswordHandler +from ..handler.ldap_password import HAS_CRACKLIB + +__version__ = '0.7.1' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -69,80 +64,10 @@ class SetLdapPasswordApplication(BaseLdapApplication): except KeyError: pass - possible_schemes = ( - 'ldap_des_crypt', - 'ldap_md5', - 'ldap_md5_crypt', - 'ldap_salted_md5', - 'ldap_sha1', - 'ldap_sha1_crypt', - 'ldap_salted_sha1', - 'ldap_sha256_crypt', - 'ldap_salted_sha256', - 'ldap_sha512_crypt', - 'ldap_salted_sha512', - 'ldap_pbkdf2_sha512', - ) - - ldap_context = passlib.apps.ldap_context - available_schemes = [] - - schema_ids = { - 'ldap_des_crypt': 'CRYPT', - 'ldap_md5': 'MD5', - 'ldap_md5_crypt': 'CRYPT-MD5', - 'ldap_salted_md5': 'SMD5', - 'ldap_sha1': 'SHA', - 'ldap_sha1_crypt': 'SHA-CRYPT', - 'ldap_salted_sha1': 'SSHA', - 'ldap_sha256_crypt': 'CRYPT-SHA256', - 'ldap_salted_sha256': 'SSHA256', - 'ldap_sha512_crypt': 'CRYPT-SHA512', - 'ldap_salted_sha512': 'SSHA512', - 'ldap_pbkdf2_sha512': 'PBKDF2_SHA512', - } - - schema_description = { - 'ldap_des_crypt': _('The ancient and notorious 3 DES crypt method.'), - 'ldap_md5': _('Pure {} hashing method.').format('MD5'), - 'ldap_md5_crypt': _("A {} based hashing algorithm.").format('MD5'), - 'ldap_salted_md5': _("Salted {} hashing method.").format('MD5'), - 'ldap_sha1': _('Pure {} hashing method.').format('SHA-1'), - 'ldap_sha1_crypt': _("A {} based hashing algorithm.").format('SHA-1'), - 'ldap_salted_sha1': _("Salted {} hashing method.").format('SHA-1'), - 'ldap_sha256_crypt': _("A {} based hashing algorithm.").format('SHA-256'), - 'ldap_salted_sha256': _("Salted {} hashing method.").format('SHA-256'), - 'ldap_sha512_crypt': _("A {} based hashing algorithm.").format('SHA-512'), - 'ldap_salted_sha512': _("Salted {} hashing method.").format('SHA-512'), - 'ldap_pbkdf2_sha512': _( - "A hashing method derived from {} with additional computing rounds.").format( - 'SHA-512'), - } - - passlib_context = None - default_schema = 'ldap_sha512_crypt' - default_schema_id = 'CRYPT-SHA512' - default_pbkdf2_rounds = 30000 - - # ------------------------------------------------------------------------- - @classmethod - def init_pass_schemes(cls): - - cls.available_schemes = [] - all_handlers = passlib.registry.list_crypt_handlers() - - for schema in cls.possible_schemes: - if schema in all_handlers: - cls.available_schemes.append(schema) - - cls.passlib_context = passlib.context.CryptContext( - schemes=cls.available_schemes, ldap_pbkdf2_sha512__rounds=cls.default_pbkdf2_rounds) - cls.passlib_context.update(default=cls.default_schema) - # ------------------------------------------------------------------------- def __init__(self, appname=None, base_dir=None): - self.init_pass_schemes() + LdapPasswordHandler.init_pass_schemes() self.current_password = None self.need_current_password = False @@ -152,12 +77,13 @@ class SetLdapPasswordApplication(BaseLdapApplication): self.new_password = None self.user_uid = None self.user_dn = None - self.schema = self.default_schema - self.schema_id = self.default_schema_id self.no_cracklib = False self.user_connection = None + self.pwd_handler = LdapPasswordHandler( + appname=appname, base_dir=base_dir, initialized=False) + my_appname = self.get_generic_appname(appname) desc = _( @@ -237,12 +163,13 @@ class SetLdapPasswordApplication(BaseLdapApplication): schema_list = [] def_schema = '' - for method in self.available_schemes: - schema_id = self.schema_ids[method] + for method in LdapPasswordHandler.available_schemes: + schema_id = LdapPasswordHandler.schema_ids[method] schema_list.append(schema_id) - if method == self.default_schema: + if method == LdapPasswordHandler.default_schema: def_schema = schema_id schema_list.append('list') + schema_list.append('help') help_txt1 = _( "The schema (hashing method) to use to hash the new password. It is possible to give " @@ -291,22 +218,24 @@ class SetLdapPasswordApplication(BaseLdapApplication): msg = "Given args:\n" + pp(self.args.__dict__) LOG.debug(msg) + self.pwd_handler.verbose = self.verbose + self.pwd_handler.simulate = self.simulate + self.pwd_handler.force = self.force + self.pwd_handler.terminal_has_colors = self.terminal_has_colors + self.pwd_handler.initialized = True + self.no_cracklib = getattr(self.args, 'no_cracklib', False) given_schema = getattr(self.args, 'schema', None) if given_schema: - if given_schema == 'list': - self._show_hashing_schemes() + if given_schema in ('list', 'help'): + self.pwd_handler.show_hashing_schemes() self.exit(0) return - for method in self.available_schemes: - schema_id = self.schema_ids[method] - LOG.debug("Testing for {m!r} ({s}) ...".format(m=method, s=schema_id)) - if schema_id == given_schema: - self.passlib_context.update(default=method) - self.schema = method - self.schema_id = schema_id - break + try: + self.pwd_handler.set_schema_by_id(given_schema) + except WrongPwdSchemaError as e: + self.exit(5, str(e)) given_user = getattr(self.args, 'user', None) if given_user: @@ -351,36 +280,6 @@ class SetLdapPasswordApplication(BaseLdapApplication): LOG.debug("User bind: LDAP instance is readonly or not as admin.") self.do_user_bind = True - # ------------------------------------------------------------------------- - def _show_hashing_schemes(self): - - max_len_schema = 1 - for method in self.available_schemes: - schema_id = self.schema_ids[method] - if len(schema_id) > max_len_schema: - max_len_schema = len(schema_id) - - title = _("Usable Hashing schemes:") - print(title) - print('-' * len(title)) - print() - - for method in self.available_schemes: - schema_id = self.schema_ids[method] - desc = self.schema_description[method] - if 'pbkdf2' in method: - desc += ' ' + _( - "This schema cannot be used for authentication on a " - "current freeradius server.") - if method == self.schema: - desc += ' ' + _("This is the default schema.") - - line = ' * {id:<{max_len}} - '.format(id=schema_id, max_len=max_len_schema) - line += desc - print(line) - - print() - # ------------------------------------------------------------------------- def pre_run(self): @@ -417,24 +316,14 @@ class SetLdapPasswordApplication(BaseLdapApplication): self.new_password = self.get_password( first_prompt, second_prompt, may_empty=False, repeat=True) - if HAS_CRACKLIB: - if self.no_cracklib: - msg = _("Checking the quality of the new password was disabled.") - LOG.warn(msg) - else: - LOG.info(_("Testing quality of new password ...")) - try: - cracklib.VeryFascistCheck(self.new_password) - except ValueError as e: - msg = _("Quality of the new password is not sufficient:") + ' ' + str(e) - LOG.error(msg) - self.exit(1) - LOG.debug("The quality of the new password seems to be sufficient.") - else: - msg = _( - "Cannot testing the quality of the new password, because the " - "Python module {!r} is not installed.").format('cracklib') + if self.no_cracklib: + msg = _("Checking the quality of the new password was disabled.") LOG.warn(msg) + else: + if self.pwd_handler.check_password_quality(self.new_password): + LOG.debug("The quality of the new password seems to be sufficient.") + else: + self.exit(1) super(SetLdapPasswordApplication, self).pre_run() @@ -576,7 +465,7 @@ class SetLdapPasswordApplication(BaseLdapApplication): print(msg) LOG.debug(_("Used schema: {!r}.").format(self.schema)) - hashed_passwd = self.passlib_context.hash(self.new_password, self.schema) + hashed_passwd = self.pwd_handler.get_hash(self.new_password, self.schema) msg = _("New password hash: '{}'.").format(self.colored(hashed_passwd, 'CYAN')) print(msg) diff --git a/lib/pp_admintools/handler/ldap_password.py b/lib/pp_admintools/handler/ldap_password.py index dc3115d..0c5dbfd 100644 --- a/lib/pp_admintools/handler/ldap_password.py +++ b/lib/pp_admintools/handler/ldap_password.py @@ -143,15 +143,21 @@ class LdapPasswordHandler(HandlingObject): # ------------------------------------------------------------------------- def __init__( self, appname=None, verbose=0, version=__version__, base_dir=None, - simulate=None, force=None, assumed_answer=None, + simulate=False, force=False, assumed_answer=None, terminal_has_colors=False, initialized=False): + self.schema = self.default_schema + self.schema_id = self.default_schema_id + super(LdapPasswordHandler, self).__init__( appname=appname, verbose=verbose, version=version, base_dir=base_dir, simulate=simulate, force=force, assumed_answer=assumed_answer, terminal_has_colors=terminal_has_colors, initialized=False, ) + if initialized: + self.initialized = True + # ------------------------------------------------------------------------- def as_dict(self, short=True): """ @@ -268,7 +274,10 @@ class LdapPasswordHandler(HandlingObject): # ------------------------------------------------------------------------- def get_hash(self, password, schema=None): - hashed_passwd = self.passlib_context.hash(password, self.schema) + if not schema: + schema = self.schema + + hashed_passwd = self.passlib_context.hash(password) return hashed_passwd # ------------------------------------------------------------------------- -- 2.39.5