[Mailman-Announce] ANNOUNCE Mailman 2.0.6

Barry A. Warsaw mailman-developers@python.org
Wed, 25 Jul 2001 15:54:55 -0400


Folks,

I've just released Mailman 2.0.6 which fixes a potential security
problem in Mailman 2.0.x, and includes a few other minor bug fixes.

It is possible, although unlikely, that you could have an empty site
password, or an empty list password.  Because of peculiarities in the
Unix crypt() function, such empty passwords could allow unauthorized
access to the list administrative pages with an arbitrary password
string.  This situation does not occur normally, but it is possible to
create it by accident (e.g. by touch'ing data/adm.pw).

This patch ensures that such empty passwords do not allow unauthorized
access, by first checking to make sure that the salt is at least 2
characters in length.  Alternatively, you can make sure that either
data/adm.pw does not exist or that it is not empty.  For the extra
paranoid, you'd need to be sure that none of your lists have empty
passwords, but that's an even more difficult situation to create by
accident.

This patch guards against both situations.  Please note that Mailman
2.1alpha is not vulnerable to this problem because it does not use
crypt().

A few other minor bugs have been fixed; see the NEWS excerpt below for
details.

As usual, I'm releasing this as both a complete tarball and as a patch
against Mailman 2.0.5.  You /must/ update your source to 2.0.5 before
applying the 2.0.6 patch.  Since the patch is small, I'm including it
in this message.  To apply, cd into your 2.0.5 source tree and apply
it like so:

    % patch -p0 < mailman-2.0.5-2.0.6.txt
    
Then run "config.status; make install".

Currently both http://mailman.sf.net and http://www.list.org are
updated, and I expect the gnu.org site to be updated soon as well.
The release information on SF is at

    http://sourceforge.net/project/shownotes.php?release_id=45268

See also

    http://www.gnu.org/software/mailman
    http://www.list.org
    http://mailman.sf.net

My thanks to Dave Helton, Ray Sanders, and Thomas Wouters for their
help with this release.

Enjoy,
-Barry

Index: NEWS
===================================================================
RCS file: /cvsroot/mailman/mailman/NEWS,v
retrieving revision 1.25.2.6
retrieving revision 1.25.2.9
diff -u -r1.25.2.6 -r1.25.2.9
--- NEWS	2001/05/03 21:06:56	1.25.2.6
+++ NEWS	2001/07/25 18:52:27	1.25.2.9
@@ -4,6 +4,34 @@
 
 Here is a history of user visible changes to Mailman.
 
+2.0.6 (25-Jul-2001)
+
+    Security fix:
+
+    - Fixed a potential security hole which could allow access to list
+      administrative features by unauthorized users.  If there is an
+      empty data/adm.pw file (the site password file), then any
+      password will be accepted as the list administrative password.
+      This exploit is caused by a common "bug" in the crypt() function
+      suffered by several Unix distributions, including at least
+      GNU/Linux and Solaris.  Given a salt string of length zero,
+      crypt() always returns the empty string.
+
+      In lieu of applying this patch, sites can run bin/mmsitepass and
+      ensure that data/adm.pw is of length 2 or greater.
+
+    Bug fixes:
+
+    - Ensure that even if DEFAULT_URL is misconfigured in mm_cfg.py
+      (i.e. is missing a trailing slash), it is always fixed upon list
+      creation.
+
+    - Check for administrivia holds before any other tests.
+
+    - SF bugs fixed: 407666, 227694
+
+    - Other miscellaneous buglets fixed.
+
 2.0.5 (04-May-2001)
 
     Fix a lock stagnation problem that can result when the user hits
Index: Mailman/MailList.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/MailList.py,v
retrieving revision 1.189
retrieving revision 1.189.2.2
diff -u -r1.189 -r1.189.2.2
--- Mailman/MailList.py	2000/11/16 04:33:27	1.189
+++ Mailman/MailList.py	2001/05/29 14:45:27	1.189.2.2
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 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
@@ -712,7 +712,7 @@
              " fails, or if the pattern does contain an `@', then the pattern"
              " is matched against the entire recipient address. "
              " <p>Matching against the local part is deprecated; in a future"
-             " release, the patterm will always be matched against the "
+             " release, the pattern will always be matched against the "
              " entire recipient address."),
 
 	    ('max_num_recipients', mm_cfg.Number, 5, 0, 
@@ -787,6 +787,7 @@
         self.InitVars(name, admin, crypted_password)
         self._ready = 1
         self.InitTemplates()
+        self.CheckValues()
         self.Save()
 	# Touch these files so they have the right dir perms no matter what.
 	# A "just-in-case" thing.  This shouldn't have to be here.
Index: Mailman/SecurityManager.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/SecurityManager.py,v
retrieving revision 1.31
retrieving revision 1.31.2.1
diff -u -r1.31 -r1.31.2.1
--- Mailman/SecurityManager.py	2000/10/02 20:40:41	1.31
+++ Mailman/SecurityManager.py	2001/07/25 18:07:51	1.31.2.1
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 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
@@ -44,8 +44,12 @@
     def ValidAdminPassword(self, pw):
 	if Utils.CheckSiteAdminPassword(pw):
             return 1
-	return type(pw) == StringType and \
-               Crypt.crypt(pw, self.password) == self.password
+        salt = self.password[:2]
+        # crypt() has a bug in that if the salt is the empty string, it will
+        # always return the empty string, regardless of the key. :(
+        if len(salt) < 2:
+            return 0
+	return Crypt.crypt(pw, salt) == self.password
 
     def ConfirmAdminPassword(self, pw):
         if not self.ValidAdminPassword(pw):
Index: Mailman/Utils.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Utils.py,v
retrieving revision 1.104.2.2
retrieving revision 1.104.2.4
diff -u -r1.104.2.2 -r1.104.2.4
--- Mailman/Utils.py	2001/04/18 04:23:07	1.104.2.2
+++ Mailman/Utils.py	2001/07/25 18:06:46	1.104.2.4
@@ -262,7 +262,7 @@
 	    finally:
 		os.umask(ou)
 	    if verbose:
-		print 'made directory: ', madepart
+		print 'made directory: ', made_part
   
 
 
@@ -405,7 +405,12 @@
         f = open(mm_cfg.SITE_PW_FILE)
         pw2 = f.read()
         f.close()
-        return Crypt.crypt(pw1, pw2[:2]) == pw2
+        salt = pw2[:2]
+        # crypt() has a bug in that if the salt is the empty string, it will
+        # always return the empty string, regardless of the key. :(
+        if len(salt) < 2:
+            return 0
+        return Crypt.crypt(pw1, salt) == pw2
     # There probably is no site admin password if there was an exception
     except IOError:
         return 0
Index: Mailman/Version.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Version.py,v
retrieving revision 1.20.2.5
retrieving revision 1.20.2.6
diff -u -r1.20.2.5 -r1.20.2.6
--- Mailman/Version.py	2001/05/03 20:58:19	1.20.2.5
+++ Mailman/Version.py	2001/07/25 18:05:30	1.20.2.6
@@ -15,7 +15,7 @@
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 # Mailman version
-VERSION = "2.0.5"
+VERSION = "2.0.6"
 
 # And as a hex number in the manner of PY_VERSION_HEX
 ALPHA = 0xa
@@ -27,7 +27,7 @@
 
 MAJOR_REV = 2
 MINOR_REV = 0
-MICRO_REV = 5
+MICRO_REV = 6
 REL_LEVEL = FINAL
 # at most 15 beta releases!
 REL_SERIAL = 0
Index: Mailman/versions.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/versions.py,v
retrieving revision 1.27
retrieving revision 1.27.2.1
diff -u -r1.27 -r1.27.2.1
--- Mailman/versions.py	2000/06/14 05:09:58	1.27
+++ Mailman/versions.py	2001/07/10 14:58:56	1.27.2.1
@@ -142,7 +142,7 @@
     # set admin_notify_mchanges
     #
     if not hasattr(l, "admin_notify_mchanges"):
-        setatrr(l, "admin_notify_mchanges",
+        setattr(l, "admin_notify_mchanges",
                 mm_cfg.DEFAULT_ADMIN_NOTIFY_MCHANGES)
     #
     # Convert the members and digest_members addresses so that the keys of
Index: Mailman/Archiver/pipermail.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Archiver/pipermail.py,v
retrieving revision 1.15
retrieving revision 1.15.2.2
diff -u -r1.15 -r1.15.2.2
--- Mailman/Archiver/pipermail.py	2000/10/20 06:18:11	1.15
+++ Mailman/Archiver/pipermail.py	2001/06/01 22:30:16	1.15.2.2
@@ -62,7 +62,7 @@
 
 # Abstract class for databases
 
-class DatabaseInterface:    
+class DatabaseInterface:
     def __init__(self): pass
     def close(self): pass
     def getArticle(self, archive, msgid): pass
@@ -162,13 +162,15 @@
 	id = strip_separators(message.getheader('Message-Id'))
 	if id == "":
             self.msgid = str(self.sequence)
-	else: self.msgid = id
+	else:
+            self.msgid = id
 
 	if message.has_key('Subject'):
             self.subject = str(message['Subject'])
 	else:
+            self.subject = 'No subject'
+	if self.subject == "":
             self.subject = 'No subject'
-	if self.subject == "": self.subject = 'No subject'
 
         self._set_date(message)
 
@@ -180,7 +182,8 @@
 	self.email = strip_separators(self.email)
 	self.author = strip_separators(self.author)
 
-	if self.author == "": self.author = self.email
+	if self.author == "":
+            self.author = self.email
 
 	# Save the In-Reply-To:, References:, and Message-ID: lines
         #
@@ -197,8 +200,10 @@
             self.in_reply_to = ''
 	else:
 	    match = msgid_pat.search(i_r_t)
-	    if match is None: self.in_reply_to = ''
-	    else: self.in_reply_to = strip_separators(match.group(1))
+	    if match is None:
+                self.in_reply_to = ''
+	    else:
+                self.in_reply_to = strip_separators(match.group(1))
 		
 	references = message.getheader('References')
 	if references is None:
@@ -352,7 +357,7 @@
                                                    refs[0])
                 for ref in refs[1:]:
                     a = self.database.getArticle(self.archive, ref)
-                    if a.date > maxdate.data:
+                    if a.date > maxdate.date:
                         maxdate = a
                 parentID = maxdate.msgid
 	    else:
Index: Mailman/Bouncers/BouncerAPI.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Bouncers/BouncerAPI.py,v
retrieving revision 1.11
retrieving revision 1.11.2.1
diff -u -r1.11 -r1.11.2.1
--- Mailman/Bouncers/BouncerAPI.py	2000/09/21 04:50:10	1.11
+++ Mailman/Bouncers/BouncerAPI.py	2001/07/10 15:00:09	1.11.2.1
@@ -82,6 +82,7 @@
 
 # for testing
 if __name__ == '__main__':
+    import sys
     import mimetools
     from Mailman import MailList
 
Index: Mailman/Bouncers/DSN.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Bouncers/DSN.py,v
retrieving revision 1.7
retrieving revision 1.7.2.1
diff -u -r1.7 -r1.7.2.1
--- Mailman/Bouncers/DSN.py	2000/07/21 05:25:53	1.7
+++ Mailman/Bouncers/DSN.py	2001/07/25 18:04:42	1.7.2.1
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 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
@@ -43,8 +43,8 @@
 
 
 def process(msg):
-    if msg.gettype() <> 'multipart/report' or \
-       msg.getparam('report-type') <> 'delivery-status':
+    if string.lower(msg.gettype()) <> 'multipart/report' or \
+       string.lower(msg.getparam('report-type')) <> 'delivery-status':
         # then
         return None
     boundary = msg.getparam('boundary')
Index: Mailman/Cgi/handle_opts.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Cgi/Attic/handle_opts.py,v
retrieving revision 1.30.2.2
retrieving revision 1.30.2.3
diff -u -r1.30.2.2 -r1.30.2.3
--- Mailman/Cgi/handle_opts.py	2001/05/03 21:05:06	1.30.2.2
+++ Mailman/Cgi/handle_opts.py	2001/07/10 14:52:32	1.30.2.3
@@ -266,14 +266,14 @@
         except Errors.MMNotAMemberError:
             PrintResults(mlist, operation, doc,
                          "%s isn't subscribed to this list."
-                         % mail.GetSender(), user)
+                         % user, user)
         except Errors.MMListNotReadyError:
             PrintResults(mlist, operation, doc, "List is not functional.",
                          user)
         except Errors.MMNoSuchUserError:
             PrintResults(mlist, operation, doc,
                          "%s is not subscribed to this list."
-                         % mail.GetSender(), user)
+                         % user, user)
         except Errors.MMBadPasswordError:
             PrintResults(mlist, operation, doc,
                          "You gave the wrong password.", user)
Index: Mailman/Handlers/Hold.py
===================================================================
RCS file: /cvsroot/mailman/mailman/Mailman/Handlers/Hold.py,v
retrieving revision 1.16
retrieving revision 1.16.2.2
diff -u -r1.16 -r1.16.2.2
--- Mailman/Handlers/Hold.py	2000/08/01 23:02:28	1.16
+++ Mailman/Handlers/Hold.py	2001/05/31 21:05:44	1.16.2.2
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 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
@@ -118,6 +118,11 @@
     if not sender or sender[:len(listname)+6] == adminaddr:
         sender = msg.GetSender(use_envelope=0)
     #
+    # possible administrivia?
+    if mlist.administrivia and Utils.IsAdministrivia(msg):
+        hold_for_approval(mlist, msg, msgdata, Administrivia)
+        # no return
+    #
     # is the poster in the list of explicitly forbidden posters?
     if len(mlist.forbidden_posters):
         forbiddens = Utils.List2Dict(mlist.forbidden_posters)
@@ -175,11 +180,6 @@
        not msgdata.get('fromusenet'):
         # then
         hold_for_approval(mlist, msg, msgdata, ImplicitDestination)
-        # no return
-    #
-    # possible administrivia?
-    if mlist.administrivia and Utils.IsAdministrivia(msg):
-        hold_for_approval(mlist, msg, msgdata, Administrivia)
         # no return
     #
     # suspicious headers?
Index: admin/www/download.ht
===================================================================
RCS file: /cvsroot/mailman/mailman/admin/www/download.ht,v
retrieving revision 1.5.2.6
retrieving revision 1.5.2.7
diff -u -r1.5.2.6 -r1.5.2.7
--- admin/www/download.ht	2001/05/03 21:09:36	1.5.2.6
+++ admin/www/download.ht	2001/07/25 18:08:31	1.5.2.7
@@ -65,9 +65,9 @@
 <h3>Downloading</h3>
 
 <p>Version
-(<!-VERSION--->2.0.5<!-VERSION--->,
+(<!-VERSION--->2.0.6<!-VERSION--->,
 released on
-<!-DATE--->May  4 2001<!-DATE--->)
+<!-DATE--->Jul 25 2001<!-DATE--->)
 is the current GNU release.  It is available from the following mirror sites:
 
 <ul>
Index: admin/www/download.html
===================================================================
RCS file: /cvsroot/mailman/mailman/admin/www/download.html,v
retrieving revision 1.6.2.8
retrieving revision 1.6.2.9
diff -u -r1.6.2.8 -r1.6.2.9
--- admin/www/download.html	2001/05/03 21:09:36	1.6.2.8
+++ admin/www/download.html	2001/07/25 18:08:31	1.6.2.9
@@ -1,6 +1,6 @@
 <HTML>
 <!-- THIS PAGE IS AUTOMATICALLY GENERATED.  DO NOT EDIT. -->
-<!-- Thu May  3 17:09:03 2001 -->
+<!-- Wed Jul 25 14:08:14 2001 -->
 <!-- USING HT2HTML 1.1 -->
 <!-- SEE http://www.wooz.org/barry/software/pyware.html -->
 <!-- User-specified headers:
@@ -237,9 +237,9 @@
 <h3>Downloading</h3>
 
 <p>Version
-(<!-VERSION--->2.0.5<!-VERSION--->,
+(<!-VERSION--->2.0.6<!-VERSION--->,
 released on
-<!-DATE--->May  4 2001<!-DATE--->)
+<!-DATE--->Jul 25 2001<!-DATE--->)
 is the current GNU release.  It is available from the following mirror sites:
 
 <ul>
Index: bin/find_member
===================================================================
RCS file: /cvsroot/mailman/mailman/bin/find_member,v
retrieving revision 1.5
retrieving revision 1.5.2.1
diff -u -r1.5 -r1.5.2.1
--- bin/find_member	2000/09/11 03:58:57	1.5
+++ bin/find_member	2001/07/25 17:38:43	1.5.2.1
@@ -90,7 +90,7 @@
         try:
             mlist = MailList.MailList(listname, lock=0)
         except Errors.MMListError:
-            print 'No such list "%s"' % name
+            print 'No such list "%s"' % listname
             continue
         if options.owners:
             owners = mlist.owner
Index: src/cgi-wrapper.c
===================================================================
RCS file: /cvsroot/mailman/mailman/src/cgi-wrapper.c,v
retrieving revision 1.13
retrieving revision 1.13.2.1
diff -u -r1.13 -r1.13.2.1
--- src/cgi-wrapper.c	2000/03/21 06:26:41	1.13
+++ src/cgi-wrapper.c	2001/05/29 13:20:27	1.13.2.1
@@ -23,7 +23,7 @@
 
 /* passed in by configure */
 #define SCRIPTNAME  SCRIPT
-#define LOG_IDENT   "Mailman cgi-wrapper (" ## SCRIPT ## ")"
+#define LOG_IDENT   "Mailman cgi-wrapper (" SCRIPT ")"
 
 /* GID that CGI scripts run as.  See your Web server's documentation. */
 #define LEGAL_PARENT_GID CGI_GID
Index: src/common.c
===================================================================
RCS file: /cvsroot/mailman/mailman/src/common.c,v
retrieving revision 1.26
retrieving revision 1.26.2.1
diff -u -r1.26 -r1.26.2.1
--- src/common.c	2000/11/09 06:18:02	1.26
+++ src/common.c	2001/05/29 13:20:27	1.26.2.1
@@ -20,7 +20,7 @@
 #include "common.h"
 
 /* passed in by configure */
-#define SCRIPTDIR PREFIX ## "/scripts/"	     /* trailing slash */
+#define SCRIPTDIR PREFIX "/scripts/"	     /* trailing slash */
 #define MODULEDIR PREFIX		     /* no trailing slash */
 
 const char* scriptdir = SCRIPTDIR;