def create_connection(authtype=None, server=None, user=None, password=None, auto_bind=False, client_strategy=ldap3.SYNC, check_names=True, auto_referrals=False): authentication = None if not user: authentication = ldap3.ANONYMOUS if authtype == AUTHTYPE.SIMPLE: if not authentication: authentication = ldap3.SIMPLE l = ldap3.Connection(server, user=user, password=to_utf8(password), auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.NTLM: # pragma: no cover if not authentication: authentication = ldap3.NTLM l = ldap3.Connection(server, user=user, password=to_utf8(password), auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.SASL_DIGEST_MD5: # pragma: no cover if not authentication: authentication = ldap3.SASL sasl_credentials = (str(user), str(password)) l = ldap3.Connection(server, sasl_mechanism="DIGEST-MD5", sasl_credentials=sasl_credentials, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, auto_referrals=auto_referrals) else: raise Exception("Authtype {0!s} not supported".format(authtype)) return l
def derive_key(xml, password): """ Derive the encryption key from the password with the parameters given in the XML soup. :param xml: The XML :param password: the password :return: The derived key, hexlified """ if not password: raise ImportException("The XML KeyContainer specifies a derived " "encryption key, but no password given!") keymeth = xml.keycontainer.encryptionkey.derivedkey.keyderivationmethod derivation_algo = keymeth["algorithm"].split("#")[-1] if derivation_algo.lower() != "pbkdf2": raise ImportException("We only support PBKDF2 as Key derivation " "function!") salt = keymeth.find("salt").text.strip() keylength = keymeth.find("keylength").text.strip() rounds = keymeth.find("iterationcount").text.strip() r = pbkdf2_hmac('sha1', to_utf8(password), base64.b64decode(salt), rounds=int(rounds), keylen=int(keylength)) return binascii.hexlify(r)
def encryptPassword(password): from privacyidea.lib.utils import to_utf8 hsm = _get_hsm() try: ret = hsm.encrypt_password(to_utf8(password)) except Exception as exx: # pragma: no cover log.warning(exx) ret = "FAILED TO ENCRYPT PASSWORD!" return ret
def checkPass(self, uid, password): """ This function checks the password for a given uid. - returns true in case of success - false if password does not match """ if self.authtype == AUTHTYPE.NTLM: # pragma: no cover # fetch the PreWindows 2000 Domain from the self.binddn # which would be of the format DOMAIN\username and compose the # bind_user to DOMAIN\sAMAcountName domain_name = self.binddn.split('\\')[0] uinfo = self.getUserInfo(uid) # In fact we need the sAMAccountName. If the username mapping is # another attribute than the sAMAccountName the authentication # will fail! bind_user = "******".format(domain_name, uinfo.get("username")) else: bind_user = self._getDN(uid) server_pool = self.get_serverpool(self.uri, self.timeout, get_info=ldap3.NONE, tls_context=self.tls_context) password = to_utf8(password) try: log.debug("Authtype: {0!r}".format(self.authtype)) log.debug("user : {0!r}".format(bind_user)) # Whatever happens. If we have an empty bind_user, we must break # since we must avoid anonymous binds! if not bind_user or len(bind_user) < 1: raise Exception("No valid user. Empty bind_user.") l = self.create_connection(authtype=self.authtype, server=server_pool, user=bind_user, password=password, receive_timeout=self.timeout, auto_referrals=not self.noreferrals) l.open() r = l.bind() log.debug("bind result: {0!r}".format(r)) if not r: raise Exception("Wrong credentials") log.debug("bind seems successful.") l.unbind() log.debug("unbind successful.") except Exception as e: log.warning( "failed to check password for {0!r}/{1!r}: {2!r}".format( uid, bind_user, e)) log.debug(traceback.format_exc()) return False return True
def test_25_encodings(self): u = u'Hello Wörld' b = b'Hello World' self.assertEquals(to_utf8(None), None) self.assertEquals(to_utf8(u), u.encode('utf8')) self.assertEquals(to_utf8(b), b) self.assertEquals(to_unicode(u), u) self.assertEquals(to_unicode(b), b.decode('utf8')) self.assertEquals(to_unicode(None), None) self.assertEquals(to_unicode(10), 10) self.assertEquals(to_bytes(u), u.encode('utf8')) self.assertEquals(to_bytes(b), b) self.assertEquals(to_bytes(10), 10) self.assertEquals(to_byte_string(u), u.encode('utf8')) self.assertEquals(to_byte_string(b), b) self.assertEquals(to_byte_string(10), b'10')
def getResolverId(self): """ Returns the resolver Id This should be an Identifier of the resolver, preferable the type and the name of the resolver. """ # Take the following parts, join them with the NULL byte and return # the hexlified SHA-1 digest id_parts = (to_utf8(self.connect_string), str(self.pool_size), str(self.pool_recycle), str(self.pool_timeout)) resolver_id = binascii.hexlify( hashlib.sha1("\x00".join(id_parts)).digest()) return "sql." + resolver_id
def getResolverId(self): """ Returns the resolver Id This should be an Identifier of the resolver, preferable the type and the name of the resolver. """ # Take the following parts, join them with the NULL byte and return # the hexlified SHA-1 digest id_parts = (to_utf8(self.connect_string), str(self.pool_size), str(self.pool_recycle), str(self.pool_timeout)) resolver_id = binascii.hexlify(hashlib.sha1("\x00".join(id_parts)).digest()) return "sql." + resolver_id
def checkPass(self, uid, password): """ This function checks the password for a given uid. - returns true in case of success - false if password does not match """ if self.authtype == AUTHTYPE.NTLM: # pragma: no cover # fetch the PreWindows 2000 Domain from the self.binddn # which would be of the format DOMAIN\username and compose the # bind_user to DOMAIN\sAMAcountName domain_name = self.binddn.split('\\')[0] uinfo = self.getUserInfo(uid) # In fact we need the sAMAccountName. If the username mapping is # another attribute than the sAMAccountName the authentication # will fail! bind_user = "******" % (domain_name, uinfo.get("username")) else: bind_user = self._getDN(uid) server_pool = self.get_serverpool(self.uri, self.timeout) password = to_utf8(password) try: log.debug("Authtype: %s" % self.authtype) log.debug("user : %s" % bind_user) # Whatever happens. If we have an empty bind_user, we must break # since we must avoid anonymous binds! if not bind_user or len(bind_user) < 1: raise Exception("No valid user. Empty bind_user.") l = self.create_connection(authtype=self.authtype, server=server_pool, user=bind_user, password=password, auto_referrals=not self.noreferrals) l.open() r = l.bind() log.debug("bind result: %s" % r) if not r: raise Exception("Wrong credentials") log.debug("bind seems successful.") l.unbind() log.debug("unbind successful.") except Exception as e: log.warning("failed to check password for %r/%r: %r" % (uid, bind_user, e)) return False return True
def derive_key(xml, password): """ Derive the encryption key from the password with the parameters given in the XML soup. :param xml: The XML :param password: the password :return: The derived key, hexlified """ if not password: raise ImportException("The XML KeyContainer specifies a derived " "encryption key, but no password given!") keymeth = xml.keycontainer.encryptionkey.derivedkey.keyderivationmethod derivation_algo = keymeth["algorithm"].split("#")[-1] if derivation_algo.lower() != "pbkdf2": raise ImportException("We only support PBKDF2 as Key derivation " "function!") salt = keymeth.find("salt").text.strip() keylength = keymeth.find("keylength").text.strip() rounds = keymeth.find("iterationcount").text.strip() r = pbkdf2(to_utf8(password), base64.b64decode(salt), int(rounds), int(keylength)) return binascii.hexlify(r)
def getUserInfo(self, userId): """ This function returns all user info for a given userid/object. :param userId: The userid of the object :type userId: string :return: A dictionary with the keys defined in self.userinfo :rtype: dict """ ret = {} self._bind() if self.uidtype.lower() == "dn": # encode utf8, so that also german ulauts work in the DN self.l.search(search_base=to_utf8(userId), search_scope=self.scope, search_filter="(&" + self.searchfilter + ")", attributes=self.userinfo.values()) else: if self.uidtype == "objectGUID": userId = uuid.UUID("{{{0!s}}}".format(userId)).bytes_le userId = escape_bytes(userId) filter = "(&{0!s}({1!s}={2!s}))".format(self.searchfilter, self.uidtype, userId) self.l.search(search_base=self.basedn, search_scope=self.scope, search_filter=filter, attributes=self.userinfo.values()) r = self.l.response r = self._trim_result(r) if len(r) > 1: # pragma: no cover raise Exception( "Found more than one object for uid {0!r}".format(userId)) for entry in r: attributes = entry.get("attributes") ret = self._ldap_attributes_to_user_object(attributes) return ret
def getUserInfo(self, userId): """ This function returns all user info for a given userid/object. :param userId: The userid of the object :type userId: string :return: A dictionary with the keys defined in self.userinfo :rtype: dict """ ret = {} self._bind() if self.uidtype.lower() == "dn": # encode utf8, so that also german ulauts work in the DN self.l.search(search_base=to_utf8(userId), search_scope=self.scope, search_filter="(&" + self.searchfilter + ")", attributes=self.userinfo.values()) else: if self.uidtype == "objectGUID": userId = uuid.UUID("{{{0!s}}}".format(userId)).bytes_le userId = escape_bytes(userId) filter = "(&{0!s}({1!s}={2!s}))".format(self.searchfilter, self.uidtype, userId) self.l.search(search_base=self.basedn, search_scope=self.scope, search_filter=filter, attributes=self.userinfo.values()) r = self.l.response r = self._trim_result(r) if len(r) > 1: # pragma: no cover raise Exception("Found more than one object for uid {0!r}".format(userId)) for entry in r: attributes = entry.get("attributes") ret = self._ldap_attributes_to_user_object(attributes) return ret
def testconnection(cls, param): """ This function lets you test the to be saved LDAP connection. This is taken from controllers/admin.py :param param: A dictionary with all necessary parameter to test the connection. :type param: dict :return: Tuple of success and a description :rtype: (bool, string) Parameters are: BINDDN, BINDPW, LDAPURI, TIMEOUT, LDAPBASE, LOGINNAMEATTRIBUTE, LDAPSEARCHFILTER, LDAPFILTER, USERINFO, SIZELIMIT, NOREFERRALS, CACERTIFICATE, AUTHTYPE """ success = False uidtype = param.get("UIDTYPE") try: server_pool = cls.get_serverpool(param.get("LDAPURI"), float(param.get("TIMEOUT", 5))) l = cls.create_connection(authtype=param.get("AUTHTYPE", AUTHTYPE.SIMPLE), server=server_pool, user=param.get("BINDDN"), password=to_utf8(param.get("BINDPW")), auto_referrals=not param.get( "NOREFERRALS")) l.open() #log.error("LDAP Server Pool States: %s" % server_pool.pool_states) if not l.bind(): raise Exception("Wrong credentials") # create searchattributes attributes = yaml.load(param["USERINFO"]).values() if uidtype.lower() != "dn": attributes.append(str(uidtype)) # search for users... g = l.extend.standard.paged_search( search_base=param["LDAPBASE"], search_filter="(&" + param["LDAPSEARCHFILTER"] + ")", search_scope=param.get("SCOPE") or ldap3.SUBTREE, attributes=attributes, paged_size=100, generator=True) # returns a generator of dictionaries count = 0 uidtype_count = 0 for entry in g: try: userid = cls._get_uid(entry, uidtype) count += 1 if userid: uidtype_count += 1 except Exception as exx: # pragma: no cover log.warning("Error during fetching LDAP objects:" " {0!r}".format(exx)) log.debug("{0!s}".format(traceback.format_exc())) if uidtype_count < count: # pragma: no cover desc = _("Your LDAP config found %i user objects, but only %i " "with the specified uidtype" % (count, uidtype_count)) else: desc = _("Your LDAP config seems to be OK, %i user objects " "found.") % count l.unbind() success = True except Exception as e: desc = "{0!r}".format(e) return success, desc
def create_connection(authtype=None, server=None, user=None, password=None, auto_bind=False, client_strategy=ldap3.SYNC, check_names=True, auto_referrals=False, receive_timeout=5): """ Create a connection to the LDAP server. :param authtype: :param server: :param user: :param password: :param auto_bind: :param client_strategy: :param check_names: :param auto_referrals: :param receive_timeout: At the moment we do not use this, since receive_timeout is not supported by ldap3 < 2. :return: """ authentication = None if not user: authentication = ldap3.ANONYMOUS if authtype == AUTHTYPE.SIMPLE: if not authentication: authentication = ldap3.SIMPLE l = ldap3.Connection(server, user=user, password=to_utf8(password), auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.NTLM: # pragma: no cover if not authentication: authentication = ldap3.NTLM l = ldap3.Connection(server, user=user, password=to_utf8(password), auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.SASL_DIGEST_MD5: # pragma: no cover if not authentication: authentication = ldap3.SASL sasl_credentials = (str(user), str(password)) l = ldap3.Connection(server, sasl_mechanism="DIGEST-MD5", sasl_credentials=sasl_credentials, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) else: raise Exception("Authtype {0!s} not supported".format(authtype)) return l
def testconnection(cls, param): """ This function lets you test the to be saved LDAP connection. This is taken from controllers/admin.py :param param: A dictionary with all necessary parameter to test the connection. :type param: dict :return: Tuple of success and a description :rtype: (bool, string) Parameters are: BINDDN, BINDPW, LDAPURI, TIMEOUT, LDAPBASE, LOGINNAMEATTRIBUTE, LDAPSEARCHFILTER, LDAPFILTER, USERINFO, SIZELIMIT, NOREFERRALS, CACERTIFICATE, AUTHTYPE """ success = False uidtype = param.get("UIDTYPE") try: server_pool = cls.get_serverpool(param.get("LDAPURI"), float(param.get("TIMEOUT", 5))) l = cls.create_connection( authtype=param.get("AUTHTYPE", AUTHTYPE.SIMPLE), server=server_pool, user=param.get("BINDDN"), password=to_utf8(param.get("BINDPW")), auto_referrals=not param.get("NOREFERRALS")) l.open() #log.error("LDAP Server Pool States: %s" % server_pool.pool_states) if not l.bind(): raise Exception("Wrong credentials") # create searchattributes attributes = yaml.load(param["USERINFO"]).values() if uidtype.lower() != "dn": attributes.append(str(uidtype)) # search for users... l.search(search_base=param["LDAPBASE"], search_scope=param.get("SCOPE") or ldap3.SUBTREE, search_filter="(&" + param["LDAPSEARCHFILTER"] + ")", attributes=attributes) r = l.response count = len(r) uidtype_count = 0 for entry in r: userid = cls._get_uid(entry, uidtype) if userid: uidtype_count += 1 if uidtype_count < count: # pragma: no cover desc = _("Your LDAP config found %i user objects, but only %i " "with the specified uidtype" % (count, uidtype_count)) else: desc = _("Your LDAP config seems to be OK, %i user objects found.")\ % count l.unbind() success = True except Exception as e: desc = "%r" % e return success, desc
def create_connection(authtype=None, server=None, user=None, password=None, auto_bind=False, client_strategy=ldap3.SYNC, check_names=True, auto_referrals=False, receive_timeout=5, start_tls=False): """ Create a connection to the LDAP server. :param authtype: :param server: :param user: :param password: :param auto_bind: :param client_strategy: :param check_names: :param auto_referrals: :param receive_timeout: At the moment we do not use this, since receive_timeout is not supported by ldap3 < 2. :return: """ authentication = None if not user: authentication = ldap3.ANONYMOUS if authtype == AUTHTYPE.SIMPLE: if not authentication: authentication = ldap3.SIMPLE # SIMPLE works with passwords as UTF8 and unicode l = ldap3.Connection(server, user=user, password=password, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.NTLM: # pragma: no cover if not authentication: authentication = ldap3.NTLM # NTLM requires the password to be unicode l = ldap3.Connection(server, user=user, password=password, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.SASL_DIGEST_MD5: # pragma: no cover if not authentication: authentication = ldap3.SASL password = to_utf8(password) sasl_credentials = (str(user), str(password)) l = ldap3.Connection(server, sasl_mechanism="DIGEST-MD5", sasl_credentials=sasl_credentials, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) else: raise Exception("Authtype {0!s} not supported".format(authtype)) if start_tls: l.open(read_server_info=False) log.debug("Doing start_tls") r = l.start_tls(read_server_info=False) return l
def testconnection(cls, param): """ This function lets you test the to be saved LDAP connection. This is taken from controllers/admin.py :param param: A dictionary with all necessary parameter to test the connection. :type param: dict :return: Tuple of success and a description :rtype: (bool, string) Parameters are: BINDDN, BINDPW, LDAPURI, TIMEOUT, LDAPBASE, LOGINNAMEATTRIBUTE, LDAPSEARCHFILTER, LDAPFILTER, USERINFO, SIZELIMIT, NOREFERRALS, CACERTIFICATE, AUTHTYPE """ success = False uidtype = param.get("UIDTYPE") timeout = float(param.get("TIMEOUT", 5)) try: server_pool = cls.get_serverpool(param.get("LDAPURI"), timeout) l = cls.create_connection(authtype=param.get("AUTHTYPE", AUTHTYPE.SIMPLE), server=server_pool, user=param.get("BINDDN"), password=to_utf8(param.get("BINDPW")), receive_timeout=timeout, auto_referrals=not param.get( "NOREFERRALS")) l.open() #log.error("LDAP Server Pool States: %s" % server_pool.pool_states) if not l.bind(): raise Exception("Wrong credentials") # create searchattributes attributes = yaml.load(param["USERINFO"]).values() if uidtype.lower() != "dn": attributes.append(str(uidtype)) # search for users... g = l.extend.standard.paged_search( search_base=param["LDAPBASE"], search_filter="(&" + param["LDAPSEARCHFILTER"] + ")", search_scope=param.get("SCOPE") or ldap3.SUBTREE, attributes=attributes, paged_size=100, generator=True) # returns a generator of dictionaries count = 0 uidtype_count = 0 for entry in g: try: userid = cls._get_uid(entry, uidtype) count += 1 if userid: uidtype_count += 1 except Exception as exx: # pragma: no cover log.warning("Error during fetching LDAP objects:" " {0!r}".format(exx)) log.debug("{0!s}".format(traceback.format_exc())) if uidtype_count < count: # pragma: no cover desc = _("Your LDAP config found %i user objects, but only %i " "with the specified uidtype" % (count, uidtype_count)) else: desc = _("Your LDAP config seems to be OK, %i user objects " "found.") % count l.unbind() success = True except Exception as e: desc = "{0!r}".format(e) return success, desc