diff --git a/sydent/http/servlets/emailservlet.py b/sydent/http/servlets/emailservlet.py index 077d15f5..1513cdf4 100644 --- a/sydent/http/servlets/emailservlet.py +++ b/sydent/http/servlets/emailservlet.py @@ -17,7 +17,7 @@ from twisted.web.resource import Resource -from sydent.util.stringutils import is_valid_client_secret +from sydent.util.stringutils import is_valid_client_secret, MAX_EMAIL_ADDRESS_LENGTH from sydent.util.emailutils import EmailAddressException, EmailSendException from sydent.validators import ( IncorrectClientSecretException, @@ -58,6 +58,13 @@ def render_POST(self, request): 'error': 'Invalid client_secret provided' } + if not (0 < len(email) <= MAX_EMAIL_ADDRESS_LENGTH): + request.setResponseCode(400) + return { + 'errcode': 'M_INVALID_PARAM', + 'error': 'Invalid email provided' + } + ipaddress = self.sydent.ip_from_request(request) brand = self.sydent.brand_from_request(request) diff --git a/sydent/http/servlets/store_invite_servlet.py b/sydent/http/servlets/store_invite_servlet.py index 3dce2a11..e30b9c19 100644 --- a/sydent/http/servlets/store_invite_servlet.py +++ b/sydent/http/servlets/store_invite_servlet.py @@ -30,6 +30,8 @@ from sydent.http.servlets import get_args, send_cors, jsonwrap, MatrixRestError from sydent.http.auth import authV2 from sydent.util.emailutils import sendEmail +from sydent.util.stringutils import MAX_EMAIL_ADDRESS_LENGTH + class StoreInviteServlet(Resource): def __init__(self, syd, require_auth=False): @@ -71,6 +73,13 @@ def render_POST(self, request): "error": "Didn't understand medium '%s'" % (medium,), } + if not (0 < len(address) <= MAX_EMAIL_ADDRESS_LENGTH): + request.setResponseCode(400) + return { + 'errcode': 'M_INVALID_PARAM', + 'error': 'Invalid email provided' + } + token = self._randomString(128) tokenStore = JoinTokenStore(self.sydent) diff --git a/sydent/util/stringutils.py b/sydent/util/stringutils.py index fdf24009..b4726403 100644 --- a/sydent/util/stringutils.py +++ b/sydent/util/stringutils.py @@ -18,7 +18,7 @@ from twisted.internet.abstract import isIPAddress, isIPv6Address # https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-register-email-requesttoken -client_secret_regex = re.compile(r"^[0-9a-zA-Z\.\=\_\-]+$") +CLIENT_SECRET_REGEX = re.compile(r"^[0-9a-zA-Z\.=_\-]+$") # hostname/domain name # https://regex101.com/r/OyN1lg/2 @@ -26,6 +26,15 @@ r"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$", flags=re.IGNORECASE) +# it's unclear what the maximum length of an email address is. RFC3696 (as corrected +# by errata) says: +# the upper limit on address lengths should normally be considered to be 254. +# +# In practice, mail servers appear to be more tolerant and allow 400 characters +# or so. Let's allow 500, which should be plenty for everyone. +# +MAX_EMAIL_ADDRESS_LENGTH = 500 + def is_valid_client_secret(client_secret): """Validate that a given string matches the client_secret regex defined by the spec @@ -36,7 +45,10 @@ def is_valid_client_secret(client_secret): :return: Whether the client_secret is valid :rtype: bool """ - return client_secret_regex.match(client_secret) is not None + return ( + 0 < len(client_secret) <= 255 + and CLIENT_SECRET_REGEX.match(client_secret) is not None + ) def is_valid_hostname(string: str) -> bool: