Skip to content

Commit bd19262

Browse files
authored
Properly handle exceptions raised while handling server auth messages (#862)
When server sends us an authentication request message and we fail to process it, we must terminate the connection and propagate the exception immediately. Currently asyncpg will just timeout waiting for `ReadyForQuery` from the server, which will never arrive. Fixes: #861
1 parent fb3b6bf commit bd19262

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

asyncpg/protocol/coreproto.pyx

+21-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
66

77

8-
from hashlib import md5 as hashlib_md5 # for MD5 authentication
8+
import hashlib
99

1010

1111
include "scram.pyx"
@@ -150,15 +150,28 @@ cdef class CoreProtocol:
150150
cdef _process__auth(self, char mtype):
151151
if mtype == b'R':
152152
# Authentication...
153-
self._parse_msg_authentication()
154-
if self.result_type != RESULT_OK:
153+
try:
154+
self._parse_msg_authentication()
155+
except Exception as ex:
156+
# Exception in authentication parsing code
157+
# is usually either malformed authentication data
158+
# or missing support for cryptographic primitives
159+
# in the hashlib module.
160+
self.result_type = RESULT_FAILED
161+
self.result = apg_exc.InternalClientError(
162+
f"unexpected error while performing authentication: {ex}")
163+
self.result.__cause__ = ex
155164
self.con_status = CONNECTION_BAD
156165
self._push_result()
166+
else:
167+
if self.result_type != RESULT_OK:
168+
self.con_status = CONNECTION_BAD
169+
self._push_result()
157170

158-
elif self.auth_msg is not None:
159-
# Server wants us to send auth data, so do that.
160-
self._write(self.auth_msg)
161-
self.auth_msg = None
171+
elif self.auth_msg is not None:
172+
# Server wants us to send auth data, so do that.
173+
self._write(self.auth_msg)
174+
self.auth_msg = None
162175

163176
elif mtype == b'K':
164177
# BackendKeyData
@@ -634,7 +647,7 @@ cdef class CoreProtocol:
634647

635648
# 'md5' + md5(md5(password + username) + salt))
636649
userpass = ((self.password or '') + (self.user or '')).encode('ascii')
637-
hash = hashlib_md5(hashlib_md5(userpass).hexdigest().\
650+
hash = hashlib.md5(hashlib.md5(userpass).hexdigest().\
638651
encode('ascii') + salt).hexdigest().encode('ascii')
639652

640653
msg.write_bytestring(b'md5' + hash)

tests/test_connect.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,13 @@ async def test_auth_password_scram_sha_256(self):
359359
await self.con.execute(alter_password)
360360
await self.con.execute("SET password_encryption = 'md5';")
361361

362-
async def test_auth_unsupported(self):
363-
pass
362+
@unittest.mock.patch('hashlib.md5', side_effect=ValueError("no md5"))
363+
async def test_auth_md5_unsupported(self, _):
364+
with self.assertRaisesRegex(
365+
exceptions.InternalClientError,
366+
".*no md5.*",
367+
):
368+
await self.connect(user='md5_user', password='correctpassword')
364369

365370

366371
class TestConnectParams(tb.TestCase):

0 commit comments

Comments
 (0)