------------------------------------------------------------
revno: 1668
fixes bugs: https://launchpad.net/bugs/775294 https://launchpad.net/bugs/1614841
committer: Mark Sapiro <m...@msapiro.net>
branch nick: 2.1
timestamp: Fri 2016-08-26 20:47:49 -0700
message:
  Fixes for CVE-2016-6893 and more.
modified:
  Mailman/Cgi/admindb.py
  Mailman/Cgi/edithtml.py
  Mailman/Cgi/options.py
  Mailman/HTMLFormatter.py
  Mailman/htmlformat.py
  NEWS


--
lp:mailman/2.1
https://code.launchpad.net/~mailman-coders/mailman/2.1

Your team Mailman Checkins is subscribed to branch lp:mailman/2.1.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/2.1/+edit-subscription
=== modified file 'Mailman/Cgi/admindb.py'
--- Mailman/Cgi/admindb.py	2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/admindb.py	2016-08-27 03:47:49 +0000
@@ -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):
@@ -135,6 +139,18 @@
         print doc.Format()
         return
 
+    # 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),
@@ -212,7 +228,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)
@@ -234,7 +254,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(

=== modified file 'Mailman/Cgi/edithtml.py'
--- Mailman/Cgi/edithtml.py	2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/edithtml.py	2016-08-27 03:47:49 +0000
@@ -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():
@@ -104,6 +107,18 @@
         print doc.Format()
         return
 
+    # 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),
@@ -148,7 +163,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())
@@ -167,7 +186,8 @@
     doc.AddItem(FontSize("+1", link))
     doc.AddItem('<p>')
     doc.AddItem('<hr>')
-    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))

=== modified file 'Mailman/Cgi/options.py'
--- Mailman/Cgi/options.py	2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/options.py	2016-08-27 03:47:49 +0000
@@ -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
@@ -315,6 +331,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)
@@ -832,7 +857,8 @@
         mlist.FormatButton('othersubs',
                            _('List my other subscriptions')))
     replacements['<mm-form-start>'] = (
-        mlist.FormatFormStart('options', user))
+        mlist.FormatFormStart('options', user, mlist=mlist, 
+            contexts=AUTH_CONTEXTS, user=user))
     replacements['<mm-user>'] = user
     replacements['<mm-presentable-user>'] = presentable_user
     replacements['<mm-email-my-pw>'] = mlist.FormatButton(

=== modified file 'Mailman/HTMLFormatter.py'
--- Mailman/HTMLFormatter.py	2015-02-13 18:41:28 +0000
+++ Mailman/HTMLFormatter.py	2016-08-27 03:47:49 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -28,6 +28,8 @@
 
 from Mailman.i18n import _
 
+from Mailman.CSRFcheck import csrf_token
+
 
 EMPTYSTRING = ''
 BR = '<br>'
@@ -317,12 +319,17 @@
             container.AddItem("</center>")
         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 ("""<form method="POST" action="%s">
+<input type="hidden" name="csrf_token" value="%s">""" 
+                % (full_url, csrf_token(mlist, contexts, user)))
         return ('<FORM Method=POST ACTION="%s">' % full_url)
 
     def FormatArchiveAnchor(self):

=== modified file 'Mailman/htmlformat.py'
--- Mailman/htmlformat.py	2016-07-15 02:10:24 +0000
+++ Mailman/htmlformat.py	2016-08-27 03:47:49 +0000
@@ -407,13 +407,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
@@ -428,7 +429,7 @@
         if self.mlist:
             output = output + \
                 '<input type="hidden" name="csrf_token" value="%s">\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</FORM>\n' % (output, spaces)
         return output

=== modified file 'NEWS'
--- NEWS	2016-08-19 21:02:14 +0000
+++ NEWS	2016-08-27 03:47:49 +0000
@@ -5,7 +5,17 @@
 
 Here is a history of user visible changes to Mailman.
 
-2.1.23 (xx-xxx-xxxx)
+2.1.23 (27-Aug-2016)
+
+  Security
+
+    - CSRF protection has been extended to the user options page.  This was
+      actually fixed by Tokio Kikuchi as part of the fix for LP: #775294 and
+      intended for Mailman 2.1.15, but that fix wasn't completely merged at the
+      time.  The full fix also addresses the admindb, and edithtml pages as
+      well as the user options page and the previously fixed admin pages.
+      Thanks to Nishant Agarwala for reporting the issue.  CVE-2016-6893
+      (LP: #1614841)
  
   New Features
 

_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to