mailman/SOURCES/mailman-CVE-2021-42096-2021...

130 lines
4.8 KiB
Diff
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

diff --git a/Mailman/CSRFcheck.py b/Mailman/CSRFcheck.py
index a1e78d9..24e3e11 100644
--- a/Mailman/CSRFcheck.py
+++ b/Mailman/CSRFcheck.py
@@ -18,11 +18,13 @@
""" Cross-Site Request Forgery checker """
import time
+import urllib
import marshal
import binascii
from Mailman import mm_cfg
-from Mailman.Utils import sha_new
+from Mailman.Logging.Syslog import syslog
+from Mailman.Utils import UnobscureEmail, sha_new
keydict = {
'user': mm_cfg.AuthUser,
@@ -37,6 +39,10 @@ keydict = {
def csrf_token(mlist, contexts, user=None):
""" create token by mailman cookie generation algorithm """
+ if user:
+ # Unmunge a munged email address.
+ user = UnobscureEmail(urllib.unquote(user))
+
for context in contexts:
key, secret = mlist.AuthContextInfo(context, user)
if key:
@@ -49,9 +55,8 @@ def csrf_token(mlist, contexts, user=None):
token = binascii.hexlify(marshal.dumps((issued, keymac)))
return token
-def csrf_check(mlist, token):
+def csrf_check(mlist, token, options_user=None):
""" check token by mailman cookie validation algorithm """
-
try:
issued, keymac = marshal.loads(binascii.unhexlify(token))
key, received_mac = keymac.split(':', 1)
@@ -62,6 +67,17 @@ def csrf_check(mlist, token):
key, user = key.split('+', 1)
else:
user = None
+ if user:
+ # This is for CVE-2021-42097. The token is a user token because
+ # of the fix for CVE-2021-42096 but it must match the user for
+ # whom the options page is requested.
+ raw_user = UnobscureEmail(urllib.unquote(user))
+ if options_user and options_user != raw_user:
+ syslog('mischief',
+ 'Form for user %s submitted with CSRF token '
+ 'issued for %s.',
+ options_user, raw_user)
+ return False
context = keydict.get(key)
key, secret = mlist.AuthContextInfo(context, user)
assert key
diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py
index 386b308..980fc09 100644
--- a/Mailman/Cgi/options.py
+++ b/Mailman/Cgi/options.py
@@ -54,9 +54,6 @@ except NameError:
True = 1
False = 0
-AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin,
- mm_cfg.AuthListModerator, mm_cfg.AuthUser)
-
def main():
global _
@@ -124,15 +121,6 @@ def main():
print doc.Format()
return
- if set(params) - set(safe_params):
- csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'))
- else:
- csrf_checked = True
- # if password is present, void cookie to force password authentication.
- if cgidata.getfirst('password'):
- os.environ['HTTP_COOKIE'] = ''
- csrf_checked = True
-
# Set the language for the page. If we're coming from the listinfo cgi,
# we might have a 'language' key in the cgi data. That was an explicit
# preference to view the page in, so we should honor that here. If that's
@@ -168,6 +156,16 @@ def main():
user = user[-1]
# Avoid cross-site scripting attacks
+ if set(params) - set(safe_params):
+ csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'),
+ Utils.UnobscureEmail(urllib.unquote(user)))
+ else:
+ csrf_checked = True
+ # if password is present, void cookie to force password authentication.
+ if cgidata.getfirst('password'):
+ os.environ['HTTP_COOKIE'] = ''
+ csrf_checked = True
+
safeuser = Utils.websafe(user)
try:
Utils.ValidateEmail(user)
@@ -867,8 +865,9 @@ def options_page(mlist, doc, user, cpuser, userlang, message=''):
mlist.FormatButton('othersubs',
_('List my other subscriptions')))
replacements['<mm-form-start>'] = (
+ # Always make the CSRF token for the user. CVE-2021-42096
mlist.FormatFormStart('options', user, mlist=mlist,
- contexts=AUTH_CONTEXTS, user=user))
+ contexts=[mm_cfg.AuthUser], user=user))
replacements['<mm-user>'] = user
replacements['<mm-presentable-user>'] = presentable_user
replacements['<mm-email-my-pw>'] = mlist.FormatButton(
diff --git a/Mailman/SecurityManager.py b/Mailman/SecurityManager.py
index 9b7f03f..e9e5ce5 100644
--- a/Mailman/SecurityManager.py
+++ b/Mailman/SecurityManager.py
@@ -104,6 +104,7 @@ class SecurityManager:
if user is None:
# A bad system error
raise TypeError, 'No user supplied for AuthUser context'
+ user = Utils.UnobscureEmail(urllib.unquote(user))
secret = self.getMemberPassword(user)
userdata = urllib.quote(Utils.ObscureEmail(user), safe='')
key += 'user+%s' % userdata