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 <[email protected]> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan
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
- For header_filter_rules matching, RFC 2047 encoded headers, non-encoded
headers and header_filter_rules patterns are now all decoded to unicode.
Both XML character references of the form &#nnnn; and unicode escapes
of the form \Uxxxx in patterns are converted to unicodes as well. Both
headers and patterns are normalized to 'NFKC' normal form before
matching, but the normalization form can be set via a new NORMALIZE_FORM
mm_cfg setting. Also, the web UI has been updated to encode characters
in text fields that are invalid in the character set of the page's
language as XML character references instead of '?'. This should help
with entering header_filter_rules patterns to match 'odd' characters.
This feature is experimental and is problematic for some cases where it
is desired to have a header_filter_rules pattern with characters not in
the character set of the list's preferred language. For patterns
without such characters, the only change in behavior should be because
of unicode normalization which should improve matching. For other
situations such as trying to match a Subject: with CJK characters (range
U+4E00..U+9FFF) on an English language (ascii) list, one can enter a
pattern like '^subject:.*[一-鿿]' or
'^subject:.*[\u4e00;-\u9fff;]' to match a Subject with any character in
the range, and it will work, but depending on the actual characters and
the browser, submitting another, even unrelated change can garble the
original entry although this usually occurs only with ascii pages and
characters in the range \u0080-\u00ff. The \Uxxxx unicode escapes must
have exactly 4 hex digits, but they are case insensitive. (LP: #558155)
- Thanks to Jim Popovitch REMOVE_DKIM_HEADERS can now be set to 3 to
preserve the original headers as X-Mailman-Original-... before removing
them.
- Several additional templates have been added to those that can be edited
via the web admin GUI. (LP: #1583387)
- SMTPDirect.py can now do SASL authentication and STARTTLS security when
connecting to the outgoiung MTA. Associated with this are new
Defaults.py/mm_cfg.py settings SMTP_AUTH, SMTP_USER, SMTP_PASSWD and
SMTP_USE_TLS. (LP: #558281)
- There is a new Defaults.py/mm_cfg.py setting SMTPLIB_DEBUG_LEVEL which
can be set to 1 to enable verbose smtplib debugging to Mailman's error
log to help with debugging 'low level smtp failures'. (LP: #1573074)
- A list's nonmember_rejection_notice attribute will now be the default
rejection reason for a held non-member post in addition to it's prior
role as the reson for an automatically rejected non-member post.
(LP: #1572330)
i18n
- The French translation of 'Dutch' is changed from 'Hollandais' to
'Néerlandais' per Francis Jorissen.
- Some German language templates that were incorrectly utf-8 encoded have
been recoded as iso-8859-1. (LP: #1602779)
- Japanese translation and documentation in messages/ja has been updated by
Yasuhito FUTATSUKI.
Bug fixes and other patches
- The admin Membership List letter links could be incorrectly rendered as
Unicode strings following a search. (LP: #1604544)
- We no longer throw an uncaught TypeError with certain defective crafted
POST requests to Mailman's CGIs. (LP: #1602608)
- Scrubber links in archives are now in the list's preferred_language
rather than the poster's language. (LP: #1586505)
- Improved logging of banned subscription and address change attempts.
(LP: #1582856)
- In rare circumstances a list can be removed while the admin or listinfo
CGI or bin/list_lists is running causing an uncaught MMUnknownListError
to be thrown. The exception is now caught and handled. (LP: #1582532)
- Set the Date: header in the wrapper message when from_is_list or
dmarc_moderation_action is Wrap Message. (LP: #1581215)
- A site can now set DMARC_ORGANIZATIONAL_DOMAIN_DATA_URL to None or the
null string if it wants to avoid using this. (LP: #1578450)
- The white space to the left of the admindb Logout link is no longer
part of the link. (LP: #1573623)
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
signature.asc
Description: OpenPGP digital signature
------------------------------------------------------ Mailman-Users mailing list [email protected] https://mail.python.org/mailman/listinfo/mailman-users Mailman FAQ: http://wiki.list.org/x/AgA3 Security Policy: http://wiki.list.org/x/QIA9 Searchable Archives: http://www.mail-archive.com/mailman-users%40python.org/ Unsubscribe: https://mail.python.org/mailman/options/mailman-users/archive%40jab.org
