Browse Source

Validate client_secret parameter according to spec (#246)

Andrew Morgan 4 years ago
parent
commit
7a49644097

+ 16 - 1
sydent/http/servlets/emailservlet.py

@@ -16,6 +16,7 @@
 
 from twisted.web.resource import Resource
 
+from sydent.util.stringutils import is_valid_client_secret
 from sydent.util.emailutils import EmailAddressException, EmailSendException
 from sydent.validators.emailvalidator import SessionExpiredException
 from sydent.validators.emailvalidator import IncorrectClientSecretException
@@ -39,8 +40,15 @@ class EmailRequestCodeServlet(Resource):
         args = get_args(request, ('email', 'client_secret', 'send_attempt'))
 
         email = args['email']
-        clientSecret = args['client_secret']
         sendAttempt = args['send_attempt']
+        clientSecret = args['client_secret']
+
+        if not is_valid_client_secret(clientSecret):
+            request.setResponseCode(400)
+            return {
+                'errcode': 'M_INVALID_PARAM',
+                'error': 'Invalid client_secret provided'
+            }
 
         ipaddress = self.sydent.ip_from_request(request)
 
@@ -114,6 +122,13 @@ class EmailValidateCodeServlet(Resource):
         tokenString = args['token']
         clientSecret = args['client_secret']
 
+        if not is_valid_client_secret(clientSecret):
+            request.setResponseCode(400)
+            return {
+                'errcode': 'M_INVALID_PARAM',
+                'error': 'Invalid client_secret provided'
+            }
+
         try:
             resp = self.sydent.validators.email.validateSessionWithToken(sid, clientSecret, tokenString)
         except IncorrectClientSecretException:

+ 8 - 0
sydent/http/servlets/getvalidated3pidservlet.py

@@ -19,6 +19,7 @@ from twisted.web.resource import Resource
 from sydent.http.servlets import jsonwrap, get_args
 from sydent.http.auth import authIfV2
 from sydent.db.valsession import ThreePidValSessionStore
+from sydent.util.stringutils import is_valid_client_secret
 from sydent.validators import SessionExpiredException, IncorrectClientSecretException, InvalidSessionIdException,\
     SessionNotValidatedException
 
@@ -37,6 +38,13 @@ class GetValidated3pidServlet(Resource):
         sid = args['sid']
         clientSecret = args['client_secret']
 
+        if not is_valid_client_secret(clientSecret):
+            request.setResponseCode(400)
+            return {
+                'errcode': 'M_INVALID_PARAM',
+                'error': 'Invalid client_secret provided'
+            }
+
         valSessionStore = ThreePidValSessionStore(self.sydent)
 
         noMatchError = {'errcode': 'M_NO_VALID_SESSION',

+ 16 - 1
sydent/http/servlets/msisdnservlet.py

@@ -25,6 +25,7 @@ from sydent.validators import (
 
 from sydent.http.servlets import get_args, jsonwrap, send_cors
 from sydent.http.auth import authIfV2
+from sydent.util.stringutils import is_valid_client_secret
 
 
 logger = logging.getLogger(__name__)
@@ -46,8 +47,15 @@ class MsisdnRequestCodeServlet(Resource):
 
         raw_phone_number = args['phone_number']
         country = args['country']
-        clientSecret = args['client_secret']
         sendAttempt = args['send_attempt']
+        clientSecret = args['client_secret']
+
+        if not is_valid_client_secret(clientSecret):
+            request.setResponseCode(400)
+            return {
+                'errcode': 'M_INVALID_PARAM',
+                'error': 'Invalid client_secret provided'
+            }
 
         try:
             phone_number_object = phonenumbers.parse(raw_phone_number, country)
@@ -116,6 +124,7 @@ class MsisdnValidateCodeServlet(Resource):
                     request.setResponseCode(302)
                     request.setHeader("Location", next_link)
             else:
+                request.setResponseCode(400)
                 msg = "Verification failed: you may need to request another verification text"
 
         templateFile = self.sydent.cfg.get('http', 'verify_response_template')
@@ -138,6 +147,12 @@ class MsisdnValidateCodeServlet(Resource):
         tokenString = args['token']
         clientSecret = args['client_secret']
 
+        if not is_valid_client_secret(clientSecret):
+            return {
+                'errcode': 'M_INVALID_PARAM',
+                'error': 'Invalid client_secret provided'
+            }
+
         try:
             resp = self.sydent.validators.msisdn.validateSessionWithToken(sid, clientSecret, tokenString)
         except IncorrectClientSecretException:

+ 9 - 0
sydent/http/servlets/threepidbindservlet.py

@@ -20,9 +20,11 @@ from twisted.web.resource import Resource
 from sydent.db.valsession import ThreePidValSessionStore
 from sydent.http.servlets import get_args, jsonwrap, send_cors, MatrixRestError
 from sydent.http.auth import authIfV2
+from sydent.util.stringutils import is_valid_client_secret
 from sydent.validators import SessionExpiredException, IncorrectClientSecretException, InvalidSessionIdException,\
     SessionNotValidatedException
 
+
 class ThreePidBindServlet(Resource):
     def __init__(self, sydent):
         self.sydent = sydent
@@ -39,6 +41,13 @@ class ThreePidBindServlet(Resource):
         mxid = args['mxid']
         clientSecret = args['client_secret']
 
+        if not is_valid_client_secret(clientSecret):
+            request.setResponseCode(400)
+            return {
+                'errcode': 'M_INVALID_PARAM',
+                'error': 'Invalid client_secret provided'
+            }
+
         # Return the same error for not found / bad client secret otherwise people can get information about
         # sessions without knowing the secret
         noMatchError = {'errcode': 'M_NO_VALID_SESSION',

+ 15 - 3
sydent/http/servlets/threepidunbindservlet.py

@@ -18,12 +18,15 @@
 import json
 import logging
 
-from sydent.http.servlets import get_args, jsonwrap
 from sydent.hs_federation.verifier import NoAuthenticationError
 from signedjson.sign import SignatureVerifyException
 from sydent.db.valsession import ThreePidValSessionStore
-from sydent.validators import SessionExpiredException, IncorrectClientSecretException, InvalidSessionIdException,\
-    SessionNotValidatedException
+from sydent.util.stringutils import is_valid_client_secret
+from sydent.validators import (
+    IncorrectClientSecretException,
+    InvalidSessionIdException,
+    SessionNotValidatedException,
+)
 
 from twisted.web.resource import Resource
 from twisted.web import server
@@ -85,6 +88,15 @@ class ThreePidUnbindServlet(Resource):
                 sid = body['sid']
                 client_secret = body['client_secret']
 
+                if not is_valid_client_secret(client_secret):
+                    request.setResponseCode(400)
+                    request.write(json.dumps({
+                        'errcode': 'M_INVALID_PARAM',
+                        'error': 'Invalid client_secret provided'
+                    }))
+                    request.finish()
+                    return
+
                 valSessionStore = ThreePidValSessionStore(self.sydent)
 
                 noMatchError = {'errcode': 'M_NO_VALID_SESSION',

+ 30 - 0
sydent/util/stringutils.py

@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 The Matrix.org Foundation C.I.C.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import re
+
+# https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-register-email-requesttoken
+# Note: The : character is allowed here for older clients, but will be removed in a
+# future release. Context: https://github.com/matrix-org/sydent/issues/247
+client_secret_regex = re.compile(r"^[0-9a-zA-Z\.\=\_\-\:]+$")
+
+
+def is_valid_client_secret(client_secret):
+    """Validate that a given string matches the client_secret regex defined by the spec
+    :param client_secret: The client_secret to validate
+    :type client_secret: str
+    :returns: Whether the client_secret is valid
+    :rtype: bool
+    """
+    return client_secret_regex.match(client_secret) is not None