summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Baker <dave@matrix.org>2015-04-02 17:01:29 +0100
committerDavid Baker <dave@matrix.org>2015-04-02 17:01:29 +0100
commit70a84f17f39bbc5c8a68541874ca4767871f2b79 (patch)
tree6d821d5eaa378b9c37ca611a0ffb2f9c8e628eda
parentExplain how I justified to myself making JsonResource not always send JSON. (diff)
downloadsynapse-70a84f17f39bbc5c8a68541874ca4767871f2b79.tar.xz
Add shared secret auth into register v2 and switch the script over.
-rwxr-xr-xregister_new_matrix_user5
-rw-r--r--synapse/api/constants.py4
-rw-r--r--synapse/rest/client/v2_alpha/register.py69
3 files changed, 63 insertions, 15 deletions
diff --git a/register_new_matrix_user b/register_new_matrix_user
index daddadc302..f833d2a4db 100755
--- a/register_new_matrix_user
+++ b/register_new_matrix_user
@@ -33,10 +33,9 @@ def request_registration(user, password, server_location, shared_secret):
     ).hexdigest()
 
     data = {
-        "user": user,
+        "username": user,
         "password": password,
         "mac": mac,
-        "type": "org.matrix.login.shared_secret",
     }
 
     server_location = server_location.rstrip("/")
@@ -44,7 +43,7 @@ def request_registration(user, password, server_location, shared_secret):
     print "Sending registration request..."
 
     req = urllib2.Request(
-        "%s/_matrix/client/api/v1/register" % (server_location,),
+        "%s/_matrix/client/v2_alpha/register" % (server_location,),
         data=json.dumps(data),
         headers={'Content-Type': 'application/json'}
     )
diff --git a/synapse/api/constants.py b/synapse/api/constants.py
index 3e0ce170a4..f825c1a58b 100644
--- a/synapse/api/constants.py
+++ b/synapse/api/constants.py
@@ -60,9 +60,11 @@ class LoginType(object):
     EMAIL_IDENTITY = u"m.login.email.identity"
     RECAPTCHA = u"m.login.recaptcha"
     APPLICATION_SERVICE = u"m.login.application_service"
+
+    # Only for C/S API v1
     SHARED_SECRET = u"org.matrix.login.shared_secret"
 
-    HIDDEN_TYPES = [APPLICATION_SERVICE, SHARED_SECRET]
+    HIDDEN_TYPES = [APPLICATION_SERVICE]
 
 
 class EventTypes(object):
diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py
index 537918ea27..a69b45f362 100644
--- a/synapse/rest/client/v2_alpha/register.py
+++ b/synapse/rest/client/v2_alpha/register.py
@@ -22,6 +22,19 @@ from synapse.http.servlet import RestServlet
 from ._base import client_v2_pattern, parse_request_allow_empty
 
 import logging
+import hmac
+from hashlib import sha1
+from synapse.util.async import run_on_reactor
+
+
+# We ought to be using hmac.compare_digest() but on older pythons it doesn't
+# exist. It's a _really minor_ security flaw to use plain string comparison
+# because the timing attack is so obscured by all the other code here it's
+# unlikely to make much difference
+if hasattr(hmac, "compare_digest"):
+    compare_digest = hmac.compare_digest
+else:
+    compare_digest = lambda a, b: a == b
 
 
 logger = logging.getLogger(__name__)
@@ -39,19 +52,30 @@ class RegisterRestServlet(RestServlet):
 
     @defer.inlineCallbacks
     def on_POST(self, request):
-        body = parse_request_allow_empty(request)
+        yield run_on_reactor()
 
-        authed, result = yield self.auth_handler.check_auth([
-            [LoginType.RECAPTCHA],
-            [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
-            [LoginType.APPLICATION_SERVICE]
-        ], body, self.hs.get_ip_from_request(request))
-
-        if not authed:
-            defer.returnValue((401, result))
+        body = parse_request_allow_empty(request)
 
-        is_application_server = LoginType.APPLICATION_SERVICE in result
-        is_using_shared_secret = LoginType.SHARED_SECRET in result
+        is_using_shared_secret = False
+        is_application_server = False
+
+        if 'mac' in body:
+            # Check registration-specific shared secret auth
+            if 'username' not in body:
+                raise SynapseError(400, "", Codes.MISSING_PARAM)
+            self._check_shared_secret_auth(
+                body['username'], body['mac']
+            )
+            is_using_shared_secret = True
+        else:
+            authed, result = yield self.auth_handler.check_auth([
+                [LoginType.RECAPTCHA],
+                [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
+                [LoginType.APPLICATION_SERVICE]
+            ], body, self.hs.get_ip_from_request(request))
+
+            if not authed:
+                defer.returnValue((401, result))
 
         can_register = (
             not self.hs.config.disable_registration
@@ -81,6 +105,29 @@ class RegisterRestServlet(RestServlet):
     def on_OPTIONS(self, _):
         return 200, {}
 
+    def _check_shared_secret_auth(self, username, mac):
+        if not self.hs.config.registration_shared_secret:
+            raise SynapseError(400, "Shared secret registration is not enabled")
+
+        user = username.encode("utf-8")
+
+        # str() because otherwise hmac complains that 'unicode' does not
+        # have the buffer interface
+        got_mac = str(mac)
+
+        want_mac = hmac.new(
+            key=self.hs.config.registration_shared_secret,
+            msg=user,
+            digestmod=sha1,
+        ).hexdigest()
+
+        if compare_digest(want_mac, got_mac):
+            return True
+        else:
+            raise SynapseError(
+                403, "HMAC incorrect",
+            )
+
 
 def register_servlets(hs, http_server):
     RegisterRestServlet(hs).register(http_server)