From d5fb561709cf2181cd5b8fffd2cf70a3fb52e5ab Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Mon, 20 Jun 2016 17:53:38 +0100 Subject: [PATCH 1/3] Optionally make committing to postgres asynchronous. Useful when running tests when you don't care whether the server will lose data that it claims that it has committed. --- synapse/storage/engines/__init__.py | 2 +- synapse/storage/engines/postgres.py | 13 ++++++++++++- synapse/storage/engines/sqlite3.py | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/synapse/storage/engines/__init__.py b/synapse/storage/engines/__init__.py index 7bb5de1fe..338b49561 100644 --- a/synapse/storage/engines/__init__.py +++ b/synapse/storage/engines/__init__.py @@ -32,7 +32,7 @@ def create_engine(database_config): if engine_class: module = importlib.import_module(name) - return engine_class(module) + return engine_class(module, database_config) raise RuntimeError( "Unsupported database engine '%s'" % (name,) diff --git a/synapse/storage/engines/postgres.py b/synapse/storage/engines/postgres.py index c2290943b..a6ae79dfa 100644 --- a/synapse/storage/engines/postgres.py +++ b/synapse/storage/engines/postgres.py @@ -19,9 +19,10 @@ from ._base import IncorrectDatabaseSetup class PostgresEngine(object): single_threaded = False - def __init__(self, database_module): + def __init__(self, database_module, database_config): self.module = database_module self.module.extensions.register_type(self.module.extensions.UNICODE) + self.synchronous_commit = database_config.get("synchronous_commit", True) def check_database(self, txn): txn.execute("SHOW SERVER_ENCODING") @@ -40,9 +41,19 @@ class PostgresEngine(object): db_conn.set_isolation_level( self.module.extensions.ISOLATION_LEVEL_REPEATABLE_READ ) + # Asynchronous commit, don't wait for the server to call fsync before + # ending the transaction. + # https://www.postgresql.org/docs/current/static/wal-async-commit.html + if not self.synchronous_commit: + cursor = db_conn.cursor() + cursor.execute("SET synchronous_commit TO OFF") + cursor.close() def is_deadlock(self, error): if isinstance(error, self.module.DatabaseError): + # https://www.postgresql.org/docs/current/static/errcodes-appendix.html + # "40001" serialization_failure + # "40P01" deadlock_detected return error.pgcode in ["40001", "40P01"] return False diff --git a/synapse/storage/engines/sqlite3.py b/synapse/storage/engines/sqlite3.py index 14203aa50..755c9a1f0 100644 --- a/synapse/storage/engines/sqlite3.py +++ b/synapse/storage/engines/sqlite3.py @@ -21,7 +21,7 @@ import struct class Sqlite3Engine(object): single_threaded = True - def __init__(self, database_module): + def __init__(self, database_module, database_config): self.module = database_module def check_database(self, txn): From 067596d341a661e008195f7f3a6887ade7cafa32 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 7 Jul 2016 16:11:37 +0100 Subject: [PATCH 2/3] Fix bug where we did not correctly explode when multiple user_ids were set in macaroon --- synapse/api/auth.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/synapse/api/auth.py b/synapse/api/auth.py index 31e1abb96..a4d658a9d 100644 --- a/synapse/api/auth.py +++ b/synapse/api/auth.py @@ -637,17 +637,22 @@ class Auth(object): try: macaroon = pymacaroons.Macaroon.deserialize(macaroon_str) - self.validate_macaroon(macaroon, rights, self.hs.config.expire_access_token) - user_prefix = "user_id = " user = None + user_id = None guest = False for caveat in macaroon.caveats: if caveat.caveat_id.startswith(user_prefix): - user = UserID.from_string(caveat.caveat_id[len(user_prefix):]) + user_id = caveat.caveat_id[len(user_prefix):] + user = UserID.from_string(user_id) elif caveat.caveat_id == "guest = true": guest = True + self.validate_macaroon( + macaroon, rights, self.hs.config.expire_access_token, + user_id=user_id, + ) + if user is None: raise AuthError( self.TOKEN_NOT_FOUND_HTTP_STATUS, "No user caveat in macaroon", @@ -692,7 +697,7 @@ class Auth(object): errcode=Codes.UNKNOWN_TOKEN ) - def validate_macaroon(self, macaroon, type_string, verify_expiry): + def validate_macaroon(self, macaroon, type_string, verify_expiry, user_id): """ validate that a Macaroon is understood by and was signed by this server. @@ -707,7 +712,7 @@ class Auth(object): v = pymacaroons.Verifier() v.satisfy_exact("gen = 1") v.satisfy_exact("type = " + type_string) - v.satisfy_general(lambda c: c.startswith("user_id = ")) + v.satisfy_exact("user_id = %s" % user_id) v.satisfy_exact("guest = true") if verify_expiry: v.satisfy_general(self._verify_expiry) From f90cf150e2b51124bb6848980394c4368e0de73a Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 7 Jul 2016 16:33:00 +0100 Subject: [PATCH 3/3] Bump version and changelog --- CHANGES.rst | 8 ++++++++ synapse/__init__.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index ecaaa189d..e1d5e876d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,11 @@ +Changes in synapse v0.16.1-r1 (2016-07-08) +========================================== + +THIS IS A CRITICAL SECURITY UPDATE. + +This fixes a bug which allowed users' accounts to be accessed by unauthorised +users. + Changes in synapse v0.16.1 (2016-06-20) ======================================= diff --git a/synapse/__init__.py b/synapse/__init__.py index 3cd79b124..2750ad3f7 100644 --- a/synapse/__init__.py +++ b/synapse/__init__.py @@ -16,4 +16,4 @@ """ This is a reference implementation of a Matrix home server. """ -__version__ = "0.16.1" +__version__ = "0.16.1-r1"