diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index b76e9a89..63dec50a 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -111,7 +111,6 @@ def handle_authentication(headers): "Auth-Server": server, "Auth-User": user_email, "Auth-User-Exists": is_valid_user, - "Auth-Password": password, "Auth-Port": port } status, code = get_status(protocol, "authentication") @@ -120,7 +119,6 @@ def handle_authentication(headers): "Auth-Error-Code": code, "Auth-User": user_email, "Auth-User-Exists": is_valid_user, - "Auth-Password": password, "Auth-Wait": 0 } # Unexpected diff --git a/core/admin/mailu/internal/views/auth.py b/core/admin/mailu/internal/views/auth.py index 60e16297..51ffda9f 100644 --- a/core/admin/mailu/internal/views/auth.py +++ b/core/admin/mailu/internal/views/auth.py @@ -6,6 +6,7 @@ import flask import flask_login import base64 import sqlalchemy.exc +import urllib @internal.route("/auth/email") def nginx_authentication(): @@ -30,6 +31,7 @@ def nginx_authentication(): if int(flask.request.headers['Auth-Login-Attempt']) < 10: response.headers['Auth-Wait'] = '3' return response + raw_password = urllib.parse.unquote(headers["Auth-Pass"]) headers = nginx.handle_authentication(flask.request.headers) response = flask.Response() for key, value in headers.items(): @@ -52,7 +54,14 @@ def nginx_authentication(): if not is_port_25: utils.limiter.exempt_ip_from_ratelimits(client_ip) elif is_valid_user: - utils.limiter.rate_limit_user(username, client_ip, password=response.headers.get('Auth-Password', None)) + password = None + try: + password = raw_password.encode("iso8859-1").decode("utf8") + except: + app.logger.warn(f'Received undecodable password for {username} from nginx: {raw_password!r}') + utils.limiter.rate_limit_user(username, client_ip, password=None) + else: + utils.limiter.rate_limit_user(username, client_ip, password=password) elif not is_from_webmail: utils.limiter.rate_limit_ip(client_ip, username) return response diff --git a/tests/compose/core/00_create_users.sh b/tests/compose/core/00_create_users.sh index 26461966..142c3cb3 100755 --- a/tests/compose/core/00_create_users.sh +++ b/tests/compose/core/00_create_users.sh @@ -8,4 +8,5 @@ docker compose -f tests/compose/core/docker-compose.yml exec -T admin flask mail docker compose -f tests/compose/core/docker-compose.yml exec -T admin flask mailu admin admin mailu.io 'password' --mode=update || exit 1 docker compose -f tests/compose/core/docker-compose.yml exec -T admin flask mailu user user mailu.io 'password' || exit 1 docker compose -f tests/compose/core/docker-compose.yml exec -T admin flask mailu user 'user/with/slash' mailu.io 'password' || exit 1 +docker compose -f tests/compose/core/docker-compose.yml exec -T admin flask mailu user 'user_UTF8' mailu.io 'password€' || exit 1 echo "User testing successful!" diff --git a/tests/compose/core/05_connectivity.py b/tests/compose/core/05_connectivity.py index cee02cf6..5cc12069 100755 --- a/tests/compose/core/05_connectivity.py +++ b/tests/compose/core/05_connectivity.py @@ -7,25 +7,31 @@ import sys import managesieve SERVER='localhost' -USERNAME='user@mailu.io' -PASSWORD='password' +USERNAME='user_UTF8@mailu.io' +PASSWORD='password€' +#https://github.com/python/cpython/issues/73936 +#SMTPlib does not support UTF8 passwords. +USERNAME_ASCII='user@mailu.io' +PASSWORD_ASCII='password' + def test_imap(server, username, password): + auth = lambda data : f'\x00{username}\x00{password}' print(f'Authenticating to imaps://{username}:{password}@{server}:993/') with imaplib.IMAP4_SSL(server) as conn: - conn.login(username, password) + conn.authenticate('PLAIN', auth) conn.noop() print('OK') print(f'Authenticating to imaps://{username}:{password}@{server}:143/') with imaplib.IMAP4(server) as conn: conn.starttls() - conn.login(username, password) + conn.authenticate('PLAIN', auth) conn.noop() print('OK') print(f'Authenticating to imap://{username}:{password}@{server}:143/') try: with imaplib.IMAP4(server) as conn: - conn.login(username, password) + conn.authenticate('PLAIN', auth) print(f'Authenticating to imap://{username}:{password}@{server}:143/ worked without STARTTLS!') sys.exit(102) except imaplib.IMAP4.error: @@ -121,7 +127,7 @@ def test_managesieve(server, username, password): if m.login('', username, password) != 'OK': print(f'Authenticating to sieve://{username}:{password}@{server}:4190/ has failed!') sys.exit(109) - + if m.listscripts()[0] != 'OK': print(f'Listing scripts failed!') sys.exit(110) @@ -130,5 +136,7 @@ def test_managesieve(server, username, password): if __name__ == '__main__': test_imap(SERVER, USERNAME, PASSWORD) test_pop3(SERVER, USERNAME, PASSWORD) - test_SMTP(SERVER, USERNAME, PASSWORD) + test_SMTP(SERVER, USERNAME_ASCII, PASSWORD_ASCII) test_managesieve(SERVER, USERNAME, PASSWORD) +#https://github.com/python/cpython/issues/73936 +#SMTPlib does not support UTF8 passwords. \ No newline at end of file diff --git a/towncrier/newsfragments/2837.bugfix b/towncrier/newsfragments/2837.bugfix new file mode 100644 index 00000000..bcbc095a --- /dev/null +++ b/towncrier/newsfragments/2837.bugfix @@ -0,0 +1 @@ +Authentication failed for email clients when the password contained a non latin-1 character. \ No newline at end of file