mirror of
https://github.com/python/cpython.git
synced 2025-10-26 03:04:41 +00:00
[3.14] gh-136134: imaplib: fix CRAM-MD5 on FIPS-only environments (GH-136615) (#138054)
This commit is contained in:
parent
bfce393614
commit
d574f83206
4 changed files with 49 additions and 31 deletions
|
|
@ -413,6 +413,9 @@ An :class:`IMAP4` instance has the following methods:
|
||||||
the password. Will only work if the server ``CAPABILITY`` response includes the
|
the password. Will only work if the server ``CAPABILITY`` response includes the
|
||||||
phrase ``AUTH=CRAM-MD5``.
|
phrase ``AUTH=CRAM-MD5``.
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
An :exc:`IMAP4.error` is raised if MD5 support is not available.
|
||||||
|
|
||||||
|
|
||||||
.. method:: IMAP4.logout()
|
.. method:: IMAP4.logout()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
# GET/SETANNOTATION contributed by Tomas Lindroos <skitta@abo.fi> June 2005.
|
# GET/SETANNOTATION contributed by Tomas Lindroos <skitta@abo.fi> June 2005.
|
||||||
# IDLE contributed by Forest <forestix@nom.one> August 2024.
|
# IDLE contributed by Forest <forestix@nom.one> August 2024.
|
||||||
|
|
||||||
__version__ = "2.59"
|
__version__ = "2.60"
|
||||||
|
|
||||||
import binascii, errno, random, re, socket, subprocess, sys, time, calendar
|
import binascii, errno, random, re, socket, subprocess, sys, time, calendar
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
@ -725,9 +725,17 @@ def login_cram_md5(self, user, password):
|
||||||
def _CRAM_MD5_AUTH(self, challenge):
|
def _CRAM_MD5_AUTH(self, challenge):
|
||||||
""" Authobject to use with CRAM-MD5 authentication. """
|
""" Authobject to use with CRAM-MD5 authentication. """
|
||||||
import hmac
|
import hmac
|
||||||
pwd = (self.password.encode('utf-8') if isinstance(self.password, str)
|
|
||||||
else self.password)
|
if isinstance(self.password, str):
|
||||||
return self.user + " " + hmac.HMAC(pwd, challenge, 'md5').hexdigest()
|
password = self.password.encode('utf-8')
|
||||||
|
else:
|
||||||
|
password = self.password
|
||||||
|
|
||||||
|
try:
|
||||||
|
authcode = hmac.HMAC(password, challenge, 'md5')
|
||||||
|
except ValueError: # HMAC-MD5 is not available
|
||||||
|
raise self.error("CRAM-MD5 authentication is not supported")
|
||||||
|
return f"{self.user} {authcode.hexdigest()}"
|
||||||
|
|
||||||
|
|
||||||
def logout(self):
|
def logout(self):
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,20 @@ def cmd_IDLE(self, tag, args):
|
||||||
self._send_tagged(tag, 'BAD', 'Expected DONE')
|
self._send_tagged(tag, 'BAD', 'Expected DONE')
|
||||||
|
|
||||||
|
|
||||||
class NewIMAPTestsMixin():
|
class AuthHandler_CRAM_MD5(SimpleIMAPHandler):
|
||||||
|
capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
|
||||||
|
def cmd_AUTHENTICATE(self, tag, args):
|
||||||
|
self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
|
||||||
|
'VzdG9uLm1jaS5uZXQ=')
|
||||||
|
r = yield
|
||||||
|
if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
|
||||||
|
b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
|
||||||
|
self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
|
||||||
|
else:
|
||||||
|
self._send_tagged(tag, 'NO', 'No access')
|
||||||
|
|
||||||
|
|
||||||
|
class NewIMAPTestsMixin:
|
||||||
client = None
|
client = None
|
||||||
|
|
||||||
def _setup(self, imap_handler, connect=True):
|
def _setup(self, imap_handler, connect=True):
|
||||||
|
|
@ -439,40 +452,31 @@ def cmd_AUTHENTICATE(self, tag, args):
|
||||||
|
|
||||||
@hashlib_helper.requires_hashdigest('md5', openssl=True)
|
@hashlib_helper.requires_hashdigest('md5', openssl=True)
|
||||||
def test_login_cram_md5_bytes(self):
|
def test_login_cram_md5_bytes(self):
|
||||||
class AuthHandler(SimpleIMAPHandler):
|
client, _ = self._setup(AuthHandler_CRAM_MD5)
|
||||||
capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
|
self.assertIn('AUTH=CRAM-MD5', client.capabilities)
|
||||||
def cmd_AUTHENTICATE(self, tag, args):
|
|
||||||
self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
|
|
||||||
'VzdG9uLm1jaS5uZXQ=')
|
|
||||||
r = yield
|
|
||||||
if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
|
|
||||||
b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
|
|
||||||
self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
|
|
||||||
else:
|
|
||||||
self._send_tagged(tag, 'NO', 'No access')
|
|
||||||
client, _ = self._setup(AuthHandler)
|
|
||||||
self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
|
|
||||||
ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf")
|
ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf")
|
||||||
self.assertEqual(ret, "OK")
|
self.assertEqual(ret, "OK")
|
||||||
|
|
||||||
@hashlib_helper.requires_hashdigest('md5', openssl=True)
|
@hashlib_helper.requires_hashdigest('md5', openssl=True)
|
||||||
def test_login_cram_md5_plain_text(self):
|
def test_login_cram_md5_plain_text(self):
|
||||||
class AuthHandler(SimpleIMAPHandler):
|
client, _ = self._setup(AuthHandler_CRAM_MD5)
|
||||||
capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
|
self.assertIn('AUTH=CRAM-MD5', client.capabilities)
|
||||||
def cmd_AUTHENTICATE(self, tag, args):
|
|
||||||
self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm'
|
|
||||||
'VzdG9uLm1jaS5uZXQ=')
|
|
||||||
r = yield
|
|
||||||
if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT'
|
|
||||||
b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'):
|
|
||||||
self._send_tagged(tag, 'OK', 'CRAM-MD5 successful')
|
|
||||||
else:
|
|
||||||
self._send_tagged(tag, 'NO', 'No access')
|
|
||||||
client, _ = self._setup(AuthHandler)
|
|
||||||
self.assertTrue('AUTH=CRAM-MD5' in client.capabilities)
|
|
||||||
ret, _ = client.login_cram_md5("tim", "tanstaaftanstaaf")
|
ret, _ = client.login_cram_md5("tim", "tanstaaftanstaaf")
|
||||||
self.assertEqual(ret, "OK")
|
self.assertEqual(ret, "OK")
|
||||||
|
|
||||||
|
def test_login_cram_md5_blocked(self):
|
||||||
|
def side_effect(*a, **kw):
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
client, _ = self._setup(AuthHandler_CRAM_MD5)
|
||||||
|
self.assertIn('AUTH=CRAM-MD5', client.capabilities)
|
||||||
|
msg = re.escape("CRAM-MD5 authentication is not supported")
|
||||||
|
with (
|
||||||
|
mock.patch("hmac.HMAC", side_effect=side_effect),
|
||||||
|
self.assertRaisesRegex(imaplib.IMAP4.error, msg)
|
||||||
|
):
|
||||||
|
client.login_cram_md5("tim", b"tanstaaftanstaaf")
|
||||||
|
|
||||||
def test_aborted_authentication(self):
|
def test_aborted_authentication(self):
|
||||||
class MyServer(SimpleIMAPHandler):
|
class MyServer(SimpleIMAPHandler):
|
||||||
def cmd_AUTHENTICATE(self, tag, args):
|
def cmd_AUTHENTICATE(self, tag, args):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
:meth:`IMAP4.login_cram_md5 <imaplib.IMAP4.login_cram_md5>` now raises an
|
||||||
|
:exc:`IMAP4.error <imaplib.IMAP4.error>` if CRAM-MD5 authentication is not
|
||||||
|
supported. Patch by Bénédikt Tran.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue