1
0
mirror of https://github.com/Mailu/Mailu.git synced 2025-01-18 03:21:36 +02:00
2838: Authentication failed for email clients when the password contained a non latin-1 character. r=mergify[bot] a=Diman0

## What type of PR?

bug fix

## What does this PR do?
Fixes a bug that results in authentication failing for email clients when the password contains a non latin-1 character.
Issue was caused by the header Auth-Password being returned with non latin-1 characters. Headers must always be latin-1 encoded. Resolved the issue by url encoding the password.

Since the returned password is only used as a partial hash for the rate limiter, I did not add code for un-quoting the password in the /internal/email endpoint.

### Related issue(s)
- closes #2837 

## Prerequisites
Before we can consider review and merge, please make sure the following list is done and checked.
If an entry in not applicable, you can check it or remove it from the list.

- [x] In case of feature or enhancement: documentation updated accordingly
- [x] Unless it's docs or a minor change: add [changelog](https://mailu.io/master/contributors/workflow.html#changelog) entry file.


Co-authored-by: Dimitri Huisman <diman@huisman.xyz>
Co-authored-by: Florent Daigniere <nextgens@users.noreply.github.com>
This commit is contained in:
bors[bot] 2023-05-31 09:37:18 +00:00 committed by GitHub
commit 6f3ee32351
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 27 additions and 10 deletions

View File

@ -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

View File

@ -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

View File

@ -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!"

View File

@ -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.

View File

@ -0,0 +1 @@
Authentication failed for email clients when the password contained a non latin-1 character.