def task(self): acme_domain = settings.app.acme_domain if not acme_domain: return if not settings.app.acme_timestamp: logger.exception( 'Failed to update acme certificate. Timestamp not set', 'tasks', acme_domain=acme_domain, ) return if not settings.app.acme_key: logger.exception( 'Failed to update acme certificate. Account key not set', 'tasks', acme_domain=acme_domain, ) return if utils.time_now() - settings.app.acme_timestamp < \ settings.app.acme_renew: return logger.info( 'Updating acme certificate', 'tasks', acme_domain=acme_domain, ) acme.update_acme_cert() app.update_server()
def auth_session_post(): username = flask.request.json["username"] password = flask.request.json["password"] otp_code = flask.request.json.get("otp_code") remote_addr = utils.get_remote_addr() admin = auth.get_by_username(username, remote_addr) if not admin: if settings.app.sso == RADIUS_AUTH: return _auth_radius(username, password) time.sleep(random.randint(0, 100) / 1000.0) return utils.jsonify({"error": AUTH_INVALID, "error_msg": AUTH_INVALID_MSG}, 401) if not otp_code and admin.otp_auth: return utils.jsonify({"error": AUTH_OTP_REQUIRED, "error_msg": AUTH_OTP_REQUIRED_MSG}, 402) if not admin.auth_check(password, otp_code, remote_addr): time.sleep(random.randint(0, 100) / 1000.0) return utils.jsonify({"error": AUTH_INVALID, "error_msg": AUTH_INVALID_MSG}, 401) flask.session["session_id"] = admin.new_session() flask.session["admin_id"] = str(admin.id) flask.session["timestamp"] = int(utils.time_now()) if not settings.conf.ssl: flask.session["source"] = remote_addr return utils.jsonify({"authenticated": True, "default": admin.default or False})
def key_sync_get(org_id, user_id, server_id, key_hash): utils.rand_sleep() if not settings.local.sub_active: return utils.response('', status_code=480) auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(404) user = org.get_user(id=user_id) if not user: return flask.abort(404) elif not user.sync_secret: return flask.abort(404) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode(hmac.new( user.sync_secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }, w=0) except pymongo.errors.DuplicateKeyError: return flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: return utils.response(key_conf['conf']) return utils.response('')
def verify_otp_code(self, code): otp_secret = self.otp_secret padding = 8 - len(otp_secret) % 8 if padding != 8: otp_secret = otp_secret.ljust(len(otp_secret) + padding, '=') otp_secret = base64.b32decode(otp_secret.upper()) valid_codes = [] epoch = int(utils.time_now() / 30) for epoch_offset in range(-1, 2): value = struct.pack('>q', epoch + epoch_offset) hmac_hash = hmac.new(otp_secret, value, hashlib.sha1).digest() offset = ord(hmac_hash[-1]) & 0x0F truncated_hash = hmac_hash[offset:offset + 4] truncated_hash = struct.unpack('>L', truncated_hash)[0] truncated_hash &= 0x7FFFFFFF truncated_hash %= 1000000 valid_codes.append('%06d' % truncated_hash) if code not in valid_codes: return False try: self.otp_collection.insert({ '_id': { 'user_id': self.id, 'code': code, }, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return False return True
def key_sync_get(org_id, user_id, server_id, key_hash): utils.rand_sleep() if not settings.local.sub_active: return utils.response('', status_code=480) auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(401) user = org.get_user(id=user_id) if not user: return flask.abort(401) elif not user.sync_secret: return flask.abort(401) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode(hmac.new( user.sync_secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }, w=0) except pymongo.errors.DuplicateKeyError: return flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: return utils.response(key_conf['conf']) return utils.response('')
def _get_base_entry(event): data = { 'event': event, 'timestamp': utils.time_now(), } data.update(settings.local.host.journal_data) return data
def link_state_delete(): if settings.app.demo_mode: return utils.demo_blocked() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(406) auth_token = auth_token[:256] auth_timestamp = auth_timestamp[:64] auth_nonce = auth_nonce[:32] auth_signature = auth_signature[:512] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(408) except ValueError: return flask.abort(405) host = link.get_host(database.ObjectId(auth_token)) if not host: return flask.abort(404) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(413) auth_test_signature = base64.b64encode( hmac.new(host.secret.encode(), auth_string.encode(), hashlib.sha512).digest()).decode() if not utils.const_compare(auth_signature, auth_test_signature): return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(409) host.set_inactive() return utils.jsonify({})
def get_base_entry(event): data = { 'id': bson.ObjectId(), 'event': event, 'timestamp': utils.time_now(), } data.update(settings.local.host.journal_data) return data
def verify_otp_code(self, code, remote_ip=None): if remote_ip and settings.vpn.cache_otp_codes: doc = self.otp_cache_collection.find_one({"_id": self.id}) if doc: _, hash_salt, cur_otp_hash = doc["otp_hash"].split("$") hash_salt = base64.b64decode(hash_salt) else: hash_salt = os.urandom(8) cur_otp_hash = None otp_hash = hashlib.sha512() otp_hash.update(code + remote_ip) otp_hash.update(hash_salt) otp_hash = base64.b64encode(otp_hash.digest()) if otp_hash == cur_otp_hash: self.otp_cache_collection.update({"_id": self.id}, {"$set": {"timestamp": utils.now()}}) return True otp_hash = "$".join(("1", base64.b64encode(hash_salt), otp_hash)) otp_secret = self.otp_secret padding = 8 - len(otp_secret) % 8 if padding != 8: otp_secret = otp_secret.ljust(len(otp_secret) + padding, "=") otp_secret = base64.b32decode(otp_secret.upper()) valid_codes = [] epoch = int(utils.time_now() / 30) for epoch_offset in range(-1, 2): value = struct.pack(">q", epoch + epoch_offset) hmac_hash = hmac.new(otp_secret, value, hashlib.sha1).digest() offset = ord(hmac_hash[-1]) & 0x0F truncated_hash = hmac_hash[offset : offset + 4] truncated_hash = struct.unpack(">L", truncated_hash)[0] truncated_hash &= 0x7FFFFFFF truncated_hash %= 1000000 valid_codes.append("%06d" % truncated_hash) if code not in valid_codes: return False response = self.otp_collection.update( {"_id": {"user_id": self.id, "code": code}}, {"$set": {"timestamp": utils.now()}}, upsert=True ) if response["updatedExisting"]: return False if remote_ip and settings.vpn.cache_otp_codes: self.otp_cache_collection.update( {"_id": self.id}, {"$set": {"otp_hash": otp_hash, "timestamp": utils.now()}}, upsert=True ) return True
def auth_session_post(): username = utils.json_filter_str('username') password = utils.json_str('password') otp_code = utils.json_opt_filter_str('otp_code') yubico_key = utils.json_opt_filter_str('yubico_key') remote_addr = utils.get_remote_addr() time.sleep(random.randint(50, 100) / 1000.) admin = auth.get_by_username(username) if not admin: if settings.app.sso and RADIUS_AUTH in settings.app.sso: return _auth_radius(username, password) time.sleep(random.randint(0, 100) / 1000.) return _auth_plugin(username, password) if (not otp_code and admin.otp_auth) or \ (not yubico_key and admin.yubikey_id): return utils.jsonify( { 'error': AUTH_OTP_REQUIRED, 'error_msg': AUTH_OTP_REQUIRED_MSG, 'otp_auth': admin.otp_auth, 'yubico_auth': bool(admin.yubikey_id), }, 402) if not limiter.auth_check(admin.id): return utils.jsonify( { 'error': AUTH_TOO_MANY, 'error_msg': AUTH_TOO_MANY_MSG, }, 400) if not admin.auth_check(password, otp_code, yubico_key, remote_addr): time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.app.server_ssl: flask.session['source'] = remote_addr utils.set_flask_sig() return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def link_state_delete(): if settings.app.demo_mode: return utils.demo_blocked() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) host = link.get_host(utils.ObjectId(auth_token)) if not host: return flask.abort(401) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode(hmac.new( host.secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(401) host.set_inactive() return utils.jsonify({})
def update_acme_cert(): if not settings.app.acme_key: settings.app.acme_key = utils.generate_private_key() settings.commit() private_key = utils.generate_private_ec_key() csr = utils.generate_csr(private_key, settings.app.acme_domain) cert = get_acme_cert(settings.app.acme_key, csr) settings.app.server_key = private_key.strip() settings.app.server_cert = cert.strip() settings.app.acme_timestamp = utils.time_now() settings.commit()
def auth_session_post(): username = utils.json_filter_str('username') password = utils.json_str('password') otp_code = utils.json_opt_filter_str('otp_code') yubico_key = utils.json_opt_filter_str('yubico_key') remote_addr = utils.get_remote_addr() time.sleep(random.randint(50, 100) / 1000.) admin = auth.get_by_username(username, remote_addr) if not admin: if settings.app.sso and RADIUS_AUTH in settings.app.sso: return _auth_radius(username, password) time.sleep(random.randint(0, 100) / 1000.) return _auth_plugin(username, password) if (not otp_code and admin.otp_auth) or \ (not yubico_key and admin.yubikey_id): return utils.jsonify({ 'error': AUTH_OTP_REQUIRED, 'error_msg': AUTH_OTP_REQUIRED_MSG, 'otp_auth': admin.otp_auth, 'yubico_auth': bool(admin.yubikey_id), }, 402) if not admin.auth_check(password, otp_code, yubico_key, remote_addr): time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.app.server_ssl: flask.session['source'] = remote_addr utils.set_flask_sig() return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def _check_auth_data(self): if not self.auth_token and not self.auth_nonce and \ not self.auth_timestamp: return if not self.auth_nonce: raise AuthError('Auth data missing nonce') if not self.auth_timestamp: raise AuthError('Auth data missing timestamp') if abs(int(self.auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Auth timestamp expired' % \ self.server.name, remote_addr=self.remote_ip, ) raise AuthError('Auth timestamp expired') if self.auth_token: auth_token_hash = hashlib.sha512() auth_token_hash.update(self.auth_token) auth_token = base64.b64encode(auth_token_hash.digest()) else: auth_token = None try: self.nonces_collection.insert({ 'token': self.user.id, 'nonce': self.auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Duplicate nonce' % \ self.server.name, remote_addr=self.remote_ip, ) raise AuthError('Duplicate nonce') self.password = self.auth_password self.server_auth_token = auth_token
def auth_session_post(): username = flask.request.json['username'] password = flask.request.json['password'] otp_code = flask.request.json.get('otp_code') remote_addr = utils.get_remote_addr() admin = auth.get_by_username(username, remote_addr) if not admin: if settings.app.sso == RADIUS_AUTH: return _auth_radius(username, password) time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not otp_code and admin.otp_auth: return utils.jsonify( { 'error': AUTH_OTP_REQUIRED, 'error_msg': AUTH_OTP_REQUIRED_MSG, }, 402) if not admin.auth_check(password, otp_code, remote_addr): time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.conf.ssl: flask.session['source'] = remote_addr return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def task(self): acme_domain = settings.app.acme_domain if not acme_domain: return if settings.app.acme_timestamp and \ settings.app.acme_key and \ utils.time_now() - settings.app.acme_timestamp < \ settings.app.acme_renew: return logger.info( 'Updating acme certificate', 'tasks', acme_domain=acme_domain, ) acme.update_acme_cert() app.update_server()
def auth_session_post(): username = flask.request.json['username'] password = flask.request.json['password'] otp_code = flask.request.json.get('otp_code') remote_addr = utils.get_remote_addr() admin = auth.get_by_username(username, remote_addr) if not admin: if settings.app.sso == RADIUS_AUTH: return _auth_radius(username, password) time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not otp_code and admin.otp_auth: return utils.jsonify({ 'error': AUTH_OTP_REQUIRED, 'error_msg': AUTH_OTP_REQUIRED_MSG, }, 402) if not admin.auth_check(password, otp_code, remote_addr): time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.conf.ssl: flask.session['source'] = remote_addr return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def auth_session_post(): username = flask.request.json['username'] password = flask.request.json['password'] remote_addr = utils.get_remote_addr() admin = auth.check_auth(username, password, remote_addr) if not admin: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.conf.ssl: flask.session['source'] = remote_addr return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def auth_session_post(): username = flask.request.json['username'] password = flask.request.json['password'] remote_addr = utils.get_remote_addr() admin = auth.check_auth(username, password, remote_addr) if not admin: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.conf.ssl: flask.session['source'] = remote_addr return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def verify_otp_code(self, code, remote_ip=None): if remote_ip and settings.vpn.cache_otp_codes: doc = self.otp_cache_collection.find_one({ '_id': self.id, }) if doc: _, hash_salt, cur_otp_hash = doc['otp_hash'].split('$') hash_salt = base64.b64decode(hash_salt) else: hash_salt = os.urandom(8) cur_otp_hash = None otp_hash = hashlib.sha512() otp_hash.update(code + remote_ip) otp_hash.update(hash_salt) otp_hash = base64.b64encode(otp_hash.digest()) if otp_hash == cur_otp_hash: self.otp_cache_collection.update({ '_id': self.id, }, {'$set': { 'timestamp': utils.now(), }}) return True otp_hash = '$'.join(( '1', base64.b64encode(hash_salt), otp_hash, )) otp_secret = self.otp_secret padding = 8 - len(otp_secret) % 8 if padding != 8: otp_secret = otp_secret.ljust(len(otp_secret) + padding, '=') otp_secret = base64.b32decode(otp_secret.upper()) valid_codes = [] epoch = int(utils.time_now() / 30) for epoch_offset in range(-1, 2): value = struct.pack('>q', epoch + epoch_offset) hmac_hash = hmac.new(otp_secret, value, hashlib.sha1).digest() offset = ord(hmac_hash[-1]) & 0x0F truncated_hash = hmac_hash[offset:offset + 4] truncated_hash = struct.unpack('>L', truncated_hash)[0] truncated_hash &= 0x7FFFFFFF truncated_hash %= 1000000 valid_codes.append('%06d' % truncated_hash) if code not in valid_codes: return False response = self.otp_collection.update( { '_id': { 'user_id': self.id, 'code': code, }, }, {'$set': { 'timestamp': utils.now(), }}, upsert=True) if response['updatedExisting']: return False if remote_ip and settings.vpn.cache_otp_codes: self.otp_cache_collection.update({ '_id': self.id, }, {'$set': { 'otp_hash': otp_hash, 'timestamp': utils.now(), }}, upsert=True) return True
def key_sync_get(org_id, user_id, server_id, key_hash): remote_addr = utils.get_remote_addr() if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(410) if usr.disabled: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(413) auth_test_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate key', ) return flask.abort(409) key_conf = usr.sync_conf(server_id, key_hash) if key_conf: usr.audit_event( 'user_profile', 'User profile synced from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_SYNC_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile synced from pritunl client', ) sync_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})
def check_session(): auth_token = flask.request.headers.get('Auth-Token', None) if auth_token: auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return False auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return False except ValueError: return False administrator = find_user(token=auth_token) if not administrator: return False auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path ] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return False auth_test_signature = base64.b64encode( hmac.new(administrator.secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return False try: Administrator.nonces_collection.insert( { 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }, w=0) except pymongo.errors.DuplicateKeyError: return False else: if not flask.session: return False admin_id = flask.session.get('admin_id') if not admin_id: return False admin_id = bson.ObjectId(admin_id) session_id = flask.session.get('session_id') administrator = get_user(admin_id, session_id) if not administrator: return False if not settings.conf.ssl and flask.session.get( 'source') != utils.get_remote_addr(): flask.session.clear() return False session_timeout = settings.app.session_timeout if session_timeout and int(utils.time_now()) - \ flask.session['timestamp'] > session_timeout: flask.session.clear() return False flask.session['timestamp'] = int(utils.time_now()) flask.g.administrator = administrator return True
def key_wg_put(org_id, user_id, server_id): org_id = org_id user_id = user_id server_id = server_id remote_addr = utils.get_remote_addr() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_token = auth_token[:256] auth_timestamp = auth_timestamp[:64] auth_nonce = auth_nonce[:32] auth_signature = auth_signature[:512] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(411) if usr.disabled: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) cipher_data64 = flask.request.json.get('data') box_nonce64 = flask.request.json.get('nonce') public_key64 = flask.request.json.get('public_key') signature64 = flask.request.json.get('signature') auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, cipher_data64, box_nonce64, public_key64, signature64]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(414) auth_test_signature = base64.b64encode(hmac.new( usr.sync_secret.encode(), auth_string.encode(), hashlib.sha512).digest()).decode() if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate nonce', ) return flask.abort(409) data_hash = hashlib.sha512( '&'.join([cipher_data64, box_nonce64, public_key64]).encode(), ).digest() try: usr.verify_sig( data_hash, base64.b64decode(signature64), ) except InvalidSignature: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Invalid rsa signature', ) return flask.abort(412) svr = usr.get_server(server_id) sender_pub_key = nacl.public.PublicKey( base64.b64decode(public_key64)) box_nonce = base64.b64decode(box_nonce64) priv_key = nacl.public.PrivateKey( base64.b64decode(svr.auth_box_private_key)) cipher_data = base64.b64decode(cipher_data64) nacl_box = nacl.public.Box(priv_key, sender_pub_key) plaintext = nacl_box.decrypt(cipher_data, box_nonce).decode() try: nonces_collection.insert({ 'token': auth_token, 'nonce': box_nonce64, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate secondary nonce', ) return flask.abort(412) key_data = json.loads(plaintext) client_wg_public_key = utils.filter_str(key_data['wg_public_key']) if len(client_wg_public_key) < 32: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key too short', ) return flask.abort(415) try: client_wg_public_key = base64.b64decode(client_wg_public_key) if len(client_wg_public_key) != 32: raise ValueError('Invalid length') client_wg_public_key = base64.b64encode(client_wg_public_key) except: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key invalid', ) return flask.abort(416) wg_keys_collection = mongo.get_collection('wg_keys') wg_key_doc = wg_keys_collection.find_one({ '_id': client_wg_public_key, }) if not wg_key_doc: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key not found', ) return flask.abort(417) instance = server.get_instance(server_id) if not instance or instance.state != 'running': return flask.abort(429) if not instance.server.wg: return flask.abort(429) clients = instance.instance_com.clients status = clients.ping_wg( user=usr, org=org, wg_public_key=client_wg_public_key, remote_ip=remote_addr, ) send_data = { 'status': status, 'timestamp': int(utils.time_now()), } send_nonce = nacl.utils.random(nacl.public.Box.NONCE_SIZE) nacl_box = nacl.public.Box(priv_key, sender_pub_key) send_cipher_data = nacl_box.encrypt( json.dumps(send_data).encode(), send_nonce) send_cipher_data = send_cipher_data[nacl.public.Box.NONCE_SIZE:] send_nonce64 = base64.b64encode(send_nonce).decode() send_cipher_data64 = base64.b64encode(send_cipher_data).decode() journal.entry( journal.USER_WG_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User wg ping from pritunl client', ) sync_signature = base64.b64encode(hmac.new( usr.sync_secret.encode(), (send_cipher_data64 + '&' + send_nonce64).encode(), hashlib.sha512).digest()).decode() return utils.jsonify({ 'data': send_cipher_data64, 'nonce': send_nonce64, 'signature': sync_signature, })
def auth_user_post(): if settings.app.demo_mode: return utils.demo_blocked() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) except ValueError: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org = organization.get_by_token(auth_token) if not org: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, ] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not org.auth_secret or len(org.auth_secret) < 8: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) auth_test_signature = base64.b64encode( hmac.new(org.auth_secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) try: org.nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) username = flask.request.json['username'] network_links = flask.request.json.get('network_links') usr = org.find_user(name=username) if usr: usr.remove() usr = org.new_user(name=username, type=CERT_CLIENT) usr.audit_event('user_created', 'User created with authentication token', remote_addr=utils.get_remote_addr()) if network_links: for network_link in network_links: try: usr.add_network_link(network_link, force=True) except (ipaddress.AddressValueError, ValueError): return _network_link_invalid() event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) keys = {} for svr in org.iter_servers(): key = usr.build_key_conf(svr.id) keys[key['name']] = key['conf'] return utils.jsonify(keys)
def key_sync_get(org_id, user_id, server_id, key_hash): if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get("Auth-Token", None) auth_timestamp = flask.request.headers.get("Auth-Timestamp", None) auth_nonce = flask.request.headers.get("Auth-Nonce", None) auth_signature = flask.request.headers.get("Auth-Signature", None) if not auth_token or not auth_timestamp or not auth_nonce or not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(401) user = org.get_user(id=user_id) if not user: return flask.abort(401) elif not user.sync_secret: return flask.abort(401) if auth_token != user.sync_token: return flask.abort(401) if user.disabled: return flask.abort(403) auth_string = "&".join( [user.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path] + ([flask.request.data] if flask.request.data else []) ) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode(hmac.new(user.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if auth_signature != auth_test_signature: return flask.abort(401) nonces_collection = mongo.get_collection("auth_nonces") try: nonces_collection.insert({"token": auth_token, "nonce": auth_nonce, "timestamp": utils.now()}) except pymongo.errors.DuplicateKeyError: return flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: user.audit_event("user_profile", "User profile synced from pritunl client", remote_addr=utils.get_remote_addr()) sync_signature = base64.b64encode( hmac.new(user.sync_secret.encode(), key_conf["conf"], hashlib.sha512).digest() ) return utils.jsonify({"signature": sync_signature, "conf": key_conf["conf"]}) return utils.jsonify({})
def _check_auth_data(self): if not self.auth_token and not self.auth_nonce and \ not self.auth_timestamp: return if not self.auth_nonce: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Auth data missing nonce' % \ 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='Auth data missing nonce', ) raise AuthError('Auth data missing nonce') if not self.auth_timestamp: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'Auth data missing timestamp') % \ 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='Auth data missing timestamp', ) raise AuthError('Auth data missing timestamp') if abs(int(self.auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Auth timestamp expired' % \ 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='Auth timestamp expired', ) raise AuthError('Auth timestamp expired') if self.auth_token: auth_token_hash = hashlib.sha512() auth_token_hash.update(self.auth_token) auth_token = base64.b64encode(auth_token_hash.digest()) else: auth_token = None try: self.nonces_collection.insert({ 'token': self.user.id, 'nonce': self.auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Duplicate nonce' % \ 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='Duplicate nonce', ) raise AuthError('Duplicate nonce') self.password = self.auth_password self.server_auth_token = auth_token
def check_session(): auth_token = flask.request.headers.get('Auth-Token', None) if auth_token: auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return False auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return False except ValueError: return False administrator = find_user(token=auth_token) if not administrator: return False auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return False auth_test_signature = base64.b64encode(hmac.new( administrator.secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return False try: Administrator.nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }, w=0) except pymongo.errors.DuplicateKeyError: return False else: if not flask.session: return False admin_id = flask.session.get('admin_id') if not admin_id: return False admin_id = bson.ObjectId(admin_id) session_id = flask.session.get('session_id') administrator = get_user(admin_id, session_id) if not administrator: return False if not settings.conf.ssl and flask.session.get( 'source') != utils.get_remote_addr(): flask.session.clear() return False session_timeout = settings.app.session_timeout if session_timeout and int(utils.time_now()) - \ flask.session['timestamp'] > session_timeout: flask.session.clear() return False flask.session['timestamp'] = int(utils.time_now()) flask.g.administrator = administrator return True
def auth_onelogin_secondary(username, passcode, remote_ip, onelogin_mode): access_token = _get_access_token() if not access_token: return False if 'passcode' in onelogin_mode and not passcode: logger.error( 'OneLogin passcode empty', 'sso', username=username, ) return False response = requests.get( _get_base_url() + '/api/1/users', headers={ 'Authorization': 'bearer:%s' % access_token, }, params={ 'username': username, }, ) if response.status_code != 200: logger.error( 'OneLogin api error', 'sso', username=username, status_code=response.status_code, response=response.content, ) return False users = response.json()['data'] if not users: logger.error( 'OneLogin user not found', 'sso', username=username, ) return False user = users[0] if user['status'] != 1: logger.error( 'OneLogin user disabled', 'sso', username=username, ) return False user_id = user['id'] response = requests.get( _get_base_url() + '/api/1/users/%d/otp_devices' % user_id, headers={ 'Authorization': 'bearer:%s' % access_token, }, ) if response.status_code != 200: logger.error( 'OneLogin api error', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False device_id = None devices = response.json()['data']['otp_devices'] needs_trigger = False for device in devices: if device['auth_factor_name'] != 'OneLogin Protect': continue if device['default']: device_id = device['id'] needs_trigger = bool(device.get('needs_trigger')) break elif not device_id: device_id = device['id'] needs_trigger = bool(device.get('needs_trigger')) if not device_id: if 'none' in onelogin_mode: logger.info( 'OneLogin secondary not available, skipped', 'sso', username=username, onelogin_mode=onelogin_mode, ) return True logger.error( 'OneLogin secondary not available', 'sso', username=username, onelogin_mode=onelogin_mode, ) return False state_token = None if needs_trigger or 'push' in onelogin_mode: response = requests.post( _get_base_url() + '/api/1/users/%d/otp_devices/%d/trigger' % (user_id, device_id), headers={ 'Authorization': 'bearer:%s' % access_token, 'Content-Type': 'application/json', 'X-Forwarded-For': remote_ip, }, json={ 'ipaddr': remote_ip, }, ) if response.status_code != 200: logger.error( 'OneLogin api error', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False activate = response.json()['data'] if not activate: logger.error( 'OneLogin activate empty', 'sso', username=username, onelogin_mode=onelogin_mode, ) return False state_token = activate[0]['state_token'] start = utils.time_now() while True: if utils.time_now() - start > 45: logger.error( 'OneLogin secondary timed out', 'sso', username=username, onelogin_mode=onelogin_mode, user_id=user_id, ) return False response = requests.post( _get_base_url() + '/api/1/users/%d/otp_devices/%d/verify' % (user_id, device_id), headers={ 'Authorization': 'bearer:%s' % access_token, 'Content-Type': 'application/json', 'X-Forwarded-For': remote_ip, }, json={ 'state_token': state_token, 'otp_token': passcode, }, ) if response.status_code != 200 and response.status_code != 401: logger.error( 'OneLogin api error', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False verify = response.json()['status'] if not verify: logger.error( 'OneLogin verify empty', 'sso', username=username, onelogin_mode=onelogin_mode, ) return False if response.status_code == 401: if 'Authentication pending' in verify['message']: time.sleep(0.5) continue logger.error( 'OneLogin secondary rejected', 'sso', username=username, onelogin_mode=onelogin_mode, user_id=user_id, ) return False if response.status_code != 200 or verify['type'] != "success": logger.error( 'OneLogin verify bad data', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False return True
def link_state_put(): if settings.app.demo_mode: return utils.demo_blocked() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(406) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(408) except ValueError: return flask.abort(405) host = link.get_host(utils.ObjectId(auth_token)) if not host: return flask.abort(404) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(413) auth_test_signature = base64.b64encode( hmac.new(host.secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(409) host.load_link() host.version = flask.request.json.get('version') host.public_address = flask.request.json.get('public_address') host.local_address = flask.request.json.get('local_address') host.address6 = flask.request.json.get('address6') data = json.dumps(host.get_state(), default=lambda x: str(x)) data += (16 - len(data) % 16) * '\x00' iv = os.urandom(16) key = hashlib.sha256(host.secret).digest() cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()).encryptor() enc_data = base64.b64encode(cipher.update(data) + cipher.finalize()) enc_signature = base64.b64encode( hmac.new(host.secret.encode(), enc_data, hashlib.sha512).digest()) resp = flask.Response(response=enc_data, mimetype='application/base64') resp.headers.add('Cache-Control', 'no-cache, no-store, must-revalidate') resp.headers.add('Pragma', 'no-cache') resp.headers.add('Expires', 0) resp.headers.add('Cipher-IV', base64.b64encode(iv)) resp.headers.add('Cipher-Signature', enc_signature) return resp
def link_state_put(): auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) host = link.get_host(utils.ObjectId(auth_token)) if not host: return flask.abort(401) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode( hmac.new(host.secret.encode(), auth_string, hashlib.sha512).digest()) if auth_signature != auth_test_signature: return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(401) data = json.dumps(host.get_state(), default=lambda x: str(x)) data += (16 - len(data) % 16) * '\x00' iv = Crypto.Random.new().read(16) key = hashlib.sha256(host.secret).digest() cipher = Crypto.Cipher.AES.new( key, Crypto.Cipher.AES.MODE_CBC, iv, ) enc_data = base64.b64encode(cipher.encrypt(data)) resp = flask.Response(response=enc_data, mimetype='application/base64') resp.headers.add('Cache-Control', 'no-cache, no-store, must-revalidate') resp.headers.add('Pragma', 'no-cache') resp.headers.add('Expires', 0) resp.headers.add('Cipher-IV', base64.b64encode(iv)) return resp
def key_sync_get(org_id, user_id, server_id, key_hash): if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(401) user = org.get_user(id=user_id) if not user: return flask.abort(401) elif not user.sync_secret: return flask.abort(401) if auth_token != user.sync_token: return flask.abort(401) if user.disabled: return flask.abort(403) auth_string = '&'.join([ user.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode(hmac.new( user.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: user.audit_event('user_profile', 'User profile synced from pritunl client', remote_addr=utils.get_remote_addr(), ) sync_signature = base64.b64encode(hmac.new( user.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})
def verify_otp_code(self, code, remote_ip=None): if remote_ip and settings.vpn.cache_otp_codes: doc = self.otp_cache_collection.find_one({ '_id': self.id, }) if doc: _, hash_salt, cur_otp_hash = doc['otp_hash'].split('$') hash_salt = base64.b64decode(hash_salt) else: hash_salt = os.urandom(8) cur_otp_hash = None otp_hash = hashlib.sha512() otp_hash.update(code + remote_ip) otp_hash.update(hash_salt) otp_hash = base64.b64encode(otp_hash.digest()) if otp_hash == cur_otp_hash: self.otp_cache_collection.update({ '_id': self.id, }, {'$set': { 'timestamp': utils.now(), }}) return True otp_hash = '$'.join(( '1', base64.b64encode(hash_salt), otp_hash, )) otp_secret = self.otp_secret padding = 8 - len(otp_secret) % 8 if padding != 8: otp_secret = otp_secret.ljust(len(otp_secret) + padding, '=') otp_secret = base64.b32decode(otp_secret.upper()) valid_codes = [] epoch = int(utils.time_now() / 30) for epoch_offset in range(-1, 2): value = struct.pack('>q', epoch + epoch_offset) hmac_hash = hmac.new(otp_secret, value, hashlib.sha1).digest() offset = ord(hmac_hash[-1]) & 0x0F truncated_hash = hmac_hash[offset:offset + 4] truncated_hash = struct.unpack('>L', truncated_hash)[0] truncated_hash &= 0x7FFFFFFF truncated_hash %= 1000000 valid_codes.append('%06d' % truncated_hash) if code not in valid_codes: return False response = self.otp_collection.update({ '_id': { 'user_id': self.id, 'code': code, }, }, {'$set': { 'timestamp': utils.now(), }}, upsert=True) if response['updatedExisting']: return False if remote_ip and settings.vpn.cache_otp_codes: self.otp_cache_collection.update({ '_id': self.id, }, {'$set': { 'otp_hash': otp_hash, 'timestamp': utils.now(), }}, upsert=True) return True
def auth_user_post(): auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) except ValueError: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org = organization.get_by_token(auth_token) if not org: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, ] + ([flask.request.data] if flask.request.data else [])) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) auth_test_signature = base64.b64encode(hmac.new( org.auth_secret.encode(), auth_string, hashlib.sha256).digest()) if auth_signature != auth_test_signature: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) try: org.nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) username = flask.request.json['username'] network_links = flask.request.json.get('network_links') usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT) usr.audit_event('user_created', 'User created with authentication token', remote_addr=utils.get_remote_addr()) if network_links: for network_link in network_links: try: usr.add_network_link(network_link, force=True) except (ipaddress.AddressValueError, ValueError): return _network_link_invalid() event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) keys = {} for svr in org.iter_servers(): key = usr.build_key_conf(svr.id) keys[key['name']] = key['conf'] return utils.jsonify(keys)
def key_sync_get(org_id, user_id, server_id, key_hash): remote_addr = utils.get_remote_addr() if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(410) if usr.disabled: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(413) auth_test_signature = base64.b64encode(hmac.new( usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate key', ) return flask.abort(409) key_conf = usr.sync_conf(server_id, key_hash) if key_conf: usr.audit_event('user_profile', 'User profile synced from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_SYNC_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile synced from pritunl client', ) sync_signature = base64.b64encode(hmac.new( usr.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})
def auth_onelogin_secondary(username, passcode, remote_ip, onelogin_mode): access_token = _get_access_token() if not access_token: return False if 'passcode' in onelogin_mode and not passcode: logger.error('OneLogin passcode empty', 'sso', username=username, ) return False response = requests.get( _get_base_url() + '/api/1/users', headers={ 'Authorization': 'bearer:%s' % access_token, }, params={ 'username': username, }, ) if response.status_code != 200: logger.error('OneLogin api error', 'sso', username=username, status_code=response.status_code, response=response.content, ) return False users = response.json()['data'] if not users: logger.error('OneLogin user not found', 'sso', username=username, ) return False user = users[0] if user['status'] != 1: logger.error('OneLogin user disabled', 'sso', username=username, ) return False user_id = user['id'] response = requests.get( _get_base_url() + '/api/1/users/%d/otp_devices' % user_id, headers={ 'Authorization': 'bearer:%s' % access_token, }, ) if response.status_code != 200: logger.error('OneLogin api error', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False device_id = None devices = response.json()['data']['otp_devices'] needs_trigger = False for device in devices: if device['auth_factor_name'] != 'OneLogin Protect': continue if device['default']: device_id = device['id'] needs_trigger = bool(device.get('needs_trigger')) break elif not device_id: device_id = device['id'] needs_trigger = bool(device.get('needs_trigger')) if not device_id: if 'none' in onelogin_mode: logger.info('OneLogin secondary not available, skipped', 'sso', username=username, onelogin_mode=onelogin_mode, ) return True logger.error('OneLogin secondary not available', 'sso', username=username, onelogin_mode=onelogin_mode, ) return False state_token = None if needs_trigger or 'push' in onelogin_mode: response = requests.post( _get_base_url() + '/api/1/users/%d/otp_devices/%d/trigger' % ( user_id, device_id), headers={ 'Authorization': 'bearer:%s' % access_token, 'Content-Type': 'application/json', 'X-Forwarded-For': remote_ip, }, json={ 'ipaddr': remote_ip, }, ) if response.status_code != 200: logger.error('OneLogin api error', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False activate = response.json()['data'] if not activate: logger.error('OneLogin activate empty', 'sso', username=username, onelogin_mode=onelogin_mode, ) return False state_token = activate[0]['state_token'] start = utils.time_now() while True: if utils.time_now() - start > 45: logger.error('OneLogin secondary timed out', 'sso', username=username, onelogin_mode=onelogin_mode, user_id=user_id, ) return False response = requests.post( _get_base_url() + '/api/1/users/%d/otp_devices/%d/verify' % ( user_id, device_id), headers={ 'Authorization': 'bearer:%s' % access_token, 'Content-Type': 'application/json', 'X-Forwarded-For': remote_ip, }, json={ 'state_token': state_token, 'otp_token': passcode, }, ) if response.status_code != 200 and response.status_code != 401: logger.error('OneLogin api error', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False verify = response.json()['status'] if not verify: logger.error('OneLogin verify empty', 'sso', username=username, onelogin_mode=onelogin_mode, ) return False if response.status_code == 401: if 'Authentication pending' in verify['message']: time.sleep(0.5) continue logger.error('OneLogin secondary rejected', 'sso', username=username, onelogin_mode=onelogin_mode, user_id=user_id, ) return False if response.status_code != 200 or verify['type'] != "success": logger.error('OneLogin verify bad data', 'sso', username=username, onelogin_mode=onelogin_mode, status_code=response.status_code, response=response.content, ) return False return True
def auth_session_post(): username = utils.json_filter_str('username')[:128] password = flask.request.json['password'] if password: password = password[:128] otp_code = utils.json_opt_filter_str('otp_code') if otp_code: otp_code = otp_code[:64] yubico_key = utils.json_opt_filter_str('yubico_key') if yubico_key: yubico_key = yubico_key[:128] remote_addr = utils.get_remote_addr() time.sleep(random.randint(50, 100) / 1000.) admin = auth.get_by_username(username) if not admin: if settings.app.sso and RADIUS_AUTH in settings.app.sso: return _auth_radius(username, password, remote_addr) time.sleep(random.randint(0, 100) / 1000.) return _auth_plugin(username, password, remote_addr) if (not otp_code and admin.otp_auth) or \ (not yubico_key and admin.yubikey_id): return utils.jsonify( { 'error': AUTH_OTP_REQUIRED, 'error_msg': AUTH_OTP_REQUIRED_MSG, 'otp_auth': admin.otp_auth, 'yubico_auth': bool(admin.yubikey_id), }, 402) if not limiter.auth_check(admin.id): journal.entry( journal.ADMIN_AUTH_FAILURE, admin.journal_data, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_RATE_LIMIT, reason_long='Too many authentication attempts', ) return utils.jsonify( { 'error': AUTH_TOO_MANY, 'error_msg': AUTH_TOO_MANY_MSG, }, 400) if not admin.auth_check(password, otp_code, yubico_key, remote_addr): time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.app.server_ssl: flask.session['source'] = remote_addr journal.entry( journal.ADMIN_SESSION_START, admin.journal_data, remote_address=remote_addr, session_id=flask.session['session_id'], ) utils.set_flask_sig() return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def key_wg_post(org_id, user_id, server_id): org_id = org_id user_id = user_id server_id = server_id remote_addr = utils.get_remote_addr() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_token = auth_token[:256] auth_timestamp = auth_timestamp[:64] auth_nonce = auth_nonce[:32] auth_signature = auth_signature[:512] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(411) if usr.disabled: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) cipher_data64 = flask.request.json.get('data') box_nonce64 = flask.request.json.get('nonce') public_key64 = flask.request.json.get('public_key') signature64 = flask.request.json.get('signature') auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, cipher_data64, box_nonce64, public_key64, signature64 ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(414) auth_test_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate nonce', ) return flask.abort(409) data_hash = hashlib.sha512('&'.join( [cipher_data64, box_nonce64, public_key64])).digest() try: usr.verify_sig( data_hash, base64.b64decode(signature64), ) except InvalidSignature: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Invalid rsa signature', ) return flask.abort(412) svr = usr.get_server(server_id) sender_pub_key = nacl.public.PublicKey(base64.b64decode(public_key64)) box_nonce = base64.b64decode(box_nonce64) priv_key = nacl.public.PrivateKey( base64.b64decode(svr.auth_box_private_key)) cipher_data = base64.b64decode(cipher_data64) nacl_box = nacl.public.Box(priv_key, sender_pub_key) plaintext = nacl_box.decrypt(cipher_data, box_nonce).decode('utf-8') try: nonces_collection.insert({ 'token': auth_token, 'nonce': box_nonce64, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate secondary nonce', ) return flask.abort(415) key_data = json.loads(plaintext) client_platform = utils.filter_str_uni(key_data['platform']) client_device_id = utils.filter_str_uni(key_data['device_id']) client_device_name = utils.filter_str_uni(key_data['device_name']) client_mac_addr = utils.filter_str_uni(key_data['mac_addr']) client_mac_addrs = key_data['mac_addrs'] if client_mac_addrs: client_mac_addrs = [utils.filter_str_uni(x) for x in client_mac_addrs] else: client_mac_addrs = None client_auth_token = key_data['token'].decode('utf-8') client_auth_nonce = utils.filter_str_uni(key_data['nonce']) client_auth_password = key_data['password'].decode('utf-8') client_auth_timestamp = int(key_data['timestamp']) client_wg_public_key = key_data['wg_public_key'].decode('utf-8') if len(client_wg_public_key) < 32: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key too short', ) return flask.abort(416) try: client_wg_public_key = base64.b64decode(client_wg_public_key) if len(client_wg_public_key) != 32: raise ValueError('Invalid length') client_wg_public_key = base64.b64encode(client_wg_public_key) except: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key invalid', ) return flask.abort(417) instance = server.get_instance(server_id) if not instance or instance.state != 'running': return flask.abort(429) if not instance.server.wg: return flask.abort(429) wg_keys_collection = mongo.get_collection('wg_keys') try: wg_keys_collection.insert({ '_id': client_wg_public_key, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, wg_public_key=client_wg_public_key, event_long='Duplicate wg public key', ) return flask.abort(413) clients = instance.instance_com.clients event = threading.Event() send_data = { 'allow': None, 'configuration': None, 'reason': None, } def callback(allow, data): send_data['allow'] = allow if allow: send_data['configuration'] = data else: send_data['reason'] = data event.set() clients.connect_wg( user=usr, org=org, wg_public_key=client_wg_public_key, auth_password=client_auth_password, auth_token=client_auth_token, auth_nonce=client_auth_nonce, auth_timestamp=client_auth_timestamp, platform=client_platform, device_id=client_device_id, device_name=client_device_name, mac_addr=client_mac_addr, mac_addrs=client_mac_addrs, remote_ip=remote_addr, connect_callback=callback, ) event.wait() send_nonce = nacl.utils.random(nacl.public.Box.NONCE_SIZE) nacl_box = nacl.public.Box(priv_key, sender_pub_key) send_cipher_data = nacl_box.encrypt(json.dumps(send_data), send_nonce) send_cipher_data = send_cipher_data[nacl.public.Box.NONCE_SIZE:] send_nonce64 = base64.b64encode(send_nonce) send_cipher_data64 = base64.b64encode(send_cipher_data) usr.audit_event( 'user_profile', 'User retrieved wg public key from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_WG_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User retrieved wg public key from pritunl client', ) sync_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), send_cipher_data64 + '&' + send_nonce64, hashlib.sha512).digest()) return utils.jsonify({ 'data': send_cipher_data64, 'nonce': send_nonce64, 'signature': sync_signature, })
def link_state_put(): if settings.app.demo_mode: return utils.demo_blocked() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) host = link.get_host(utils.ObjectId(auth_token)) if not host: return flask.abort(401) auth_string = '&'.join([ auth_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode(hmac.new( host.secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(401) host.load_link() host.version = flask.request.json.get('version') host.public_address = flask.request.json.get('public_address') data = json.dumps(host.get_state(), default=lambda x: str(x)) data += (16 - len(data) % 16) * '\x00' iv = Crypto.Random.new().read(16) key = hashlib.sha256(host.secret).digest() cipher = Crypto.Cipher.AES.new( key, Crypto.Cipher.AES.MODE_CBC, iv, ) enc_data = base64.b64encode(cipher.encrypt(data)) enc_signature = base64.b64encode(hmac.new( host.secret.encode(), enc_data, hashlib.sha512).digest()) resp = flask.Response(response=enc_data, mimetype='application/base64') resp.headers.add('Cache-Control', 'no-cache, no-store, must-revalidate') resp.headers.add('Pragma', 'no-cache') resp.headers.add('Expires', 0) resp.headers.add('Cipher-IV', base64.b64encode(iv)) resp.headers.add('Cipher-Signature', enc_signature) return resp
def key_sync_get(org_id, user_id, server_id, key_hash): if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: return flask.abort(401) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: return flask.abort(401) except ValueError: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(401) user = org.get_user(id=user_id) if not user: return flask.abort(401) elif not user.sync_secret: return flask.abort(401) if auth_token != user.sync_token: return flask.abort(401) if user.disabled: return flask.abort(403) auth_string = '&'.join([ user.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: return flask.abort(401) auth_test_signature = base64.b64encode( hmac.new(user.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if auth_signature != auth_test_signature: return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: return flask.abort(401) key_conf = user.sync_conf(server_id, key_hash) if key_conf: user.audit_event( 'user_profile', 'User profile synced from pritunl client', remote_addr=utils.get_remote_addr(), ) sync_signature = base64.b64encode( hmac.new(user.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})