Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions Lib/smtplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@

__all__ = ["SMTPException", "SMTPNotSupportedError", "SMTPServerDisconnected", "SMTPResponseException",
"SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError",
"SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError",
"SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError", "SMTPAuthHashUnsupportedError",
"quoteaddr", "quotedata", "SMTP"]

SMTP_PORT = 25
Expand Down Expand Up @@ -141,6 +141,10 @@ class SMTPAuthenticationError(SMTPResponseException):
combination provided.
"""

class SMTPAuthHashUnsupportedError(SMTPException):
"""Raised when the authentication mechanism uses a hash algorithm unsupported by the system."""


def quoteaddr(addrstring):
"""Quote a subset of the email addresses defined by RFC 821.

Expand Down Expand Up @@ -665,8 +669,11 @@ def auth_cram_md5(self, challenge=None):
# CRAM-MD5 does not support initial-response.
if challenge is None:
return None
return self.user + " " + hmac.HMAC(
self.password.encode('ascii'), challenge, 'md5').hexdigest()
try:
return self.user + " " + hmac.HMAC(
self.password.encode('ascii'), challenge, 'md5').hexdigest()
except ValueError as e:
raise SMTPAuthHashUnsupportedError(f'CRAM-MD5 failed: {e}') from e
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate all calls and only wrap the hmac.HMAC call in a try-catch. TiA


def auth_plain(self, challenge=None):
""" Authobject to use with PLAIN authentication. Requires self.user and
Expand Down Expand Up @@ -743,13 +750,12 @@ def login(self, user, password, *, initial_response_ok=True):
return (code, resp)
except SMTPAuthenticationError as e:
last_exception = e
except ValueError as e:
except SMTPAuthHashUnsupportedError as e:
# Some environments (e.g., FIPS) disable certain hashing algorithms like MD5,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is too verbose. Just write something like "skip unsupported HMAC-HASH algorithms"

# which are required by CRAM-MD5. This raises a ValueError when trying to use HMAC.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name of the exception in the comment needs update

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, tks
I’ve updated the comment to reflect the correct exception name

# If this happens, we catch the exception and continue trying the next auth method.
last_exception = e
if 'unsupported' in str(e).lower():
continue
continue

# We could not login successfully. Return result of last attempt.
raise last_exception
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_smtplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,7 @@ def testAUTH_PLAIN_initial_response_auth(self):

class TestSMTPLoginValueError(unittest.TestCase):
def broken_hmac(*args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it an external function, and mock the entire class instead.

raise ValueError("[digital envelope routines] unsupported")
raise smtplib.SMTPAuthHashUnsupportedError("CRAM-MD5 failed: [digital envelope routines] unsupported")

def test_login_raises_valueerror_when_cram_md5_fails(self):
with patch("hmac.HMAC", self.broken_hmac):
Expand All @@ -1593,7 +1593,7 @@ def docmd(self, *args, **kwargs):
return 334, b"Y2hhbGxlbmdl"

smtp = FakeSMTP()
with self.assertRaises(ValueError) as ctx:
with self.assertRaises(smtplib.SMTPAuthHashUnsupportedError) as ctx:
smtp.login("user", "pass")
self.assertIn("unsupported", str(ctx.exception).lower())

Expand Down
Loading