def _ldap_bind(self): """ Set LDAP binding """ #ldap.set_option(ldap.OPT_DEBUG_LEVEL, 4095) if self.auth_type == 'gssapi' : if not ldap.SASL_AVAIL: raise AnsibleLookupError("Cannot use auth=gssapi when SASL is not configured with the local LDAP install") # if self.username or self.password: # raise AnsibleError("Explicit credentials are not supported when auth_type='gssapi'. Call kinit outside of Ansible") elif self.auth_type == 'simple' and not (self.username and self.password): raise AnsibleError("The username and password values are required when auth_type=simple") else: if self.username and self.password: pass # self.auth_type = 'simple' if ldap.SASL_AVAIL: self.auth_type == 'gssapi' else: raise AnsibleError("Invalid auth_type value '%s': expecting either 'gssapi', or 'simple'" % self.auth_type) if ldapurl.isLDAPUrl(self.domain): ldap_url = ldapurl.LDAPUrl(ldapUrl=self.domain) else: self.port = self.port if self.port else 389 if self.scheme == 'ldap' else 636 ldap_url = ldapurl.LDAPUrl(hostport="%s:%d" % (self.domain, self.port), urlscheme=self.scheme) if self.validate_certs is False : ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW) if not ldap.TLS_AVAIL and conn_url.urlscheme == 'ldaps': raise AnsibleLookupError("Cannot use TLS as the local LDAP installed has not been configured to support it") conn_url = ldap_url.initializeUrl() #self.ldap_session = MyLDAPObject(conn_url, trace_level=3) # higher trace levels self.ldap_session = MyLDAPObject(conn_url) self.ldap_session.page_size = 900 self.ldap_session.set_option(ldap.OPT_PROTOCOL_VERSION, 3) self.ldap_session.set_option(ldap.OPT_REFERRALS, 0) if self.auth_type == 'simple': try: self.ldap_session.bind_s(self.username, self.password, ldap.AUTH_SIMPLE) except ldap.LDAPError as err: raise AnsibleError("Failed to simple bind against LDAP host '%s': %s " % (conn_url, to_native(err))) else: cmd = ['kinit', self.username] error_code = subprocess.run(cmd, input=self.password.encode(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode if error_code > 0: raise AnsibleError("kinit failure") try: self.ldap_session.sasl_gssapi_bind_s() except ldap.AUTH_UNKNOWN as err: # The SASL GSSAPI binding is not installed, e.g. cyrus-sasl-gssapi. Give a better error message than what python-ldap provides raise AnsibleError("Failed to do a sasl bind against LDAP host '%s', the GSSAPI mech is not installed: %s" % (conn_url, to_native(err))) except ldap.LDAPError as err: raise AnsibleError("Failed to do a sasl bind against LDAP host '%s': %s" % (conn_url, to_native(err)))
def authLDAP(self, username, password): import ldap, ldapurl ld = ldapurl.LDAPUrl(self.ldapUrl) ldapConnector = ldap.open(ld.hostport.split(':')[0]) ldapConnector.protocol_version = ldap.VERSION3 ldapConnector.simple_bind(self.authdn, self.password) searchScope = ldap.SCOPE_SUBTREE retrieveAttributes = ["userPassword"] if ld.filterstr: searchFilter = "(&(%s=%s)%s)" % (ld.attrs[0],username,ld.filterstr) else: searchFilter = "%s=%s" % (ld.attrs[0],username) try: ldap_result_id = ldapConnector.search(ld.dn, searchScope, searchFilter, retrieveAttributes) result_set = [] while 1: result_type, result_data = ldapConnector.result(ldap_result_id, 0) if (result_data == []): break else: if result_type == ldap.RES_SEARCH_ENTRY: result_set.append(result_data) if not result_set: return False return result_set[0][0][1] except ldap.LDAPError, e: print "[Enamel Error] Error in LDAP user lookup." print e return False
def __init__(self, host, port, encoding, dn='', password=''): """ Connects to a LDAP server can raise ldap.LDAPError """ if not host.startswith('ldap://'): uri = 'ldap://' + host else: uri = host if port != 389: uri += ':' + str(port) log.debug('Connecting to %s', uri) ldap_conn = ldap.initialize(uri) ldap_conn.protocol_version = ldap.VERSION3 if dn != '' and password != '': log.debug('Binding %s', dn) ldap_conn.simple_bind_s(dn, password) self._ldap_url = ldapurl.LDAPUrl(uri) self._connection = ldap_conn self._encoding = encoding
def changePassword(user_identifier, old_pass, new_pass): lu = ldapurl.LDAPUrl(LDAP_URL) l = ldap.initialize(lu.initializeUrl()) l.protocol_version = ldap.VERSION3 l.simple_bind_s(LDAP_ROOT_USER_DN, LDAP_ROOT_USER_PASS) user_dn, detail = get_user_dn(l, user_identifier) if user_dn: try: output = l.passwd_s(user_dn, old_pass, new_pass) res = ResponseStatus(200, "SUCCESS", "Password changed successfully") except ldap.UNWILLING_TO_PERFORM: res = ResponseStatus(400, "OLDPASSWRONG", "Old Password not correct") except: res = ResponseStatus(400, "WENTWRONG", "Something went wrong") else: res = ResponseStatus(400, "NOUSERNAMEFOREMAIL", "No dn found for email") l.unbind_s() return res
def reset_password(user_email, new_pass): lu = ldapurl.LDAPUrl(LDAP_URL) l = ldap.initialize(lu.initializeUrl()) l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_PROTOCOL_VERSION, 3) l.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND) l.set_option(ldap.OPT_X_TLS_DEMAND, True) l.simple_bind_s(LDAP_ROOT_USER_DN, LDAP_ROOT_USER_PASS) user_dn, detail = get_user_dn(l, user_email) if not user_dn: res = ResponseStatus(400, "NOUSERNAMEFOREMAIL", "No dn found for email") else: sha1coded = hashlib.sha1(bytes(new_pass, "utf-8")).hexdigest() password_value = bytes( "{SHA}" + base64.b64encode(binascii.unhexlify(sha1coded)).decode('utf-8'), 'utf-8') add_pass = [(ldap.MOD_REPLACE, 'userPassword', [password_value])] try: l.modify_s(user_dn, add_pass) res = ResponseStatus(200, "SUCCESS", "Password changed successfully") except Exception as e: res = ResponseStatus(400, "WENTWRONG", e.args[0]) l.unbind_s() return res
def __wait_for_connection(self, timeout): lurl = ldapurl.LDAPUrl(self.uri) if lurl.urlscheme == 'ldapi': wait_for_open_socket(lurl.hostport, timeout) else: (host,port) = lurl.hostport.split(':') wait_for_open_ports(host, int(port), timeout)
def check_auth(user, password, get_dn=False): get = Environment.getInstance().config.get log = logging.getLogger(__name__) url = ldapurl.LDAPUrl(get("ldap.url")) bind_user = get('ldap.bind-user', default=None) bind_dn = get('ldap.bind-dn', default=None) bind_secret = get('ldap.bind-secret', default=None) conn = ldap.ldapobject.ReconnectLDAPObject( "%s://%s" % (url.urlscheme, url.hostport), retry_max=int(get("ldap.retry-max", default=3)), retry_delay=int(get("ldap.retry-delay", default=5))) # We only want v3 conn.protocol_version = ldap.VERSION3 # If no SSL scheme used, try TLS if get("ldap.tls", default="True").lower( ) == "true" and ldap.TLS_AVAIL and url.urlscheme != "ldaps": try: conn.start_tls_s() except ldap.PROTOCOL_ERROR as detail: log.debug("cannot use TLS, falling back to unencrypted session") try: # Get a connection if bind_dn: log.debug("starting simple bind using '%s'" % bind_dn) conn.simple_bind_s(bind_dn, bind_secret) elif bind_user: log.debug("starting SASL bind using '%s'" % bind_user) auth_tokens = ldap.sasl.digest_md5(bind_user, bind_secret) conn.sasl_interactive_bind_s("", auth_tokens) else: log.debug("starting anonymous bind") conn.simple_bind_s() # Search for the given user UID res = conn.search_s( url.dn, ldap.SCOPE_SUBTREE, filter_format( "(|(&(objectClass=registeredDevice)(deviceUUID=%s))(&(objectClass=person)(uid=%s)))", [user, user]), ['dn']) if len(res) == 1: dn = res[0][0] log.debug("starting simple bind using '%s'" % dn) conn.simple_bind_s(dn, password) return True if not get_dn else dn elif len(res) > 1: log.error("LDAP authentication failed: user %s not unique" % user) else: log.error("LDAP authentication failed: user %s not found" % user) except ldap.INVALID_CREDENTIALS as detail: log.error("LDAP authentication failed: %s" % str(detail)) return False
def removeServer(self, host, port, protocol): """ Remove a server definition from the list of servers used """ hp = '%s:%s' % (host, port) conn = ldapurl.LDAPUrl(urlscheme=protocol, hostport=hp) server_url = conn.initializeUrl() if server_url in self.servers.keys(): del self.servers[server_url]
def __init__(self, username, passwd): self.username = username self.passwd = passwd self.use_tls = False self.ldap_url = ldapurl.LDAPUrl(settings.LDAP_HOST) if self.ldap_url.urlscheme == 'ldaps': self.use_tls = True ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)
def __init__(self,ldap_url): self.ldap_url = ldapurl.LDAPUrl(ldap_url) # Open the connection self.l = ldap.ldapobject.SimpleLDAPObject( self.ldap_url.initializeUrl(),trace_level=0 ) self.stop_event = threading.Event() threading.Thread.__init__(self) self.setName(self.__class__.__name__+self.getName()[6:]) print 'Initialized',self.getName(),self.ldap_url.unparse()
def test_remove_server(self): conn = self._makeSimple() existing = list(conn.servers.values())[0] ldap_url = ldapurl.LDAPUrl(existing['url']) host, port = ldap_url.hostport.split(':') protocol = ldap_url.urlscheme conn.removeServer(host, port, protocol) self.assertEqual(len(conn.servers.values()), 0)
def urlfetch(uri, trace_level=0): """ Fetches a parsed schema entry by uri. If uri is a LDAP URL the LDAP server is queried directly. Otherwise uri is assumed to point to a LDIF file which is loaded with urllib. """ uri = uri.strip() if uri.startswith('ldap:') or uri.startswith('ldaps:') or uri.startswith( 'ldapi:'): import ldapurl ldap_url = ldapurl.LDAPUrl(uri) # This is an internal function; don't enable bytes_mode. l = ldap.initialize(ldap_url.initializeUrl(), trace_level, bytes_mode=False) l.protocol_version = ldap.VERSION3 l.simple_bind_s(ldap_url.who or '', ldap_url.cred or '') subschemasubentry_dn = l.search_subschemasubentry_s(ldap_url.dn) if subschemasubentry_dn is None: s_temp = None else: if ldap_url.attrs is None: schema_attrs = SCHEMA_ATTRS else: schema_attrs = ldap_url.attrs s_temp = l.read_subschemasubentry_s(subschemasubentry_dn, attrs=schema_attrs) l.unbind_s() del l else: import ldif from ldap.compat import urlopen ldif_file = urlopen(uri) ldif_parser = ldif.LDIFRecordList(ldif_file, max_entries=1) ldif_parser.parse() subschemasubentry_dn, s_temp = ldif_parser.all_records[0] # Work-around for mixed-cased attribute names subschemasubentry_entry = ldap.cidict.cidict() s_temp = s_temp or {} for at, av in s_temp.items(): if at in SCHEMA_CLASS_MAPPING: try: subschemasubentry_entry[at].extend(av) except KeyError: subschemasubentry_entry[at] = av # Finally parse the schema if subschemasubentry_dn != None: parsed_sub_schema = ldap.schema.SubSchema(subschemasubentry_entry) else: parsed_sub_schema = None return subschemasubentry_dn, parsed_sub_schema
def __init__(self, config_file): """ Initializes LDAP server communication wrapper with provided config file """ config = ConfigParser.ConfigParser() config.read(config_file) try: self.ldap_username = config.get('active_directory', 'username') self.ldap_password = config.get('active_directory', 'password') self.ldap_provider_url = config.get('active_directory', 'provider_url') self.ldap_dn = config.get('active_directory', 'dn') self.ldap_protocol_version = config.get('active_directory', 'protocol_version') self.ldap_enable_tls = config.get('active_directory', 'tls_enabled') self.ldap_filter = config.get('active_directory', 'ldap_filter') self.ldap_user_name = config.get('active_directory', 'ldap_user_name') self.ldap_user_surname = config.get('active_directory', 'ldap_user_surname') self.ldap_user_email = config.get('active_directory', 'ldap_user_email') self.ldap_user_uid = config.get('active_directory', 'ldap_user_uid') except ConfigParser.NoOptionError as e: print "[x] Error: Config file is not complete! Section '%s' must contain option '%s'. " \ "Check config examples at https://github.com/Oomnitza.\nExiting." % (e.section, e.option) sys.exit(2) ldap.set_option(ldap.OPT_REFERRALS, 0) ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 30) # the default LDAP protocol version - if not recognized - is v3 if self.ldap_protocol_version == '2': ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION2) else: self.ldap_protocol_version = '3' ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) try: parsed_url = ldapurl.LDAPUrl(self.ldap_provider_url) except ValueError: print "[x] Error: Invalid url to LDAP service. Check config examples at https://github.com/Oomnitza." \ "\nExiting" sys.exit(2) self.ldap_connection = ldap.initialize(parsed_url.unparse()) if self.ldap_enable_tls in ['True', 'true', '1' ] and self.ldap_protocol_version == '3': try: self.ldap_connection.start_tls_s() except ldap.PROTOCOL_ERROR: print "[x] Error: LDAP server do not support TLS connection. Change the 'tls_enabled' to 'False' if " \ "you allow insecure data transfer.\nExiting" sys.exit(2)
def test_ticket48013(topology_st): ''' Content Synchonization: Test that invalid cookies are caught ''' cookies = ('#', '##', 'a#a#a', 'a#a#1') # Enable dynamic plugins try: topology_st.standalone.modify_s( DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', b'on')]) except ldap.LDAPError as e: log.error('Failed to enable dynamic plugin! {}'.format( e.args[0]['desc'])) assert False # Enable retro changelog topology_st.standalone.plugins.enable(name=PLUGIN_RETRO_CHANGELOG) # Enbale content sync plugin topology_st.standalone.plugins.enable(name=PLUGIN_REPL_SYNC) # Set everything up ldap_url = ldapurl.LDAPUrl('ldap://%s:%s' % (HOST_STANDALONE, PORT_STANDALONE)) ldap_connection = SyncObject(ldap_url.initializeUrl()) # Authenticate try: ldap_connection.simple_bind_s(DN_DM, PASSWORD) except ldap.LDAPError as e: log.error('Login to LDAP server failed: {}'.format(e.args[0]['desc'])) assert False # Test invalid cookies for invalid_cookie in cookies: log.info('Testing cookie: %s' % invalid_cookie) try: ldap_connection.sync_search(invalid_cookie) ldap_connection.poll() log.fatal('Invalid cookie accepted!') assert False except Exception as e: log.info('Invalid cookie correctly rejected: {}'.format( e.args[0]['info'])) pass # Success log.info('Test complete')
def __init__(self, who=None, cred="", method="", auth=None): # Parse the openldap config file try: configfile = open("/etc/openldap/ldap.conf") regexBase = re.compile("^BASE\s+(.*)$") regexHost = re.compile("^URI\s+(.*)$") for line in configfile.xreadlines(): find = regexBase.search(line) if find != None: self.dnBase = find.expand(r"\1").strip() else: find = regexHost.search(line) if find != None: self.strLDAPServer = find.expand(r"\1").strip() # if self.strLDAPServer.startswith('ldap://'): # self.strLDAPServer = self.strLDAPServer[7:] # self.strLDAPServer = self.strLDAPServer.rstrip('/') if self.dnBase == None: raise RuntimeError, "No BASE statement in config file" if self.strLDAPServer == None: raise RuntimeError, "No HOST statement in config file" servers = self.strLDAPServer.split() except: raise RuntimeError, "could not read LDAP configuration: %s (line %d)" % ( sys.exc_value, sys.exc_traceback.tb_lineno) # Open the LDAP database connected = "N" for strLDAPServer in servers: try: # print "Connecting to %s" %strLDAPServer #ldapdb_url=ldapurl.LDAPUrl(hostport=strLDAPServer, dn=self.dnBase) ldapdb_url = ldapurl.LDAPUrl(strLDAPServer + "/" + self.dnBase) self.ldapHandle = ldap.initialize(ldapdb_url.unparse()) if who == None: # Bind as user anonymous self.ldapHandle.simple_bind_s() elif auth == None: # Bind using the simple authentication self.ldapHandle.bind_s(who, cred, method) else: self.ldapHandle.sasl_interactive_bind_s(who, auth) connected = "Y" break except: print "Unable to connect to server. Will try the next one." if (connected == "N"): raise RuntimeError, "could not connect to LDAP: %s (line %d)" % ( sys.exc_value, sys.exc_traceback.tb_lineno)
def get_email_from_identifier(user_identifier): lu = ldapurl.LDAPUrl(LDAP_URL) l = ldap.initialize(lu.initializeUrl()) l.protocol_version = ldap.VERSION3 l.simple_bind_s(LDAP_ROOT_USER_DN, LDAP_ROOT_USER_PASS) user_dn, detail = get_user_dn(l, user_identifier) if detail and "mail" in detail and detail["mail"]: return detail["mail"][0].decode("utf-8") else: return None
def _handle_referral(self, exception): """ Handle a referral specified in the passed-in exception """ payload = exception.args[0] info = payload.get('info') ldap_url = info[info.find('ldap'):] if ldapurl.isLDAPUrl(ldap_url): conn_str = ldapurl.LDAPUrl(ldap_url).initializeUrl() conn = self._connect(conn_str) conn.simple_bind_s(self._encode_incoming(self.bind_dn), self._encode_incoming(self.bind_pwd)) return conn else: raise ldap.CONNECT_ERROR('Bad referral "%s"' % str(exception))
def test_add_server_existing(self): # If a LDAP server definition with the same LDAP URL exists, it # will be replaced with the new values. conn = self._makeSimple() existing = list(conn.servers.values())[0] ldap_url = ldapurl.LDAPUrl(existing['url']) host, port = ldap_url.hostport.split(':') protocol = ldap_url.urlscheme conn.addServer(host, port, protocol, conn_timeout=10, op_timeout=15) self.assertEqual(len(conn.servers.values()), 1) server = list(conn.servers.values())[0] self.assertEqual(server['url'], 'ldap://host:636') self.assertEqual(server['conn_timeout'], 10) self.assertEqual(server['op_timeout'], 15)
def addServer(self, host, port, protocol, conn_timeout=-1, op_timeout=-1): """ Add a server definition to the list of servers used """ start_tls = False if protocol == 'ldaptls': protocol = 'ldap' start_tls = True hp = '%s:%s' % (host, port) conn = ldapurl.LDAPUrl(urlscheme=protocol, hostport=hp) server_url = conn.initializeUrl() self.servers[server_url] = { 'url': server_url, 'conn_timeout': conn_timeout, 'op_timeout': op_timeout, 'start_tls': start_tls }
def info_from_ldap(self, config=None, password=''): """ Get information about this user from LDAP. """ import ldap, ldapurl resp = {"result": "ERROR"} if not config: config = CRITsConfig.objects().first() # Make sure we have the rquired settings, else return failure if not config.ldap_server or not config.ldap_userdn: return resp ldap_server = config.ldap_server.split(':') scheme = "ldap" if config.ldap_tls: scheme = "ldaps" url = ldapurl.LDAPUrl('%s://%s' % (scheme, ldap_server[0])) if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) # setup auth for custom cn's cn = "cn=" if config.ldap_usercn: cn = config.ldap_usercn # setup auth for custom cn's if len(config.ldap_usercn) > 0: un = "%s%s,%s" % (config.ldap_usercn, self.username, config.ldap_userdn) elif "@" in config.ldap_userdn: un = "%s%s" % (self.username, config.ldap_userdn) else: un = self.username try: # Try auth bind first l.simple_bind_s(un, password) logger.info("Bound to LDAP for: %s" % self.username) except Exception, e: logger.error("Error binding to LDAP for: %s" % self.username) logger.error("ERR: %s" % e)
def __init__(self): self.env = Environment.getInstance() self.log = logging.getLogger(__name__) # Initialize from configuration get = self.env.config.get self.__url = ldapurl.LDAPUrl(get("ldap.url")) self.__bind_user = get('ldap.bind-user', default=None) self.__bind_dn = get('ldap.bind-dn', default=None) self.__bind_secret = get('ldap.bind-secret', default=None) self.__pool = int(get('ldap.pool-size', default=10)) # Sanity check if self.__bind_user and not ldap.SASL_AVAIL: raise LDAPException(C.make_error("NO_SASL_SUPPORT")) # Initialize static pool LDAPHandler.connection_handle = [None] * self.__pool LDAPHandler.connection_usage = [False] * self.__pool
def validate_ldap_url(param, options=None): """ Raises ParamValidationError if provided param is not a valid LDAP URL """ if not param: return try: import ldapurl except ImportError: msg = ( 'The python ldap package is required to use this functionality.') raise ParamValidationError(msg) try: ldapurl.LDAPUrl(param) except ValueError as ve: msg = ('The given string [%s] is not a valid LDAP URL: %s' % (param, ve)) raise ParamValidationError(msg)
def urlfetch(uri, trace_level=0): """ Fetches a parsed schema entry by uri. If uri is a LDAP URL the LDAP server is queried directly. Otherwise uri is assumed to point to a LDIF file which is loaded with urllib. """ uri = uri.strip() if uri.startswith('ldap:') or uri.startswith('ldaps:') or uri.startswith( 'ldapi:'): import ldapurl ldap_url = ldapurl.LDAPUrl(uri) l = ldap.initialize(ldap_url.initializeUrl(), trace_level) l.protocol_version = ldap.VERSION3 l.simple_bind_s(ldap_url.who or '', ldap_url.cred or '') subschemasubentry_dn = l.search_subschemasubentry_s( ldap_url.dn).decode() if subschemasubentry_dn is None: subschemasubentry_entry = None else: if ldap_url.attrs is None: schema_attrs = SCHEMA_ATTRS else: schema_attrs = ldap_url.attrs subschemasubentry_entry = l.read_subschemasubentry_s( subschemasubentry_dn, attrs=schema_attrs) subschemasubentry_entry = By2Str(subschemasubentry_entry) l.unbind_s() del l else: import urllib.request, ldif ldif_file = urllib.request.urlopen(uri) ldif_parser = ldif.LDIFRecordList(ldif_file, max_entries=1) ldif_parser.parse() subschemasubentry_dn, subschemasubentry_entry = ldif_parser.all_records[ 0] if subschemasubentry_dn != None: parsed_sub_schema = ldap.schema.SubSchema(subschemasubentry_entry) else: parsed_sub_schema = None return subschemasubentry_dn, parsed_sub_schema
def ldap_connect(dn=None, pwd=None, host=None): # '''Connects to LDAP. The connection is cached. # # If uid is not None, it is taken as the user to connect as, # otherwise it chooses the current user. If password is None, it # will attempt to connect first without a password and if that fails # it will try to read a password from the terminal # ''' # global _ldap_conn # if uid is None and pwd is None and _ldap_conn is not None: # return _ldap_conn # if uid is None: # dn = ldap_myself() # else: # dn = ldap_byuid(uid) # l = _ldap_conn # l.simple_bind_s(dn, pwd) # return l global _ldap_conn if dn is None: # if no DN is specified, we try ldapi first try: l = ldap.initialize("ldapi:///") l.sasl_interactive_bind_s("", ldap.sasl.external()) except: l = ldap.initialize(options.HOST) if options.UseTLS: l.set_option(ldap.OPT_X_TLS_CACERTFILE, options.TLS_CACERTFILE) l.start_tls_s() uid = mod_pwd.getpwuid(os.getuid())[0] passwd = getpass.getpass() l.simple_bind_s(uidfmt % uid, passwd) else: if host is None: host = "127.0.0.1" l = ldap.initialize(str(ldapurl.LDAPUrl(host))) l.simple_bind(dn, pwd) _ldap_conn = l return l
def __init__(self): self.config = Config.get_instance() self.log = logging.getLogger(__name__) # Initialize from configuration get = self.config.get self.__url = ldapurl.LDAPUrl(get("ldap.url")) self.__bind_user = get('ldap.bind_user', default=None) self.__bind_dn = get('ldap.bind_dn', default=None) self.__bind_secret = get('ldap.bind_secret', default=None) self.__pool = int(get('ldap.pool_size', default=10)) # Sanity check if self.__bind_user and not ldap.SASL_AVAIL: self.log.error("bind_user needs SASL support, which doesn't seem to be available in python-ldap") sys.exit(1) if not self.__url: self.log.error("there is no 'url' specified in the 'ldap' section of the configuration file") sys.exit(1) # Initialize static pool LDAPHandler.connection_handle = [None] * self.__pool LDAPHandler.connection_usage = [False] * self.__pool
def authenticate(self): # ldap.set_option(ldap.OPT_DEBUG_LEVEL, 1) ldap.set_option(ldap.OPT_REFERRALS, 0) ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 30) # the default LDAP protocol version - if not recognized - is v3 if self.settings['protocol_version'] == '2': ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION2) else: if self.settings['protocol_version'] != '3': LOG.warning("Unrecognized Protocol Version '%s', setting to '3'.", self.settings['protocol_version']) self.settings['protocol_version'] = '3' ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) try: parsed_url = ldapurl.LDAPUrl(self.settings['url']) except ValueError: raise AuthenticationError("Invalid url to LDAP service. " "Check config examples at https://github.com/Oomnitza/oomnitza-connector") self.ldap_connection = ldap.initialize(parsed_url.unparse()) cacert_file = self.settings.get('cacert_file', '') if cacert_file: cacert_file = os.path.abspath(cacert_file) if not os.path.isfile(cacert_file): raise ConfigError("%s is not a valid file!" % cacert_file) LOG.info("Setting CACert File to: %r.", cacert_file) ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, cacert_file) cacert_dir = self.settings.get('cacert_dir', '') if cacert_dir: cacert_dir = os.path.abspath(cacert_dir) if not os.path.isdir(cacert_dir): raise ConfigError("%s is not a valid directory!" % cacert_dir) LOG.info("Setting CACert Dir to: %r.", cacert_dir) ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, cacert_dir) # check for tls # if self.settings['enable_tls'] in self.TrueValues and self.settings['protocol_version'] == '3': if self.settings.get('verify_ssl', True) in TrueValues: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND) else: LOG.warning("verify_ssl = '%s' so SSL certificate validation has been disabled.", self.settings.get('verify_ssl', True)) ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW) try: if self.settings['username'].lower() == 'anonymous': self.ldap_connection.simple_bind_s() else: password = self.settings['password'] if not password: LOG.warning("No password set for LDAP. Connecting without password.") password = u"" self.ldap_connection.simple_bind_s(self.settings['username'], password) except ldap.INVALID_CREDENTIALS: LOG.exception("Error calling simple_bind_s()") raise AuthenticationError("Cannot connect to the LDAP server with given credentials. " "Check the 'username', 'password' and 'dn' options " "in the config file in the '[ldap]' section.") except ldap.UNWILLING_TO_PERFORM as exp: LOG.exception("Error calling simple_bind_s()") raise AuthenticationError("Cannot connect to the LDAP server with given credentials: " + exp.args[0]['info'])
def run(self, terms, variables=None, **kwargs): if not HAS_LDAP: msg = missing_required_lib("python-ldap", url="https://pypi.org/project/python-ldap/") msg += ". Import Error: %s" % LDAP_IMP_ERR raise AnsibleLookupError(msg) # Load the variables and direct args into the lookup options self.set_options(var_options=variables, direct=kwargs) domain = self.get_option('domain') port = self.get_option('port') scheme = self.get_option('scheme') start_tls = self.get_option('start_tls') validate_certs = self.get_option('validate_certs') cacert_file = self.get_option('ca_cert') search_base = self.get_option('search_base') username = self.get_option('username') password = self.get_option('password') auth = self.get_option('auth') allow_plaintext = self.get_option('allow_plaintext') # Validate and set input values # https://www.openldap.org/lists/openldap-software/200202/msg00456.html validate_certs_map = { 'never': ldap.OPT_X_TLS_NEVER, 'allow': ldap.OPT_X_TLS_ALLOW, 'try': ldap.OPT_X_TLS_TRY, 'demand': ldap.OPT_X_TLS_DEMAND, # Same as OPT_X_TLS_HARD } validate_certs_value = validate_certs_map.get(validate_certs, None) if validate_certs_value is None: valid_keys = list(validate_certs_map.keys()) valid_keys.sort() raise AnsibleLookupError("Invalid validate_certs value '%s': valid values are '%s'" % (validate_certs, "', '".join(valid_keys))) if auth not in ['gssapi', 'simple']: raise AnsibleLookupError("Invalid auth value '%s': expecting either 'gssapi', or 'simple'" % auth) elif auth == 'gssapi': if not ldap.SASL_AVAIL: raise AnsibleLookupError("Cannot use auth=gssapi when SASL is not configured with the local LDAP " "install") if username or password: raise AnsibleLookupError("Explicit credentials are not supported when auth='gssapi'. Call kinit " "outside of Ansible") elif auth == 'simple' and not (username and password): raise AnsibleLookupError("The username and password values are required when auth=simple") if ldapurl.isLDAPUrl(domain): ldap_url = ldapurl.LDAPUrl(ldapUrl=domain) else: port = port if port else 389 if scheme == 'ldap' else 636 ldap_url = ldapurl.LDAPUrl(hostport="%s:%d" % (domain, port), urlscheme=scheme) # We have encryption if using LDAPS, or StartTLS is used, or we auth with SASL/GSSAPI encrypted = ldap_url.urlscheme == 'ldaps' or start_tls or auth == 'gssapi' if not encrypted and not allow_plaintext: raise AnsibleLookupError("Current configuration will result in plaintext traffic exposing credentials. " "Set auth=gssapi, scheme=ldaps, start_tls=True, or allow_plaintext=True to " "continue") if ldap_url.urlscheme == 'ldaps' or start_tls: # We cannot use conn.set_option as OPT_X_TLS_NEWCTX (required to use the new context) is not supported on # older distros like EL7. Setting it on the ldap object works instead if not ldap.TLS_AVAIL: raise AnsibleLookupError("Cannot use TLS as the local LDAP installed has not been configured to support it") ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, validate_certs_value) if cacert_file: cacert_path = os.path.expanduser(os.path.expandvars(cacert_file)) if not os.path.exists(to_bytes(cacert_path)): raise AnsibleLookupError("The cacert_file specified '%s' does not exist" % to_native(cacert_path)) try: # While this is a path, python-ldap expects a str/unicode and not bytes ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, to_text(cacert_path)) except ValueError: # https://keathmilligan.net/python-ldap-and-macos/ raise AnsibleLookupError("Failed to set path to cacert file, this is a known issue with older " "OpenLDAP libraries on the host. Update OpenLDAP and reinstall " "python-ldap to continue") conn_url = ldap_url.initializeUrl() conn = ldap.initialize(conn_url, bytes_mode=False) conn.set_option(ldap.OPT_PROTOCOL_VERSION, 3) conn.set_option(ldap.OPT_REFERRALS, 0) # Allow us to search from the base # Make sure we run StartTLS before doing the bind to protect the credentials if start_tls: try: conn.start_tls_s() except ldap.LDAPError as err: raise AnsibleLookupError("Failed to send StartTLS to LDAP host '%s': %s" % (conn_url, to_native(err))) if auth == 'simple': try: conn.bind_s(to_text(username), to_text(password)) except ldap.LDAPError as err: raise AnsibleLookupError("Failed to simple bind against LDAP host '%s': %s" % (conn_url, to_native(err))) else: try: conn.sasl_gssapi_bind_s() except ldap.AUTH_UNKNOWN as err: # The SASL GSSAPI binding is not installed, e.g. cyrus-sasl-gssapi. Give a better error message than # what python-ldap provides raise AnsibleLookupError("Failed to do a sasl bind against LDAP host '%s', the GSSAPI mech is not " "installed: %s" % (conn_url, to_native(err))) except ldap.LDAPError as err: raise AnsibleLookupError("Failed to do a sasl bind against LDAP host '%s': %s" % (conn_url, to_native(err))) try: if not search_base: root_dse = conn.read_rootdse_s() search_base = root_dse['defaultNamingContext'][0] ret = [] # TODO: change method to search for all servers in 1 request instead of multiple requests for server in terms: ret.append(get_laps_password(conn, server, search_base)) finally: conn.unbind_s() return ret
def authenticate(self, username=None, password=None, user_agent=None, remote_addr=None, accept_language=None, totp_enabled='Disabled'): """ Perform the authentication of the user. :param username: The user to authenticate. :type username: str :param password: The password provided to authenticate with. :type password: str :param user_agent: The user-agent in the request. :type user_agent: str :param remote_addr: The hostname/ip in the request. :type remote_addr: str :param accept_language: The Accept Language in the request. :type accept_language: str :param totp_enabled: If TOTP is enabled and should be checked as well. :type totp_enabled: str :returns: :class:`crits.core.user.CRITsUser`, None """ # Need username and password for logins, checkem both if not all([username, password]): return None e = EmbeddedLoginAttempt() e.user_agent = user_agent e.remote_addr = remote_addr e.accept_language = accept_language fusername = username if '\\' in username: username = username.split("\\")[1] user = CRITsUser.objects(username=username).first() if user: # If the user needs TOTP and it is not disabled system-wide, and # the user has exceeded the login threshold for this time period # don't go any further. Track the invalid login and return. if (((user.totp and totp_enabled == 'Optional') or totp_enabled == 'Required') and self._exceeded_login_threshold(user)): e.success = False self.track_login_attempt(user, e) user.reload() return None config = CRITsConfig.objects().first() if not config: return None if config.ldap_auth: import ldap, ldapurl try: # If you are using Oracle's server that's based on # Netscape's code, and your users can't login after # password expiration warning kicks in, you need: # python-ldap 2.4.15 installed and # import ldap.controls.pwdpolicy to fix it # import ldap.controls.pwdpolicy except ImportError: logger.info("ldap.controls.pwdpolicy not present.") try: # don't parse the port if there is one ldap_server = config.ldap_server.split(':') scheme = "ldap" if config.ldap_tls: scheme = "ldaps" url = ldapurl.LDAPUrl('%s://%s' % (scheme, ldap_server[0])) if len(ldap_server) == 2: l = ldap.initialize('%s:%s' % (url.unparse(), ldap_server[1])) else: l = ldap.initialize(url.unparse()) l.protocol_version = 3 l.set_option(ldap.OPT_REFERRALS, 0) l.set_option(ldap.OPT_TIMEOUT, 10) # setup auth for custom cn's if len(config.ldap_usercn) > 0: un = "%s%s,%s" % (config.ldap_usercn, fusername, config.ldap_userdn) elif "@" in config.ldap_userdn: un = "%s%s" % (fusername, config.ldap_userdn) else: un = fusername logger.info("Logging in user: %s" % un) l.simple_bind_s(un, password) user = self._successful_settings(user, e, totp_enabled) if config.ldap_update_on_login: user.update_from_ldap("Auto LDAP update", config, password) l.unbind() return user except ldap.INVALID_CREDENTIALS: l.unbind() logger.info("Invalid LDAP credentials for: %s" % un) except Exception, err: logger.info("LDAP Auth error: %s" % err) # If LDAP auth fails, attempt normal CRITs auth. # This will help with being able to use local admin accounts when # you have LDAP auth enabled. if password and user.check_password(password): self._successful_settings(user, e, totp_enabled) if config.ldap_update_on_login: user.update_from_ldap("Auto LDAP update", config) return user else: e.success = False user.invalid_login_attempts += 1 if user.is_active and user.invalid_login_attempts > settings.INVALID_LOGIN_ATTEMPTS: user.is_active = False logger.info("Account disabled due to too many invalid login attempts: %s" % user.username) if config.crits_email_end_tag: subject = "CRITs Account Lockout" + config.crits_email_subject_tag else: subject = config.crits_email_subject_tag + "CRITs Account Lockout" body = """ You are receiving this email because your CRITs account has been locked out due to too many invalid login attempts. If you did not perform this action, someone may be attempting to access your account. Please contact a site administrator to resolve. """ user.email_user(subject, body) self.track_login_attempt(user, e) user.reload()
if ldap_connection: ldap_connection.close_db() ldap_connection.unbind_s() del ldap_connection # Shutdown sys.exit(0) # Time to actually begin execution # Install our signal handlers signal.signal(signal.SIGTERM, commenceShutdown) signal.signal(signal.SIGINT, commenceShutdown) try: ldap_url = ldapurl.LDAPUrl(sys.argv[1]) database_path = sys.argv[2] except IndexError, e: print('Usage:\n' '{script_name} <LDAP URL> <pathname of database>\n' '{script_name} "ldap://127.0.0.1/cn=users,dc=test' '?*' '?sub' '?(objectClass=*)' '?bindname=uid=admin%2ccn=users%2cdc=test,' 'X-BINDPW=password" db.shelve').format(script_name=sys.argv[0]) sys.exit(1) except ValueError as e: print('Error parsing command-line arguments:', str(e)) sys.exit(1)
""" Example showing the use of the password extended operation. """ import sys,ldap,ldapurl,getpass # Set debugging level ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) ldapmodule_trace_level = 2 ldapmodule_trace_file = sys.stderr lu = ldapurl.LDAPUrl(sys.argv[1]) print('Old password') oldpw = getpass.getpass() print('New password') newpw = getpass.getpass() # Set path name of file containing all CA certificates # needed to validate server certificates ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,'/etc/httpd/ssl.crt/myCA-cacerts.pem') # Create LDAPObject instance l = ldap.initialize(lu.initializeUrl(),trace_level=ldapmodule_trace_level,trace_file=ldapmodule_trace_file) l.protocol_version=ldap.VERSION3 l.simple_bind_s(lu.dn,oldpw) l.passwd(lu.dn,oldpw,newpw)