From 4899c4bbc49202ae6973a71017262bfba572edee Mon Sep 17 00:00:00 2001
From: msapiro <>
Date: Mon, 12 Dec 2005 00:47:25 +0000
Subject: Fixes for bug 1080943. Add error response for ./ and ../ in URL

---
 Mailman/Cgi/private.py | 33 +++++++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

(limited to 'Mailman/Cgi')

diff --git a/Mailman/Cgi/private.py b/Mailman/Cgi/private.py
index cbab3b06..5830e933 100644
--- a/Mailman/Cgi/private.py
+++ b/Mailman/Cgi/private.py
@@ -12,7 +12,8 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
 
 """Provide a password-interface wrapper around private archives.
 """
@@ -67,10 +68,17 @@ def main():
         return
 
     path = os.environ.get('PATH_INFO')
+    tpath = true_path(path)
+    if tpath <> path[1:]:
+        msg = _('Private archive - "./" and "../" not allowed in URL.')
+        doc.SetTitle(msg)
+        doc.AddItem(Header(2, msg))
+        print doc.Format()
+        syslog('error', 'Private archive hostile path: %s', path)
+        return
     # BAW: This needs to be converted to the Site module abstraction
     true_filename = os.path.join(
-        mm_cfg.PRIVATE_ARCHIVE_FILE_DIR,
-        true_path(path))
+        mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, tpath)
 
     listname = parts[0].lower()
     mboxfile = ''
@@ -127,11 +135,24 @@ def main():
         # Output the password form
         charset = Utils.GetCharSet(mlist.preferred_language)
         print 'Content-type: text/html; charset=' + charset + '\n\n'
-        while path and path[0] == '/':
-            path=path[1:]  # Remove leading /'s
+        # Put the original full path in the authorization form, but avoid
+        # trailing slash if we're not adding parts. We add it below.
+        action = mlist.GetScriptURL('private', absolute=1)
+        if parts[1:]:
+            action = os.path.join(action, SLASH.join(parts[1:]))
+        # If we added '/index.html' to true_filename, add a slash to the
+        # URL. We need this because we no longer add the trailing slash in
+        # the private.html template. It's always OK to test parts[-1]
+        # since we've already verified parts[0] is listname.
+        # The basic rule is if the post URL (action) is a directory, it must
+        # be slash terminated, and not if it's a file. Otherwise, relative
+        # links in the target archive page don't work.
+        if true_filename.endswith('/index.html') and \
+                parts[-1] <> 'index.html':
+            action += SLASH
         print Utils.maketext(
             'private.html',
-            {'action'  : mlist.GetScriptURL('private', absolute=1),
+            {'action'  : action,
              'realname': mlist.real_name,
              'message' : message,
              }, mlist=mlist)
-- 
cgit v1.2.3