# IRIS Source Code # contact@dfir-iris.org # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import random import string import ldap3.core.exceptions import ssl from ldap3 import Connection from ldap3 import Server from ldap3 import Tls from ldap3.utils import conv from app import app from app.datamgmt.manage.manage_users_db import get_active_user_by_login from app.datamgmt.manage.manage_users_db import create_user from app.datamgmt.manage.manage_users_db import add_user_to_group from app.datamgmt.manage.manage_groups_db import get_group_by_name log = app.logger def _get_unique_identifier(user_login): if app.config.get('LDAP_AUTHENTICATION_TYPE').lower() == 'ntlm': return user_login[user_login.find('\\')+1:] return user_login def _provision_user(connection, user_login): if get_active_user_by_login(user_login): return search_base = app.config.get('LDAP_SEARCH_DN') attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') unique_identifier = conv.escape_filter_chars(_get_unique_identifier(user_login)) attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') attributes = [] if attribute_display_name: attributes.append(attribute_display_name) if attribute_mail: attributes.append(attribute_mail) connection.search(search_base, f'({attribute_unique_identifier}={unique_identifier})', attributes=attributes) entry = connection.entries[0] if attribute_display_name: user_name = entry[attribute_display_name].value else: user_name = user_login if attribute_mail: user_email = entry[attribute_mail].value else: user_email = f'{user_login}@ldap' log.info(f'Provisioning user "{user_login}" which is present in LDAP but not yet in database.') # TODO the user password is chosen randomly # ideally it should be possible to create a user without providing any password # TODO to create the user password, we use the same code as the one to generate the administrator password in post_init.py # => should factor and reuse this code bit as a function # => also, it should probably be more secure to use the secrets module (instead of random) password = ''.join(random.choices(string.printable[:-6], k=16)) # TODO It seems email unicity is required (a fixed email causes a problem at the second account creation) # The email either comes from the ldap or is forged from the login to ensure unicity user = create_user(user_name, user_login, password, user_email, True) initial_group = get_group_by_name(app.config.get('IRIS_NEW_USERS_DEFAULT_GROUP')) add_user_to_group(user.id, initial_group.group_id) def ldap_authenticate(ldap_user_name, ldap_user_pwd): """ Authenticate to the LDAP server """ if app.config.get('LDAP_AUTHENTICATION_TYPE').lower() != 'ntlm': ldap_user_name = conv.escape_filter_chars(ldap_user_name) ldap_user = f"{app.config.get('LDAP_USER_PREFIX')}{ldap_user_name.strip()}{ ','+app.config.get('LDAP_USER_SUFFIX') if app.config.get('LDAP_USER_SUFFIX') else ''}" else: ldap_user = f"{ldap_user_name.strip()}" if app.config.get('LDAP_CUSTOM_TLS_CONFIG') is True: tls_configuration = Tls(validate=ssl.CERT_REQUIRED, version=app.config.get('LDAP_TLS_VERSION'), local_certificate_file=app.config.get('LDAP_SERVER_CERTIFICATE'), local_private_key_file=app.config.get('LDAP_PRIVATE_KEY'), local_private_key_password=app.config.get('LDAP_PRIVATE_KEY_PASSWORD'), ca_certs_file=app.config.get('LDAP_CA_CERTIFICATE') ) server = Server(f'{app.config.get("LDAP_CONNECT_STRING")}', use_ssl=app.config.get('LDAP_USE_SSL'), tls=tls_configuration) else: server = Server(f'{app.config.get("LDAP_CONNECT_STRING")}', use_ssl=app.config.get('LDAP_USE_SSL')) conn = Connection(server, user=ldap_user, password=ldap_user_pwd, auto_referrals=False, authentication=app.config.get('LDAP_AUTHENTICATION_TYPE')) try: if not conn.bind(): log.error(f"Cannot bind to ldap server: {conn.last_error} ") return False if app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): _provision_user(conn, ldap_user_name) except ldap3.core.exceptions.LDAPInvalidCredentialsResult as e: log.error(f'Wrong credentials. Error : {e.__str__()}') return False except Exception as e: raise Exception(e.__str__()) log.info("Successful authenticated user") return True