def sso_yubico_post(): remote_addr = utils.get_remote_addr() sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (AZURE_YUBICO_AUTH, GOOGLE_YUBICO_AUTH, AUTHZERO_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': token, }, remove=True) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_TOKEN, reason_long='Invalid Yubikey authentication token', ) return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) valid, yubico_id = sso.auth_yubico(key) if not valid or not yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, username=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_YUBIKEY_FAILED, reason_long='Yubikey authentication failed', ) return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) return _validate_user(username, email, sso_mode, org_id, groups, remote_addr, yubico_id=yubico_id)
def sso_yubico_post(): remote_addr = utils.get_remote_addr() sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': token, }, remove=True) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_TOKEN, reason_long='Invalid Yubikey authentication token', ) return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) valid, yubico_id = sso.auth_yubico(key) if not valid or not yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, username=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_YUBIKEY_FAILED, reason_long='Yubikey authentication failed', ) return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) return _validate_user(username, email, sso_mode, org_id, groups, remote_addr, yubico_id=yubico_id)
def sso_yubico_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': token, }, remove=True) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) valid, yubico_id = sso.auth_yubico(key) if not valid or not yubico_id: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) return _validate_user(username, email, sso_mode, org_id, groups, yubico_id=yubico_id)
def auth_check(self, password, otp_code=None, yubico_key=None, remote_addr=None): if not self.test_password(password): self.audit_event( 'admin_auth', 'Administrator login failed, invalid password', remote_addr=remote_addr, ) return False if self.otp_auth and not self.verify_otp_code(otp_code): self.audit_event( 'admin_auth', 'Administrator login failed, ' + 'invalid two-factor authentication code', remote_addr=remote_addr, ) return False if self.yubikey_id: valid, public_id = sso.auth_yubico(yubico_key) if not valid or self.yubikey_id != public_id: self.audit_event( 'admin_auth', 'Administrator login failed, invalid YubiKey', remote_addr=remote_addr, ) return False if self.disabled: self.audit_event( 'admin_auth', 'Administrator login failed, administrator is disabled', remote_addr=remote_addr, ) return False self.audit_event( 'admin_auth', 'Administrator login successful', remote_addr=remote_addr, ) return True
def sso_yubico_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] valid, yubico_id = sso.auth_yubico(key) if not valid: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id != usr.yubico_id: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def _check_password(self): if settings.vpn.stress_test or self.user.link_server_id: return if self.user.bypass_secondary: logger.info( 'Bypass secondary enabled, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) return if self.has_token: logger.info( 'Client authentication cached, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) return if self.whitelisted: logger.info( 'Client network whitelisted, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) return if not limiter.auth_check(self.user.id): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. Too many ' + 'authentication attempts') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Too many authentication attempts') sso_mode = settings.app.sso or '' duo_mode = settings.app.sso_duo_mode onelogin_mode = utils.get_onelogin_mode() okta_mode = utils.get_okta_mode() auth_type = self.user.auth_type or '' has_duo_passcode = DUO_AUTH in sso_mode and \ DUO_AUTH in auth_type and duo_mode == 'passcode' has_onelogin_passcode = SAML_ONELOGIN_AUTH == sso_mode and \ SAML_ONELOGIN_AUTH in auth_type and onelogin_mode == 'passcode' has_okta_passcode = SAML_OKTA_AUTH == sso_mode and \ SAML_OKTA_AUTH in auth_type and okta_mode == 'passcode' if has_duo_passcode or has_onelogin_passcode or has_okta_passcode: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password passcode_len = settings.app.sso_duo_passcode_length orig_password = self.password passcode = self.password[-passcode_len:] self.password = self.password[:-passcode_len] allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }) if doc: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping secondary passcode', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) if not allow: if DUO_AUTH in sso_mode: label = 'Duo' duo_auth = sso.Duo( username=self.user.name, factor=duo_mode, remote_ip=self.remote_ip, auth_type='Connection', passcode=passcode, ) allow = duo_auth.authenticate() elif SAML_ONELOGIN_AUTH == sso_mode: label = 'OneLogin' allow = sso.auth_onelogin_secondary( username=self.user.name, passcode=passcode, remote_ip=self.remote_ip, onelogin_mode=onelogin_mode, ) elif SAML_OKTA_AUTH == sso_mode: label = 'Okta' allow = sso.auth_okta_secondary( username=self.user.name, passcode=passcode, remote_ip=self.remote_ip, okta_mode=okta_mode, ) else: raise AuthError('Unknown secondary passcode challenge') if not allow: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed %s passcode authentication') % (self.server.name, label), remote_addr=self.remote_ip, ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'Enter %s Passcode' % label, True) else: self.set_challenge(None, 'Enter %s Passcode' % label, True) raise AuthError('Challenge secondary passcode') raise AuthError('Invalid secondary passcode') if settings.app.sso_cache and not self.server_auth_token: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }, upsert=True) elif YUBICO_AUTH in sso_mode and YUBICO_AUTH in auth_type: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password yubikey = self.password[-44:] self.password = self.password[:-44] yubikey_hash = hashlib.sha512() yubikey_hash.update(yubikey) yubikey_hash = base64.b64encode(yubikey_hash.digest()) allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }) if doc: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping Yubikey', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) if not allow: valid, yubico_id = sso.auth_yubico(yubikey) if yubico_id != self.user.yubico_id: valid = False if not valid: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed Yubico authentication') % (self.server.name), remote_addr=self.remote_ip, ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'YubiKey', True) else: self.set_challenge(None, 'YubiKey', True) raise AuthError('Challenge YubiKey') raise AuthError('Invalid YubiKey') if settings.app.sso_cache and not self.server_auth_token: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }, upsert=True) elif self.server.otp_auth and self.user.type == CERT_CLIENT: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password otp_code = self.password[-6:] self.password = self.password[:-6] allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.otp_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }) if doc: self.otp_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping OTP', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) if not allow: if not self.user.verify_otp_code(otp_code): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed two-step authentication') % (self.server.name), remote_addr=self.remote_ip, ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'Enter OTP Code', True) else: self.set_challenge(None, 'Enter OTP Code', True) raise AuthError('Challenge OTP code') raise AuthError('Invalid OTP code') if settings.app.sso_cache and not self.server_auth_token: self.otp_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }, upsert=True) if self.user.has_pin(): if not self.user.check_pin(self.password): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) if self.has_challenge(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') raise AuthError('Invalid pin') elif settings.user.pin_mode == PIN_REQUIRED: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User does not have a pin set') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('User does not have a pin set')
def _check_password(self): if self.user.bypass_secondary or self.user.link_server_id or \ settings.vpn.stress_test or self.has_token: return sso_mode = settings.app.sso or '' duo_mode = settings.app.sso_duo_mode auth_type = self.user.auth_type or '' if DUO_AUTH in sso_mode and DUO_AUTH in auth_type and \ duo_mode == 'passcode': if not self.password and self.has_challenge() and \ self.user.has_pin(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password passcode_len = settings.app.sso_duo_passcode_length orig_password = self.password passcode = self.password[-passcode_len:] self.password = self.password[:-passcode_len] allow = False if settings.app.sso_cache: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }) if doc: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }) allow = True if not allow: duo_auth = sso.Duo( username=self.user.name, factor=duo_mode, remote_ip=self.remote_ip, auth_type='Connection', passcode=passcode, ) allow = duo_auth.authenticate() if not allow: if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'Enter Duo Passcode', True) else: self.set_challenge(None, 'Enter Duo Passcode', True) raise AuthError('Challenge Duo code') self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed Duo passcode authentication') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid OTP code') if settings.app.sso_cache: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }, upsert=True) elif YUBICO_AUTH in sso_mode and YUBICO_AUTH in auth_type: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password yubikey = self.password[-44:] self.password = self.password[:-44] yubikey_hash = hashlib.sha512() yubikey_hash.update(yubikey) yubikey_hash = base64.b64encode(yubikey_hash.digest()) allow = False if settings.app.sso_cache: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }) if doc: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }) allow = True if not allow: valid, yubico_id = sso.auth_yubico(yubikey) if yubico_id != self.user.yubico_id: valid = False if not valid: if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'YubiKey', True) else: self.set_challenge(None, 'YubiKey', True) raise AuthError('Challenge YubiKey') self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed Yubico authentication') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid YubiKey') if settings.app.sso_cache: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }, upsert=True) elif self.server.otp_auth and self.user.type == CERT_CLIENT: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password otp_code = self.password[-6:] self.password = self.password[:-6] allow = False if settings.vpn.otp_cache: doc = self.otp_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }) if doc: self.otp_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }) allow = True if not allow: if not self.user.verify_otp_code(otp_code): if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'Enter OTP Code', True) else: self.set_challenge(None, 'Enter OTP Code', True) raise AuthError('Challenge OTP code') self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed two-step authentication') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid OTP code') if settings.vpn.otp_cache: self.otp_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }, upsert=True) if self.user.has_pin(): if not self.user.check_pin(self.password): if self.has_challenge(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid pin') elif settings.user.pin_mode == PIN_REQUIRED: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User does not have a pin set') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('User does not have a pin set')
def _check_password(self): if settings.vpn.stress_test or self.user.link_server_id: return if self.user.bypass_secondary: logger.info( 'Bypass secondary enabled, skipping password', 'sso', username=self.user.name, ) return if self.has_token: logger.info( 'Client authentication cached, skipping password', 'sso', username=self.user.name, ) return if self.whitelisted: logger.info( 'Client network whitelisted, skipping password', 'sso', username=self.user.name, ) return doc = self.limiter_collection.find_and_modify({ '_id': self.user.id, }, { '$inc': { 'count': 1 }, '$setOnInsert': { 'timestamp': utils.now() }, }, new=True, upsert=True) if utils.now() > doc['timestamp'] + datetime.timedelta( seconds=settings.app.auth_limiter_ttl): doc = { 'count': 1, 'timestamp': utils.now(), } self.limiter_collection.update({ '_id': self.user.id, }, doc, upsert=True) if doc['count'] > settings.app.auth_limiter_count_max: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. Too many ' + 'authentication attempts') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Too many authentication attempts') sso_mode = settings.app.sso or '' duo_mode = settings.app.sso_duo_mode auth_type = self.user.auth_type or '' if DUO_AUTH in sso_mode and DUO_AUTH in auth_type and \ duo_mode == 'passcode': if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password passcode_len = settings.app.sso_duo_passcode_length orig_password = self.password passcode = self.password[-passcode_len:] self.password = self.password[:-passcode_len] allow = False if settings.app.sso_cache: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }) if doc: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping Duo', 'sso', username=self.user.name, ) if not allow: duo_auth = sso.Duo( username=self.user.name, factor=duo_mode, remote_ip=self.remote_ip, auth_type='Connection', passcode=passcode, ) allow = duo_auth.authenticate() if not allow: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed Duo passcode authentication') % (self.server.name), remote_addr=self.remote_ip, ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'Enter Duo Passcode', True) else: self.set_challenge(None, 'Enter Duo Passcode', True) raise AuthError('Challenge Duo code') raise AuthError('Invalid OTP code') if settings.app.sso_cache: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }, upsert=True) elif YUBICO_AUTH in sso_mode and YUBICO_AUTH in auth_type: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password yubikey = self.password[-44:] self.password = self.password[:-44] yubikey_hash = hashlib.sha512() yubikey_hash.update(yubikey) yubikey_hash = base64.b64encode(yubikey_hash.digest()) allow = False if settings.app.sso_cache: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }) if doc: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping Yubikey', 'sso', username=self.user.name, ) if not allow: valid, yubico_id = sso.auth_yubico(yubikey) if yubico_id != self.user.yubico_id: valid = False if not valid: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed Yubico authentication') % (self.server.name), remote_addr=self.remote_ip, ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'YubiKey', True) else: self.set_challenge(None, 'YubiKey', True) raise AuthError('Challenge YubiKey') raise AuthError('Invalid YubiKey') if settings.app.sso_cache: self.sso_passcode_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }, upsert=True) elif self.server.otp_auth and self.user.type == CERT_CLIENT: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password otp_code = self.password[-6:] self.password = self.password[:-6] allow = False if settings.vpn.otp_cache: doc = self.otp_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }) if doc: self.otp_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping OTP', 'sso', username=self.user.name, ) if not allow: if not self.user.verify_otp_code(otp_code): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed two-step authentication') % (self.server.name), remote_addr=self.remote_ip, ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge(orig_password, 'Enter OTP Code', True) else: self.set_challenge(None, 'Enter OTP Code', True) raise AuthError('Challenge OTP code') raise AuthError('Invalid OTP code') if settings.vpn.otp_cache: self.otp_cache_collection.update( { 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }, upsert=True) if self.user.has_pin(): if not self.user.check_pin(self.password): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % (self.server.name), remote_addr=self.remote_ip, ) if self.has_challenge(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') raise AuthError('Invalid pin') elif settings.user.pin_mode == PIN_REQUIRED: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'User does not have a pin set') % (self.server.name), remote_addr=self.remote_ip, ) raise AuthError('User does not have a pin set')
def auth_check(self, password, otp_code=None, yubico_key=None, remote_addr=None): if not self.test_password(password): journal.entry( journal.ADMIN_AUTH_FAILURE, self.journal_data, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_INVALID_PASSWORD, reason_long='Invalid password', ) self.audit_event( 'admin_auth', 'Administrator login failed, invalid password', remote_addr=remote_addr, ) return False if self.otp_auth and not self.verify_otp_code(otp_code): journal.entry( journal.ADMIN_AUTH_FAILURE, self.journal_data, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_INVALID_OTP, reason_long='Invalid two-factor authentication code', ) self.audit_event( 'admin_auth', 'Administrator login failed, ' + 'invalid two-factor authentication code', remote_addr=remote_addr, ) return False if self.yubikey_id: valid, public_id = sso.auth_yubico(yubico_key) if not valid or self.yubikey_id != public_id: journal.entry( journal.ADMIN_AUTH_FAILURE, self.journal_data, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_INVALID_YUBIKEY, reason_long='Invalid YubiKey', ) self.audit_event( 'admin_auth', 'Administrator login failed, invalid YubiKey', remote_addr=remote_addr, ) return False if self.disabled: journal.entry( journal.ADMIN_AUTH_FAILURE, self.journal_data, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_DISABLED, reason_long='Account is disabled', ) self.audit_event( 'admin_auth', 'Administrator login failed, administrator is disabled', remote_addr=remote_addr, ) return False journal.entry( journal.ADMIN_AUTH_SUCCESS, self.journal_data, remote_address=remote_addr, ) self.audit_event( 'admin_auth', 'Administrator login successful', remote_addr=remote_addr, ) return True
def sso_yubico_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] valid, yubico_id = sso.auth_yubico(key) if not valid: return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id != usr.yubico_id: return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def _check_password(self): if self.user.bypass_secondary or self.user.link_server_id or \ settings.vpn.stress_test or self.has_token: return sso_mode = settings.app.sso or '' duo_mode = settings.app.sso_duo_mode auth_type = self.user.auth_type or '' if DUO_AUTH in sso_mode and DUO_AUTH in auth_type and \ duo_mode == 'passcode': if not self.password and self.has_challenge() and \ self.user.has_pin(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password passcode_len = settings.app.sso_duo_passcode_length orig_password = self.password passcode = self.password[-passcode_len:] self.password = self.password[:-passcode_len] allow = False if settings.app.sso_cache: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }) if doc: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }) allow = True if not allow: duo_auth = sso.Duo( username=self.user.name, factor=duo_mode, remote_ip=self.remote_ip, auth_type='Connection', passcode=passcode, ) allow = duo_auth.authenticate() if not allow: if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'Enter Duo Passcode', True) else: self.set_challenge( None, 'Enter Duo Passcode', True) raise AuthError('Challenge Duo code') self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed Duo passcode authentication') % ( self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid OTP code') if settings.app.sso_cache: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }, upsert=True) elif YUBICO_AUTH in sso_mode and YUBICO_AUTH in auth_type: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password yubikey = self.password[-44:] self.password = self.password[:-44] yubikey_hash = hashlib.sha512() yubikey_hash.update(yubikey) yubikey_hash = base64.b64encode(yubikey_hash.digest()) allow = False if settings.app.sso_cache: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }) if doc: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }) allow = True if not allow: valid, yubico_id = sso.auth_yubico(yubikey) if yubico_id != self.user.yubico_id: valid = False if not valid: if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'YubiKey', True) else: self.set_challenge( None, 'YubiKey', True) raise AuthError('Challenge YubiKey') self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed Yubico authentication') % ( self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid YubiKey') if settings.app.sso_cache: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }, upsert=True) elif self.server.otp_auth and self.user.type == CERT_CLIENT: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password otp_code = self.password[-6:] self.password = self.password[:-6] allow = False if settings.vpn.otp_cache: doc = self.otp_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }) if doc: self.otp_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }) allow = True if not allow: if not self.user.verify_otp_code(otp_code): if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'Enter OTP Code', True) else: self.set_challenge( None, 'Enter OTP Code', True) raise AuthError('Challenge OTP code') self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed two-step authentication') % ( self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid OTP code') if settings.vpn.otp_cache: self.otp_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }, upsert=True) if self.user.has_pin(): if not self.user.check_pin(self.password): if self.has_challenge(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) raise AuthError('Invalid pin') elif settings.user.pin_mode == PIN_REQUIRED: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User does not have a pin set') % ( self.server.name), remote_addr=self.remote_ip, ) raise AuthError('User does not have a pin set')
def _check_password(self): if settings.vpn.stress_test or self.user.link_server_id: return if self.user.bypass_secondary: logger.info( 'Bypass secondary enabled, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_BYPASS, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Bypass secondary enabled, skipping password', ) return if self.has_token: logger.info( 'Client authentication cached, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Client authentication cached, skipping password', ) return if self.whitelisted: logger.info( 'Client network whitelisted, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_WHITELIST, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Client network whitelisted, skipping password', ) return if not limiter.auth_check(self.user.id): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. Too many ' + 'authentication attempts') % (self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Too many authentication attempts', ) raise AuthError('Too many authentication attempts') sso_mode = settings.app.sso or '' duo_mode = settings.app.sso_duo_mode onelogin_mode = utils.get_onelogin_mode() okta_mode = utils.get_okta_mode() auth_type = self.user.auth_type or '' has_duo_passcode = DUO_AUTH in sso_mode and \ DUO_AUTH in auth_type and duo_mode == 'passcode' has_onelogin_passcode = SAML_ONELOGIN_AUTH == sso_mode and \ SAML_ONELOGIN_AUTH in auth_type and onelogin_mode == 'passcode' has_okta_passcode = SAML_OKTA_AUTH == sso_mode and \ SAML_OKTA_AUTH in auth_type and okta_mode == 'passcode' if has_duo_passcode or has_onelogin_passcode or has_okta_passcode: if not self.password and self.has_challenge() and \ self.user.has_pin(): journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password passcode_len = settings.app.sso_duo_passcode_length orig_password = self.password passcode = self.password[-passcode_len:] self.password = self.password[:-passcode_len] allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }) if doc: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping secondary passcode', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Authentication cached, ' + \ 'skipping secondary passcode', ) if not allow: if DUO_AUTH in sso_mode: label = 'Duo' duo_auth = sso.Duo( username=self.user.name, factor=duo_mode, remote_ip=self.remote_ip, auth_type='Connection', passcode=passcode, ) allow = duo_auth.authenticate() elif SAML_ONELOGIN_AUTH == sso_mode: label = 'OneLogin' allow = sso.auth_onelogin_secondary( username=self.user.name, passcode=passcode, remote_ip=self.remote_ip, onelogin_mode=onelogin_mode, ) elif SAML_OKTA_AUTH == sso_mode: label = 'Okta' allow = sso.auth_okta_secondary( username=self.user.name, passcode=passcode, remote_ip=self.remote_ip, okta_mode=okta_mode, ) else: raise AuthError('Unknown secondary passcode challenge') if not allow: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed %s passcode authentication') % ( self.server.name, label), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed passcode authentication', ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'Enter %s Passcode' % label, True) else: self.set_challenge( None, 'Enter %s Passcode' % label, True) raise AuthError('Challenge secondary passcode') raise AuthError('Invalid secondary passcode') if settings.app.sso_cache and not self.server_auth_token: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }, upsert=True) elif YUBICO_AUTH in sso_mode and YUBICO_AUTH in auth_type: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password yubikey = self.password[-44:] self.password = self.password[:-44] yubikey_hash = hashlib.sha512() yubikey_hash.update(yubikey) yubikey_hash = base64.b64encode(yubikey_hash.digest()) allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }) if doc: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping Yubikey', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Authentication cached, ' + \ 'skipping Yubikey', ) if not allow: valid, yubico_id = sso.auth_yubico(yubikey) if yubico_id != self.user.yubico_id: valid = False if not valid: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed Yubico authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed Yubico authentication', ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'YubiKey', True) else: self.set_challenge( None, 'YubiKey', True) raise AuthError('Challenge YubiKey') raise AuthError('Invalid YubiKey') if settings.app.sso_cache and not self.server_auth_token: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }, upsert=True) elif self.server.otp_auth and self.user.type == CERT_CLIENT: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password otp_code = self.password[-6:] self.password = self.password[:-6] allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.otp_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }) if doc: self.otp_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping OTP', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Authentication cached, ' + \ 'skipping OTP', ) if not allow: if not self.user.verify_otp_code(otp_code): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed two-step authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed two-step authentication', ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'Enter OTP Code', True) else: self.set_challenge( None, 'Enter OTP Code', True) raise AuthError('Challenge OTP code') raise AuthError('Invalid OTP code') if settings.app.sso_cache and not self.server_auth_token: self.otp_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }, upsert=True) if self.user.has_pin(): if not self.user.check_pin(self.password): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) if self.has_challenge(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') raise AuthError('Invalid pin') elif settings.user.pin_mode == PIN_REQUIRED: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User does not have a pin set') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User does not have a pin set', ) raise AuthError('User does not have a pin set')