[Mailman-Developers] Mailman 2.1.23 release and fix for CVE-2016-6893
Mark Sapiro
mark at msapiro.net
Sat Aug 27 09:47:35 EDT 2016
I am pleased to announce the release of Mailman 2.1.23.
Python 2.4 is the minimum supported, but Python 2.7 is strongly recommended.
This release contains a fix for CVE-2016-6893 which is also attached
here as a patch. It also has new features, a few i18n updates and some
bug fixes. See the attached README for details.
Mailman is free software for managing email mailing lists and
e-newsletters. Mailman is used for all the python.org and
SourceForge.net mailing lists, as well as at hundreds of other sites.
For more information, please see our web site at one of:
http://www.list.org
http://www.gnu.org/software/mailman
http://mailman.sourceforge.net/
http://mirror.list.org/
Mailman 2.1.23 can be downloaded from
https://launchpad.net/mailman/2.1/
http://ftp.gnu.org/gnu/mailman/
https://sourceforge.net/projects/mailman/
--
Mark Sapiro <mark at msapiro.net> The highway is for gamblers,
San Francisco Bay Area, California better use your sense - B. Dylan
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: README
URL: <http://mail.python.org/pipermail/mailman-developers/attachments/20160827/232a97b6/attachment-0001.ksh>
-------------- next part --------------
Patch for CVE-2016-6893
This will apply with possible minor line number diffs to any Mailman >= 2.1.15
For Mailman < 2.1.15, the required Mailman/CSRFcheck.py module doesn't
exist and other CSRF vulnerabilities exist in the admin UI, so upgrade is
recommended.
=== modified file 'Mailman/Cgi/admindb.py'
--- Mailman/Cgi/admindb.py 2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/admindb.py 2016-08-23 23:12:05 +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-23 23:12:05 +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-23 23:12:05 +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-23 23:04:58 +0000
@@ -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-23 23:20:30 +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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/mailman-developers/attachments/20160827/232a97b6/attachment-0001.sig>
More information about the Mailman-Developers
mailing list