mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-08-02 22:06:04 -04:00
Fix inconsistent handling of upper and lower cases of email addresses. (#7021)
fixes #7016
This commit is contained in:
parent
8097659f6e
commit
21a212f8e5
8 changed files with 282 additions and 51 deletions
|
@ -45,6 +45,7 @@ from synapse.metrics.background_process_metrics import run_as_background_process
|
|||
from synapse.module_api import ModuleApi
|
||||
from synapse.push.mailer import load_jinja2_templates
|
||||
from synapse.types import Requester, UserID
|
||||
from synapse.util.threepids import canonicalise_email
|
||||
|
||||
from ._base import BaseHandler
|
||||
|
||||
|
@ -928,7 +929,7 @@ class AuthHandler(BaseHandler):
|
|||
# for the presence of an email address during password reset was
|
||||
# case sensitive).
|
||||
if medium == "email":
|
||||
address = address.lower()
|
||||
address = canonicalise_email(address)
|
||||
|
||||
await self.store.user_add_threepid(
|
||||
user_id, medium, address, validated_at, self.hs.get_clock().time_msec()
|
||||
|
@ -956,7 +957,7 @@ class AuthHandler(BaseHandler):
|
|||
|
||||
# 'Canonicalise' email addresses as per above
|
||||
if medium == "email":
|
||||
address = address.lower()
|
||||
address = canonicalise_email(address)
|
||||
|
||||
identity_handler = self.hs.get_handlers().identity_handler
|
||||
result = await identity_handler.try_unbind_threepid(
|
||||
|
|
|
@ -28,6 +28,7 @@ from synapse.rest.client.v2_alpha._base import client_patterns
|
|||
from synapse.rest.well_known import WellKnownBuilder
|
||||
from synapse.types import UserID
|
||||
from synapse.util.msisdn import phone_number_to_msisdn
|
||||
from synapse.util.threepids import canonicalise_email
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -206,11 +207,14 @@ class LoginRestServlet(RestServlet):
|
|||
if medium is None or address is None:
|
||||
raise SynapseError(400, "Invalid thirdparty identifier")
|
||||
|
||||
# For emails, canonicalise the address.
|
||||
# We store all email addresses canonicalised in the DB.
|
||||
# (See add_threepid in synapse/handlers/auth.py)
|
||||
if medium == "email":
|
||||
# For emails, transform the address to lowercase.
|
||||
# We store all email addreses as lowercase in the DB.
|
||||
# (See add_threepid in synapse/handlers/auth.py)
|
||||
address = address.lower()
|
||||
try:
|
||||
address = canonicalise_email(address)
|
||||
except ValueError as e:
|
||||
raise SynapseError(400, str(e))
|
||||
|
||||
# We also apply account rate limiting using the 3PID as a key, as
|
||||
# otherwise using 3PID bypasses the ratelimiting based on user ID.
|
||||
|
|
|
@ -30,7 +30,7 @@ from synapse.http.servlet import (
|
|||
from synapse.push.mailer import Mailer, load_jinja2_templates
|
||||
from synapse.util.msisdn import phone_number_to_msisdn
|
||||
from synapse.util.stringutils import assert_valid_client_secret, random_string
|
||||
from synapse.util.threepids import check_3pid_allowed
|
||||
from synapse.util.threepids import canonicalise_email, check_3pid_allowed
|
||||
|
||||
from ._base import client_patterns, interactive_auth_handler
|
||||
|
||||
|
@ -83,7 +83,15 @@ class EmailPasswordRequestTokenRestServlet(RestServlet):
|
|||
client_secret = body["client_secret"]
|
||||
assert_valid_client_secret(client_secret)
|
||||
|
||||
email = body["email"]
|
||||
# Canonicalise the email address. The addresses are all stored canonicalised
|
||||
# in the database. This allows the user to reset his password without having to
|
||||
# know the exact spelling (eg. upper and lower case) of address in the database.
|
||||
# Stored in the database "foo@bar.com"
|
||||
# User requests with "FOO@bar.com" would raise a Not Found error
|
||||
try:
|
||||
email = canonicalise_email(body["email"])
|
||||
except ValueError as e:
|
||||
raise SynapseError(400, str(e))
|
||||
send_attempt = body["send_attempt"]
|
||||
next_link = body.get("next_link") # Optional param
|
||||
|
||||
|
@ -94,6 +102,10 @@ class EmailPasswordRequestTokenRestServlet(RestServlet):
|
|||
Codes.THREEPID_DENIED,
|
||||
)
|
||||
|
||||
# The email will be sent to the stored address.
|
||||
# This avoids a potential account hijack by requesting a password reset to
|
||||
# an email address which is controlled by the attacker but which, after
|
||||
# canonicalisation, matches the one in our database.
|
||||
existing_user_id = await self.hs.get_datastore().get_user_id_by_threepid(
|
||||
"email", email
|
||||
)
|
||||
|
@ -274,10 +286,13 @@ class PasswordRestServlet(RestServlet):
|
|||
if "medium" not in threepid or "address" not in threepid:
|
||||
raise SynapseError(500, "Malformed threepid")
|
||||
if threepid["medium"] == "email":
|
||||
# For emails, transform the address to lowercase.
|
||||
# We store all email addreses as lowercase in the DB.
|
||||
# For emails, canonicalise the address.
|
||||
# We store all email addresses canonicalised in the DB.
|
||||
# (See add_threepid in synapse/handlers/auth.py)
|
||||
threepid["address"] = threepid["address"].lower()
|
||||
try:
|
||||
threepid["address"] = canonicalise_email(threepid["address"])
|
||||
except ValueError as e:
|
||||
raise SynapseError(400, str(e))
|
||||
# if using email, we must know about the email they're authing with!
|
||||
threepid_user_id = await self.datastore.get_user_id_by_threepid(
|
||||
threepid["medium"], threepid["address"]
|
||||
|
@ -392,7 +407,16 @@ class EmailThreepidRequestTokenRestServlet(RestServlet):
|
|||
client_secret = body["client_secret"]
|
||||
assert_valid_client_secret(client_secret)
|
||||
|
||||
email = body["email"]
|
||||
# Canonicalise the email address. The addresses are all stored canonicalised
|
||||
# in the database.
|
||||
# This ensures that the validation email is sent to the canonicalised address
|
||||
# as it will later be entered into the database.
|
||||
# Otherwise the email will be sent to "FOO@bar.com" and stored as
|
||||
# "foo@bar.com" in database.
|
||||
try:
|
||||
email = canonicalise_email(body["email"])
|
||||
except ValueError as e:
|
||||
raise SynapseError(400, str(e))
|
||||
send_attempt = body["send_attempt"]
|
||||
next_link = body.get("next_link") # Optional param
|
||||
|
||||
|
@ -403,9 +427,7 @@ class EmailThreepidRequestTokenRestServlet(RestServlet):
|
|||
Codes.THREEPID_DENIED,
|
||||
)
|
||||
|
||||
existing_user_id = await self.store.get_user_id_by_threepid(
|
||||
"email", body["email"]
|
||||
)
|
||||
existing_user_id = await self.store.get_user_id_by_threepid("email", email)
|
||||
|
||||
if existing_user_id is not None:
|
||||
if self.config.request_token_inhibit_3pid_errors:
|
||||
|
|
|
@ -47,7 +47,7 @@ from synapse.push.mailer import load_jinja2_templates
|
|||
from synapse.util.msisdn import phone_number_to_msisdn
|
||||
from synapse.util.ratelimitutils import FederationRateLimiter
|
||||
from synapse.util.stringutils import assert_valid_client_secret, random_string
|
||||
from synapse.util.threepids import check_3pid_allowed
|
||||
from synapse.util.threepids import canonicalise_email, check_3pid_allowed
|
||||
|
||||
from ._base import client_patterns, interactive_auth_handler
|
||||
|
||||
|
@ -116,7 +116,14 @@ class EmailRegisterRequestTokenRestServlet(RestServlet):
|
|||
client_secret = body["client_secret"]
|
||||
assert_valid_client_secret(client_secret)
|
||||
|
||||
email = body["email"]
|
||||
# For emails, canonicalise the address.
|
||||
# We store all email addresses canonicalised in the DB.
|
||||
# (See on_POST in EmailThreepidRequestTokenRestServlet
|
||||
# in synapse/rest/client/v2_alpha/account.py)
|
||||
try:
|
||||
email = canonicalise_email(body["email"])
|
||||
except ValueError as e:
|
||||
raise SynapseError(400, str(e))
|
||||
send_attempt = body["send_attempt"]
|
||||
next_link = body.get("next_link") # Optional param
|
||||
|
||||
|
@ -128,7 +135,7 @@ class EmailRegisterRequestTokenRestServlet(RestServlet):
|
|||
)
|
||||
|
||||
existing_user_id = await self.hs.get_datastore().get_user_id_by_threepid(
|
||||
"email", body["email"]
|
||||
"email", email
|
||||
)
|
||||
|
||||
if existing_user_id is not None:
|
||||
|
@ -552,6 +559,15 @@ class RegisterRestServlet(RestServlet):
|
|||
if login_type in auth_result:
|
||||
medium = auth_result[login_type]["medium"]
|
||||
address = auth_result[login_type]["address"]
|
||||
# For emails, canonicalise the address.
|
||||
# We store all email addresses canonicalised in the DB.
|
||||
# (See on_POST in EmailThreepidRequestTokenRestServlet
|
||||
# in synapse/rest/client/v2_alpha/account.py)
|
||||
if medium == "email":
|
||||
try:
|
||||
address = canonicalise_email(address)
|
||||
except ValueError as e:
|
||||
raise SynapseError(400, str(e))
|
||||
|
||||
existing_user_id = await self.store.get_user_id_by_threepid(
|
||||
medium, address
|
||||
|
|
|
@ -48,3 +48,26 @@ def check_3pid_allowed(hs, medium, address):
|
|||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def canonicalise_email(address: str) -> str:
|
||||
"""'Canonicalise' email address
|
||||
Case folding of local part of email address and lowercase domain part
|
||||
See MSC2265, https://github.com/matrix-org/matrix-doc/pull/2265
|
||||
|
||||
Args:
|
||||
address: email address to be canonicalised
|
||||
Returns:
|
||||
The canonical form of the email address
|
||||
Raises:
|
||||
ValueError if the address could not be parsed.
|
||||
"""
|
||||
|
||||
address = address.strip()
|
||||
|
||||
parts = address.split("@")
|
||||
if len(parts) != 2:
|
||||
logger.debug("Couldn't parse email address %s", address)
|
||||
raise ValueError("Unable to parse email address")
|
||||
|
||||
return parts[0].casefold() + "@" + parts[1].lower()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue