Skip to content

bpo-27820: Fix AUTH LOGIN logic in smtplib.SMTP #24118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 12, 2021

Conversation

pepoluan
Copy link
Contributor

@pepoluan pepoluan commented Jan 5, 2021

Previously, smtplib.SMTP.auth_login responded with username if challenge is not given, and always responded with password if challenge is given. This causes auth failure when initial_response_ok=False.

The standard for AUTH LOGIN specifies that clients should simply respond with base64-encoded username on first challenge, and with base64-encoded password on second challenge, disregarding the actual contents of the challenges themselves.

Suitable test cases have been added to test_smtplib.py + a fix for a long-standing bug in test_smtplib.py where setting initial_response_ok=False will always result in "Internal Confusion" error.

https://bugs.python.org/issue27820

@@ -785,7 +785,7 @@ def found_terminator(self):
except ResponseException as e:
self.smtp_state = self.COMMAND
self.push('%s %s' % (e.smtp_code, e.smtp_error))
return
return
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The mis-indented return caused the test for smtpd state to "fall through" to the next test, resulting ultimately in "Internal Confusion" error.

@@ -662,7 +666,7 @@ def auth_plain(self, challenge=None):
def auth_login(self, challenge=None):
""" Authobject to use with LOGIN authentication. Requires self.user and
self.password to be set."""
if challenge is None:
if challenge is None or self._auth_challenge_count < 2:
Copy link
Contributor Author

@pepoluan pepoluan Jan 10, 2021

Choose a reason for hiding this comment

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

The draft standard for AUTH LOGIN specifies that clients SHOULD ignore the actual contents of the challenge, and respond only based on how many challenges have been received.

@github-actions
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Feb 10, 2021
@pepoluan
Copy link
Contributor Author

This PR is stale because it has been open for 30 days with no activity.

Well, it's a clean, non-conflicting PR (so far) ... so there's no need to keep updating it? 😅

@github-actions github-actions bot removed the stale Stale PR or inactive for long period of time. label Feb 11, 2021
@pepoluan
Copy link
Contributor Author

Bump

@orsenthil orsenthil self-assigned this Mar 9, 2021
And in the process also fix a longstanding bug in the
SimSMTPChannel.found_terminator() method that causes inability to test
SMTP AUTH with initial_response_ok=False.
Copy link
Contributor Author

@pepoluan pepoluan left a comment

Choose a reason for hiding this comment

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

Response to some questions.

Broken/buggy server might 'trap' us in AUTH.

We raise an exception and let the user handle things.
@pepoluan pepoluan force-pushed the bpo-27820-fix-authlogin branch from 5c8f4d8 to 481ca55 Compare March 10, 2021 04:21
Comment on lines +649 to +654
# If server keeps sending challenges, something is wrong.
if self._auth_challenge_count > _MAXCHALLENGE:
raise SMTPException(
"Server AUTH mechanism infinite loop. Last response: "
+ repr((code, resp))
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This guards against broken/buggy server that traps us in the AUTH state.

At this point, state is indeterminate, and we should let user handle things. That's why it simply raises. (The only "guaranteed safe" thing to do is close(), actually.)

"AUTH trap" is when server keeps sending AUTH Challenge (334 ...)
Comment on lines +854 to +857
def _auth_buggy(self, arg=None):
# This AUTH mechanism will 'trap' client in a neverending 334
# base64 encoded 'BuGgYbUgGy'
self.push('334 QnVHZ1liVWdHeQ==')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Totally made-up AUTH mechanism whose purpose is to 'trap' the client in an infinite loop of 334 (AUTH Challenge)

Comment on lines +1102 to +1113
smtp = smtplib.SMTP(
HOST, self.port, local_hostname='localhost',
timeout=support.LOOPBACK_TIMEOUT
)
try:
smtp.user, smtp.password = sim_auth
smtp.ehlo("test_auth_buggy")
expect = r"^Server AUTH mechanism infinite loop.*"
with self.assertRaisesRegex(smtplib.SMTPException, expect) as cm:
smtp.auth("BUGGY", auth_buggy, initial_response_ok=False)
finally:
smtp.close()
Copy link
Contributor Author

@pepoluan pepoluan Mar 10, 2021

Choose a reason for hiding this comment

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

Can't use smtplib.SMTP as context manager here ... its __exit__ causes a different exception to be raised in addition. So the test uses a decidedly "lower level" mechanism to close the connection.

VSCode did not indicate any problems, that's why I missed this
during previous push.
Copy link
Member

@orsenthil orsenthil left a comment

Choose a reason for hiding this comment

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

LGTM. Thank you, @pepoluan .

@miss-islington
Copy link
Contributor

Thanks @pepoluan for the PR, and @orsenthil for merging it 🌮🎉.. I'm working now to backport this PR to: 3.8, 3.9.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Mar 12, 2021
* Fix auth_login logic (bpo-27820)

* Also fix a longstanding bug in the SimSMTPChannel.found_terminator() method that causes inability to test
SMTP AUTH with initial_response_ok=False.
(cherry picked from commit 7591d94)

Co-authored-by: Pandu E POLUAN <[email protected]>
@bedevere-bot
Copy link

GH-24832 is a backport of this pull request to the 3.9 branch.

@bedevere-bot bedevere-bot removed the needs backport to 3.9 only security fixes label Mar 12, 2021
@miss-islington
Copy link
Contributor

Sorry, @pepoluan and @orsenthil, I could not cleanly backport this to 3.8 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 7591d9455eb37525c832da3d65e1a7b3e6dbf613 3.8

@bedevere-bot
Copy link

GH-24833 is a backport of this pull request to the 3.8 branch.

orsenthil pushed a commit that referenced this pull request Mar 13, 2021
* Fix auth_login logic (bpo-27820)

* Also fix a longstanding bug in the SimSMTPChannel.found_terminator() method that causes inability to test
SMTP AUTH with initial_response_ok=False.
(cherry picked from commit 7591d94)

Co-authored-by: Pandu E POLUAN <[email protected]>
orsenthil added a commit that referenced this pull request Mar 13, 2021
)

* bpo-27820: Fix AUTH LOGIN logic in smtplib.SMTP (GH-24118)

* Fix auth_login logic (bpo-27820)

* Also fix a longstanding bug in the SimSMTPChannel.found_terminator() method that causes inability to test
SMTP AUTH with initial_response_ok=False.

(cherry picked from commit 7591d94)

* Set timeout to 15 directly.

Co-authored-by: Pandu E POLUAN <[email protected]>
sthagen added a commit to sthagen/python-cpython that referenced this pull request Mar 13, 2021
bpo-27820: Fix AUTH LOGIN logic in smtplib.SMTP (pythonGH-24118)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants