diff -Nru mailman-2.1.22/debian/changelog mailman-2.1.22/debian/changelog --- mailman-2.1.22/debian/changelog 2016-04-25 16:56:06.000000000 +0000 +++ mailman-2.1.22/debian/changelog 2016-10-06 15:21:18.000000000 +0000 @@ -1,3 +1,14 @@ +mailman (1:2.1.22-1ubuntu0.1) yakkety-security; urgency=medium + + * SECURITY UPDATE: CSRF vulnerability in the user options page + - debian/patches/CVE-2016-6893.patch: add CSRF checks to + Mailman/Cgi/admindb.py, Mailman/Cgi/edithtml.py, + Mailman/Cgi/options.py, Mailman/HTMLFormatter.py, + Mailman/htmlformat.py. + - CVE-2016-6893 + + -- Marc Deslauriers Thu, 06 Oct 2016 11:21:18 -0400 + mailman (1:2.1.22-1) unstable; urgency=medium * New upstream release. (Closes: #821367) diff -Nru mailman-2.1.22/debian/control mailman-2.1.22/debian/control --- mailman-2.1.22/debian/control 2016-04-25 16:55:46.000000000 +0000 +++ mailman-2.1.22/debian/control 2016-10-06 15:21:18.000000000 +0000 @@ -1,7 +1,8 @@ Source: mailman Section: mail Priority: optional -Maintainer: Mailman for Debian +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Mailman for Debian Uploaders: Lionel Elie Mamane , Thijs Kinkhorst , Hector Garcia diff -Nru mailman-2.1.22/debian/patches/CVE-2016-6893.patch mailman-2.1.22/debian/patches/CVE-2016-6893.patch --- mailman-2.1.22/debian/patches/CVE-2016-6893.patch 1970-01-01 00:00:00.000000000 +0000 +++ mailman-2.1.22/debian/patches/CVE-2016-6893.patch 2016-10-06 15:21:18.000000000 +0000 @@ -0,0 +1,255 @@ +Description: fix CSRF vulnerability in the user options page +Origin: backport, http://bazaar.launchpad.net/~mailman-coders/mailman/2.1/revision/1668 +Bug: https://bugs.launchpad.net/mailman/+bug/1614841 +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=835970 + +Index: mailman-2.1.22/Mailman/Cgi/admindb.py +=================================================================== +--- mailman-2.1.22.orig/Mailman/Cgi/admindb.py 2016-04-17 14:52:38.000000000 -0400 ++++ mailman-2.1.22/Mailman/Cgi/admindb.py 2016-10-06 11:20:40.021338339 -0400 +@@ -39,6 +39,7 @@ + from Mailman.Cgi import Auth + from Mailman.htmlformat import * + from Mailman.Logging.Syslog import syslog ++from Mailman.CSRFcheck import csrf_check + + EMPTYSTRING = '' + NL = '\n' +@@ -58,6 +59,9 @@ + else: + ssort = SSENDER + ++AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin, ++ mm_cfg.AuthListModerator) ++ + + + def helds_by_skey(mlist, ssort=SSENDER): +@@ -123,6 +127,18 @@ + # Make sure the user is authorized to see this page. + cgidata = cgi.FieldStorage(keep_blank_values=1) + ++ # CSRF check ++ safe_params = ['adminpw', 'admlogin', 'msgid', 'sender', 'details'] ++ params = cgidata.keys() ++ if set(params) - set(safe_params): ++ csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) ++ else: ++ csrf_checked = True ++ # if password is present, void cookie to force password authentication. ++ if cgidata.getvalue('adminpw'): ++ os.environ['HTTP_COOKIE'] = '' ++ csrf_checked = True ++ + if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, + mm_cfg.AuthListModerator, + mm_cfg.AuthSiteAdmin), +@@ -200,7 +216,11 @@ + elif not details: + # This is a form submission + doc.SetTitle(_('%(realname)s Administrative Database Results')) +- process_form(mlist, doc, cgidata) ++ if csrf_checked: ++ process_form(mlist, doc, cgidata) ++ else: ++ doc.addError( ++ _('The form lifetime has expired. (request forgery check)')) + # Now print the results and we're done. Short circuit for when there + # are no pending requests, but be sure to save the results! + admindburl = mlist.GetScriptURL('admindb', absolute=1) +@@ -221,7 +241,7 @@ + mlist.Save() + return + +- form = Form(admindburl) ++ form = Form(admindburl, mlist=mlist, contexts=AUTH_CONTEXTS) + # Add the instructions template + if details == 'instructions': + doc.AddItem(Header( +Index: mailman-2.1.22/Mailman/Cgi/edithtml.py +=================================================================== +--- mailman-2.1.22.orig/Mailman/Cgi/edithtml.py 2016-04-17 14:52:38.000000000 -0400 ++++ mailman-2.1.22/Mailman/Cgi/edithtml.py 2016-10-06 11:20:40.021338339 -0400 +@@ -30,9 +30,12 @@ + from Mailman.Cgi import Auth + from Mailman.Logging.Syslog import syslog + from Mailman import i18n ++from Mailman.CSRFcheck import csrf_check + + _ = i18n._ + ++AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin) ++ + + + def main(): +@@ -82,6 +85,18 @@ + # Must be authenticated to get any farther + cgidata = cgi.FieldStorage() + ++ # CSRF check ++ safe_params = ['VARHELP', 'adminpw', 'admlogin'] ++ params = cgidata.keys() ++ if set(params) - set(safe_params): ++ csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) ++ else: ++ csrf_checked = True ++ # if password is present, void cookie to force password authentication. ++ if cgidata.getvalue('adminpw'): ++ os.environ['HTTP_COOKIE'] = '' ++ csrf_checked = True ++ + # Editing the html for a list is limited to the list admin and site admin. + if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, + mm_cfg.AuthSiteAdmin), +@@ -126,7 +141,11 @@ + + try: + if cgidata.keys(): +- ChangeHTML(mlist, cgidata, template_name, doc) ++ if csrf_checked: ++ ChangeHTML(mlist, cgidata, template_name, doc) ++ else: ++ doc.addError( ++ _('The form lifetime has expired. (request forgery check)')) + FormatHTML(mlist, doc, template_name, template_info) + finally: + doc.AddItem(mlist.GetMailmanFooter()) +@@ -145,7 +164,8 @@ + doc.AddItem(FontSize("+1", link)) + doc.AddItem('

') + doc.AddItem('


') +- form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name) ++ form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name, ++ mlist=mlist, contexts=AUTH_CONTEXTS) + text = Utils.maketext(template_name, raw=1, mlist=mlist) + # MAS: Don't websafe twice. TextArea does it. + form.AddItem(TextArea('html_code', text, rows=40, cols=75)) +Index: mailman-2.1.22/Mailman/Cgi/options.py +=================================================================== +--- mailman-2.1.22.orig/Mailman/Cgi/options.py 2016-04-17 14:52:38.000000000 -0400 ++++ mailman-2.1.22/Mailman/Cgi/options.py 2016-10-06 11:20:40.025338383 -0400 +@@ -33,6 +33,7 @@ + from Mailman import i18n + from Mailman.htmlformat import * + from Mailman.Logging.Syslog import syslog ++from Mailman.CSRFcheck import csrf_check + + OR = '|' + SLASH = '/' +@@ -51,6 +52,8 @@ + True = 1 + False = 0 + ++AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin, ++ mm_cfg.AuthListModerator, mm_cfg.AuthUser) + + + def main(): +@@ -104,6 +107,19 @@ + # The total contents of the user's response + cgidata = cgi.FieldStorage(keep_blank_values=1) + ++ # CSRF check ++ safe_params = ['displang-button', 'language', 'email', 'password', 'login', ++ 'login-unsub', 'login-remind', 'VARHELP', 'UserOptions'] ++ params = cgidata.keys() ++ if set(params) - set(safe_params): ++ csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) ++ else: ++ csrf_checked = True ++ # if password is present, void cookie to force password authentication. ++ if cgidata.getvalue('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 +@@ -305,6 +321,15 @@ + print doc.Format() + return + ++ # Before going further, get the result of CSRF check and do nothing ++ # if it has failed. ++ if csrf_checked == False: ++ doc.addError( ++ _('The form lifetime has expired. (request forgery check)')) ++ options_page(mlist, doc, user, cpuser, userlang) ++ print doc.Format() ++ return ++ + if cgidata.has_key('logout'): + print mlist.ZapCookie(mm_cfg.AuthUser, user) + loginpage(mlist, doc, user, language) +@@ -822,7 +847,8 @@ + mlist.FormatButton('othersubs', + _('List my other subscriptions'))) + replacements[''] = ( +- mlist.FormatFormStart('options', user)) ++ mlist.FormatFormStart('options', user, mlist=mlist, ++ contexts=AUTH_CONTEXTS, user=user)) + replacements[''] = user + replacements[''] = presentable_user + replacements[''] = mlist.FormatButton( +Index: mailman-2.1.22/Mailman/HTMLFormatter.py +=================================================================== +--- mailman-2.1.22.orig/Mailman/HTMLFormatter.py 2016-04-17 14:52:38.000000000 -0400 ++++ mailman-2.1.22/Mailman/HTMLFormatter.py 2016-10-06 11:20:40.025338383 -0400 +@@ -28,6 +28,8 @@ + + from Mailman.i18n import _ + ++from Mailman.CSRFcheck import csrf_token ++ + + EMPTYSTRING = '' + BR = '
' +@@ -317,12 +319,17 @@ + container.AddItem("") + return container + +- def FormatFormStart(self, name, extra=''): ++ def FormatFormStart(self, name, extra='', ++ mlist=None, contexts=None, user=None): + base_url = self.GetScriptURL(name) + if extra: + full_url = "%s/%s" % (base_url, extra) + else: + full_url = base_url ++ if mlist: ++ return ("""
++""" ++ % (full_url, csrf_token(mlist, contexts, user))) + return ('' % full_url) + + def FormatArchiveAnchor(self): +Index: mailman-2.1.22/Mailman/htmlformat.py +=================================================================== +--- mailman-2.1.22.orig/Mailman/htmlformat.py 2016-10-06 11:05:14.000000000 -0400 ++++ mailman-2.1.22/Mailman/htmlformat.py 2016-10-06 11:20:40.025338383 -0400 +@@ -408,13 +408,14 @@ + + class Form(Container): + def __init__(self, action='', method='POST', encoding=None, +- mlist=None, contexts=None, *items): ++ mlist=None, contexts=None, user=None, *items): + apply(Container.__init__, (self,) + items) + self.action = action + self.method = method + self.encoding = encoding + self.mlist = mlist + self.contexts = contexts ++ self.user = user + + def set_action(self, action): + self.action = action +@@ -429,7 +430,7 @@ + if self.mlist: + output = output + \ + '\n' \ +- % csrf_token(self.mlist, self.contexts) ++ % csrf_token(self.mlist, self.contexts, self.user) + output = output + Container.Format(self, indent+2) + output = '%s\n%s
\n' % (output, spaces) + return output diff -Nru mailman-2.1.22/debian/patches/series mailman-2.1.22/debian/patches/series --- mailman-2.1.22/debian/patches/series 2016-04-25 16:01:10.000000000 +0000 +++ mailman-2.1.22/debian/patches/series 2016-10-06 15:20:31.000000000 +0000 @@ -10,3 +10,4 @@ 79_archiver_slash.patch 90_gettext_errors.patch 91_utf8.patch +CVE-2016-6893.patch