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 <m...@msapiro.net>        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:.*[&#19968;-&#40959;]' 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

Attachment: signature.asc
Description: OpenPGP digital signature

------------------------------------------------------
Mailman-Users mailing list Mailman-Users@python.org
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

Reply via email to