From 1334bf44e744ff9e0a79b20451dc51583d5f2ce2 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Dec 02 2021 15:58:01 +0000 Subject: import mailman-2.1.29-12.module+el8.5.0+13466+327eb9f3.2 --- diff --git a/SOURCES/mailmain-CVE-2021-42096-2021-42097.patch b/SOURCES/mailmain-CVE-2021-42096-2021-42097.patch deleted file mode 100644 index ee05f0a..0000000 --- a/SOURCES/mailmain-CVE-2021-42096-2021-42097.patch +++ /dev/null @@ -1,129 +0,0 @@ -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[''] = ( -+ # 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[''] = user - replacements[''] = presentable_user - replacements[''] = 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 diff --git a/SOURCES/mailman-CVE-2021-42096-2021-42097.patch b/SOURCES/mailman-CVE-2021-42096-2021-42097.patch new file mode 100644 index 0000000..ee05f0a --- /dev/null +++ b/SOURCES/mailman-CVE-2021-42096-2021-42097.patch @@ -0,0 +1,129 @@ +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[''] = ( ++ # 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[''] = user + replacements[''] = presentable_user + replacements[''] = 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 diff --git a/SOURCES/mailman-CVE-2021-44227.patch b/SOURCES/mailman-CVE-2021-44227.patch new file mode 100644 index 0000000..0fab95c --- /dev/null +++ b/SOURCES/mailman-CVE-2021-44227.patch @@ -0,0 +1,82 @@ +diff --git a/Mailman/CSRFcheck.py b/Mailman/CSRFcheck.py +index 24e3e11..81998cf 100644 +--- a/Mailman/CSRFcheck.py ++++ b/Mailman/CSRFcheck.py +@@ -55,7 +55,7 @@ def csrf_token(mlist, contexts, user=None): + token = binascii.hexlify(marshal.dumps((issued, keymac))) + return token + +-def csrf_check(mlist, token, options_user=None): ++def csrf_check(mlist, token, cgi_user=None): + """ check token by mailman cookie validation algorithm """ + try: + issued, keymac = marshal.loads(binascii.unhexlify(token)) +@@ -67,12 +67,25 @@ def csrf_check(mlist, token, options_user=None): + key, user = key.split('+', 1) + else: + user = None ++ # Don't allow unprivileged tokens for admin or admindb. ++ if cgi_user == 'admin': ++ if key not in ('admin', 'site'): ++ syslog('mischief', ++ 'admin form submitted with CSRF token issued for %s.', ++ key + '+' + user if user else key) ++ return False ++ elif cgi_user == 'admindb': ++ if key not in ('moderator', 'admin', 'site'): ++ syslog('mischief', ++ 'admindb form submitted with CSRF token issued for %s.', ++ key + '+' + user if user else key) ++ return False + 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: ++ if cgi_user and cgi_user != raw_user: + syslog('mischief', + 'Form for user %s submitted with CSRF token ' + 'issued for %s.', +diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py +index d2577b5..7c600df 100644 +--- a/Mailman/Cgi/admin.py ++++ b/Mailman/Cgi/admin.py +@@ -107,7 +107,8 @@ def main(): + 'legend'] + params = cgidata.keys() + if set(params) - set(safe_params): +- csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token')) ++ csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'), ++ 'admin') + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. +diff --git a/Mailman/Cgi/admindb.py b/Mailman/Cgi/admindb.py +index 58bb357..c152501 100644 +--- a/Mailman/Cgi/admindb.py ++++ b/Mailman/Cgi/admindb.py +@@ -144,7 +144,8 @@ def main(): + safe_params = ['adminpw', 'admlogin', 'msgid', 'sender', 'details'] + params = cgidata.keys() + if set(params) - set(safe_params): +- csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token')) ++ csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'), ++ 'admindb') + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. +diff --git a/Mailman/Cgi/edithtml.py b/Mailman/Cgi/edithtml.py +index 1dd9e87..170e811 100644 +--- a/Mailman/Cgi/edithtml.py ++++ b/Mailman/Cgi/edithtml.py +@@ -111,7 +111,8 @@ def main(): + safe_params = ['VARHELP', 'adminpw', 'admlogin'] + params = cgidata.keys() + if set(params) - set(safe_params): +- csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token')) ++ csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'), ++ 'admin') + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. diff --git a/SPECS/mailman.spec b/SPECS/mailman.spec index 4a09cac..18412e4 100644 --- a/SPECS/mailman.spec +++ b/SPECS/mailman.spec @@ -3,7 +3,7 @@ Summary: Mailing list manager with built in Web access Name: mailman Version: 2.1.29 -Release: 12%{?dist}.1 +Release: 12%{?dist}.2 Epoch: 3 Group: Applications/Internet Source0: ftp://ftp.gnu.org/pub/gnu/mailman/mailman-%{version}.tgz @@ -34,7 +34,8 @@ Patch26: mailman-bouncer_oom_crash.patch Patch27: mailman-2.1.29-login_content_injection.patch Patch28: mailman-2.1.29-options_content_njection.patch Patch29: mailman-2.1.29-cmd_reply_encoding.patch -Patch30: mailmain-CVE-2021-42096-2021-42097.patch +Patch30: mailman-CVE-2021-42096-2021-42097.patch +Patch31: mailman-CVE-2021-44227.patch License: GPLv2+ URL: http://www.list.org/ @@ -129,6 +130,7 @@ additional installation steps, these are described in: %patch28 -p0 -b .options_injection %patch29 -p0 -b .cmd_reply_encoding %patch30 -p1 -b .CVE-2021-42096-2021-42097 +%patch31 -p1 -b .CVE-2021-44227 #cp $RPM_SOURCE_DIR/mailman.INSTALL.REDHAT.in INSTALL.REDHAT.in cp %{SOURCE5} INSTALL.REDHAT.in @@ -582,6 +584,10 @@ exit 0 %dir %attr(775,root,%{mmgroup}) %{lockdir} %changelog +* Sun Nov 28 2021 Martin Osvald - 3:2.1.29-12.2 +- Fix for CVE-2021-44227 +- Resolves: #2026871 + * Tue Nov 09 2021 Martin Osvald - 3:2.1.29-12.1 - Fix for CVE-2021-42096 - Fix for CVE-2021-42097