------------------------------------------------------------
revno: 1712
fixes bug: https://launchpad.net/bugs/1695667
committer: Mark Sapiro <m...@msapiro.net>
branch nick: 2.1
timestamp: Mon 2017-06-05 20:48:34 -0700
message:
  Defend against CGI requests with multiple values for the same parameter.
modified:
  Mailman/Cgi/admin.py
  Mailman/Cgi/admindb.py
  Mailman/Cgi/confirm.py
  Mailman/Cgi/create.py
  Mailman/Cgi/edithtml.py
  Mailman/Cgi/listinfo.py
  Mailman/Cgi/options.py
  Mailman/Cgi/private.py
  Mailman/Cgi/rmlist.py
  Mailman/Cgi/roster.py
  Mailman/Cgi/subscribe.py
  Mailman/Gui/Privacy.py
  Mailman/Gui/Topics.py
  NEWS


--
lp:mailman/2.1
https://code.launchpad.net/~mailman-coders/mailman/2.1

Your team Mailman Checkins is subscribed to branch lp:mailman/2.1.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/2.1/+edit-subscription
=== modified file 'Mailman/Cgi/admin.py'
--- Mailman/Cgi/admin.py	2017-04-26 05:33:19 +0000
+++ Mailman/Cgi/admin.py	2017-06-06 03:48:34 +0000
@@ -87,7 +87,7 @@
     # If the user is not authenticated, we're done.
     cgidata = cgi.FieldStorage(keep_blank_values=1)
     try:
-        cgidata.getvalue('csrf_token', '')
+        cgidata.getfirst('csrf_token', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc = Document()
@@ -105,17 +105,17 @@
                    'legend']
     params = cgidata.keys()
     if set(params) - set(safe_params):
-        csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token'))
+        csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'))
     else:
         csrf_checked = True
     # if password is present, void cookie to force password authentication.
-    if cgidata.getvalue('adminpw'):
+    if cgidata.getfirst('adminpw'):
         os.environ['HTTP_COOKIE'] = ''
         csrf_checked = True
 
     if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin,
                                   mm_cfg.AuthSiteAdmin),
-                                 cgidata.getvalue('adminpw', '')):
+                                 cgidata.getfirst('adminpw', '')):
         if cgidata.has_key('adminpw'):
             # This is a re-authorization attempt
             msg = Bold(FontSize('+1', _('Authorization failed.'))).Format()
@@ -155,7 +155,7 @@
     if qsenviron:
         parsedqs = cgi.parse_qs(qsenviron)
     if cgidata.has_key('VARHELP'):
-        varhelp = cgidata.getvalue('VARHELP')
+        varhelp = cgidata.getfirst('VARHELP')
     elif parsedqs:
         # POST methods, even if their actions have a query string, don't get
         # put into FieldStorage's keys :-(
@@ -913,7 +913,7 @@
                 _('(help)')).Format()
     table.AddRow([Label(_('Find member %(link)s:')),
                   TextBox('findmember',
-                          value=cgidata.getvalue('findmember', '')),
+                          value=cgidata.getfirst('findmember', '')),
                   SubmitButton('findmember_btn', _('Search...'))])
     container.AddItem(table)
     container.AddItem('<hr><p>')
@@ -926,7 +926,7 @@
     all = [_m.encode() for _m in mlist.getMembers()]
     all.sort(lambda x, y: cmp(x.lower(), y.lower()))
     # See if the query has a regular expression
-    regexp = cgidata.getvalue('findmember', '').strip()
+    regexp = cgidata.getfirst('findmember', '').strip()
     try:
         regexp = regexp.decode(Utils.GetCharSet(mlist.preferred_language))
     except UnicodeDecodeError:
@@ -1385,14 +1385,14 @@
 def change_options(mlist, category, subcat, cgidata, doc):
     def safeint(formvar, defaultval=None):
         try:
-            return int(cgidata.getvalue(formvar))
+            return int(cgidata.getfirst(formvar))
         except (ValueError, TypeError):
             return defaultval
     confirmed = 0
     # Handle changes to the list moderator password.  Do this before checking
     # the new admin password, since the latter will force a reauthentication.
-    new = cgidata.getvalue('newmodpw', '').strip()
-    confirm = cgidata.getvalue('confirmmodpw', '').strip()
+    new = cgidata.getfirst('newmodpw', '').strip()
+    confirm = cgidata.getfirst('confirmmodpw', '').strip()
     if new or confirm:
         if new == confirm:
             mlist.mod_password = sha_new(new).hexdigest()
@@ -1402,8 +1402,8 @@
             doc.addError(_('Moderator passwords did not match'))
     # Handle changes to the list poster password.  Do this before checking
     # the new admin password, since the latter will force a reauthentication.
-    new = cgidata.getvalue('newpostpw', '').strip()
-    confirm = cgidata.getvalue('confirmpostpw', '').strip()
+    new = cgidata.getfirst('newpostpw', '').strip()
+    confirm = cgidata.getfirst('confirmpostpw', '').strip()
     if new or confirm:
         if new == confirm:
             mlist.post_password = sha_new(new).hexdigest()
@@ -1412,8 +1412,8 @@
         else:
             doc.addError(_('Poster passwords did not match'))
     # Handle changes to the list administrator password
-    new = cgidata.getvalue('newpw', '').strip()
-    confirm = cgidata.getvalue('confirmpw', '').strip()
+    new = cgidata.getfirst('newpw', '').strip()
+    confirm = cgidata.getfirst('confirmpw', '').strip()
     if new or confirm:
         if new == confirm:
             mlist.password = sha_new(new).hexdigest()
@@ -1429,8 +1429,8 @@
         gui.handleForm(mlist, category, subcat, cgidata, doc)
     # mass subscription, removal processing for members category
     subscribers = ''
-    subscribers += cgidata.getvalue('subscribees', '')
-    subscribers += cgidata.getvalue('subscribees_upload', '')
+    subscribers += cgidata.getfirst('subscribees', '')
+    subscribers += cgidata.getfirst('subscribees_upload', '')
     if subscribers:
         entries = filter(None, [n.strip() for n in subscribers.splitlines()])
         send_welcome_msg = safeint('send_welcome_msg_to_this_batch',
@@ -1439,7 +1439,7 @@
                                    mlist.admin_notify_mchanges)
         # Default is to subscribe
         subscribe_or_invite = safeint('subscribe_or_invite', 0)
-        invitation = cgidata.getvalue('invitation', '')
+        invitation = cgidata.getfirst('invitation', '')
         digest = mlist.digest_is_default
         if not mlist.digestable:
             digest = 0
@@ -1540,8 +1540,8 @@
             doc.AddItem('<p>')
     # Address Changes
     if cgidata.has_key('change_from'):
-        change_from = cgidata.getvalue('change_from', '')
-        change_to = cgidata.getvalue('change_to', '')
+        change_from = cgidata.getfirst('change_from', '')
+        change_to = cgidata.getfirst('change_to', '')
         schange_from = Utils.websafe(change_from)
         schange_to = Utils.websafe(change_to)
         success = False
@@ -1585,7 +1585,7 @@
 """))
         subject = _('%(list_name)s address change notice.')
         i18n.set_translation(otrans)
-        if success and cgidata.getvalue('notice_old', '') == 'yes':
+        if success and cgidata.getfirst('notice_old', '') == 'yes':
             # Send notice to old address.
             msg = Message.UserNotification(change_from,
                 mlist.GetOwnerEmail(),
@@ -1595,7 +1595,7 @@
                 )
             msg.send(mlist)
             doc.AddItem(Header(3, _('Notification sent to %(schange_from)s.')))
-        if success and cgidata.getvalue('notice_new', '') == 'yes':
+        if success and cgidata.getfirst('notice_new', '') == 'yes':
             # Send notice to new address.
             msg = Message.UserNotification(change_to,
                 mlist.GetOwnerEmail(),
@@ -1648,16 +1648,16 @@
                 # BAW: Hmm...
                 pass
 
-            newname = cgidata.getvalue(quser+'_realname', '')
+            newname = cgidata.getfirst(quser+'_realname', '')
             newname = Utils.canonstr(newname, mlist.preferred_language)
             mlist.setMemberName(user, newname)
 
-            newlang = cgidata.getvalue(quser+'_language')
+            newlang = cgidata.getfirst(quser+'_language')
             oldlang = mlist.getMemberLanguage(user)
             if Utils.IsLanguage(newlang) and newlang <> oldlang:
                 mlist.setMemberLanguage(user, newlang)
 
-            moderate = not not cgidata.getvalue(quser+'_mod')
+            moderate = not not cgidata.getfirst(quser+'_mod')
             mlist.setMemberOption(user, mm_cfg.Moderate, moderate)
 
             # Set the `nomail' flag, but only if the user isn't already

=== modified file 'Mailman/Cgi/admindb.py'
--- Mailman/Cgi/admindb.py	2016-08-27 03:47:49 +0000
+++ Mailman/Cgi/admindb.py	2017-06-06 03:48:34 +0000
@@ -127,7 +127,7 @@
     # Make sure the user is authorized to see this page.
     cgidata = cgi.FieldStorage(keep_blank_values=1)
     try:
-        cgidata.getvalue('adminpw', '')
+        cgidata.getfirst('adminpw', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc = Document()
@@ -143,18 +143,18 @@
     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'))
+        csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'))
     else:
         csrf_checked = True
     # if password is present, void cookie to force password authentication.
-    if cgidata.getvalue('adminpw'):
+    if cgidata.getfirst('adminpw'):
         os.environ['HTTP_COOKIE'] = ''
         csrf_checked = True
 
     if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin,
                                   mm_cfg.AuthListModerator,
                                   mm_cfg.AuthSiteAdmin),
-                                 cgidata.getvalue('adminpw', '')):
+                                 cgidata.getfirst('adminpw', '')):
         if cgidata.has_key('adminpw'):
             # This is a re-authorization attempt
             msg = Bold(FontSize('+1', _('Authorization failed.'))).Format()
@@ -805,18 +805,18 @@
                 action = k[:len(prefix)-1]
                 qsender = k[len(prefix):]
                 sender = unquote_plus(qsender)
-                value = cgidata.getvalue(k)
+                value = cgidata.getfirst(k)
                 senderactions.setdefault(sender, {})[action] = value
                 for id in cgidata.getlist(qsender):
                     senderactions[sender].setdefault('message_ids',
                                                      []).append(int(id))
     # discard-all-defers
     try:
-        discardalldefersp = cgidata.getvalue('discardalldefersp', 0)
+        discardalldefersp = cgidata.getfirst('discardalldefersp', 0)
     except ValueError:
         discardalldefersp = 0
     # Get the summary sequence
-    ssort = int(cgidata.getvalue('summary_sort', SSENDER))
+    ssort = int(cgidata.getfirst('summary_sort', SSENDER))
     for sender in senderactions.keys():
         actions = senderactions[sender]
         # Handle what to do about all this sender's held messages
@@ -935,7 +935,7 @@
             forwardaddr = cgidata[forwardaddrkey].value
         # Should we ban this address?  Do this check before handling the
         # request id because that will evict the record.
-        if cgidata.getvalue(bankey):
+        if cgidata.getfirst(bankey):
             sender = mlist.GetRecord(request_id)[1]
             if sender not in mlist.ban_list:
                 # We don't need to validate the sender.  An invalid address

=== modified file 'Mailman/Cgi/confirm.py'
--- Mailman/Cgi/confirm.py	2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/confirm.py	2017-06-06 03:48:34 +0000
@@ -74,7 +74,7 @@
     # Get the form data to see if this is a second-step confirmation
     cgidata = cgi.FieldStorage(keep_blank_values=1)
     try:
-        cookie = cgidata.getvalue('cookie')
+        cookie = cgidata.getfirst('cookie')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
@@ -124,17 +124,17 @@
 
     try:
         if content[0] == Pending.SUBSCRIPTION:
-            if cgidata.getvalue('cancel'):
+            if cgidata.getfirst('cancel'):
                 subscription_cancel(mlist, doc, cookie)
-            elif cgidata.getvalue('submit'):
+            elif cgidata.getfirst('submit'):
                 subscription_confirm(mlist, doc, cookie, cgidata)
             else:
                 subscription_prompt(mlist, doc, cookie, content[1])
         elif content[0] == Pending.UNSUBSCRIPTION:
             try:
-                if cgidata.getvalue('cancel'):
+                if cgidata.getfirst('cancel'):
                     unsubscription_cancel(mlist, doc, cookie)
-                elif cgidata.getvalue('submit'):
+                elif cgidata.getfirst('submit'):
                     unsubscription_confirm(mlist, doc, cookie)
                 else:
                     unsubscription_prompt(mlist, doc, cookie, *content[1:])
@@ -145,9 +145,9 @@
                 # Expunge this record from the pending database.
                 expunge(mlist, cookie)
         elif content[0] == Pending.CHANGE_OF_ADDRESS:
-            if cgidata.getvalue('cancel'):
+            if cgidata.getfirst('cancel'):
                 addrchange_cancel(mlist, doc, cookie)
-            elif cgidata.getvalue('submit'):
+            elif cgidata.getfirst('submit'):
                 addrchange_confirm(mlist, doc, cookie)
             else:
                 # Watch out for users who have unsubscribed themselves in the
@@ -161,16 +161,16 @@
                     # Expunge this record from the pending database.
                     expunge(mlist, cookie)
         elif content[0] == Pending.HELD_MESSAGE:
-            if cgidata.getvalue('cancel'):
+            if cgidata.getfirst('cancel'):
                 heldmsg_cancel(mlist, doc, cookie)
-            elif cgidata.getvalue('submit'):
+            elif cgidata.getfirst('submit'):
                 heldmsg_confirm(mlist, doc, cookie)
             else:
                 heldmsg_prompt(mlist, doc, cookie, *content[1:])
         elif content[0] == Pending.RE_ENABLE:
-            if cgidata.getvalue('cancel'):
+            if cgidata.getfirst('cancel'):
                 reenable_cancel(mlist, doc, cookie)
-            elif cgidata.getvalue('submit'):
+            elif cgidata.getfirst('submit'):
                 reenable_confirm(mlist, doc, cookie)
             else:
                 reenable_prompt(mlist, doc, cookie, *content[1:])
@@ -349,20 +349,20 @@
         try:
             # Some pending values may be overridden in the form.  email of
             # course is hardcoded. ;)
-            lang = cgidata.getvalue('language')
+            lang = cgidata.getfirst('language')
             if not Utils.IsLanguage(lang):
                 lang = mlist.preferred_language
             i18n.set_language(lang)
             doc.set_language(lang)
             if cgidata.has_key('digests'):
                 try:
-                    digest = int(cgidata.getvalue('digests'))
+                    digest = int(cgidata.getfirst('digests'))
                 except ValueError:
                     digest = None
             else:
                 digest = None
             userdesc = mlist.pend_confirm(cookie, expunge=False)[1]
-            fullname = cgidata.getvalue('realname', None)
+            fullname = cgidata.getfirst('realname', None)
             if fullname is not None:
                 fullname = Utils.canonstr(fullname, lang)
             overrides = UserDesc(fullname=fullname, digest=digest, lang=lang)

=== modified file 'Mailman/Cgi/create.py'
--- Mailman/Cgi/create.py	2017-06-02 22:25:12 +0000
+++ Mailman/Cgi/create.py	2017-06-06 03:48:34 +0000
@@ -44,7 +44,7 @@
 
     cgidata = cgi.FieldStorage()
     try:
-        cgidata.getvalue('doit', '')
+        cgidata.getfirst('doit', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
@@ -85,26 +85,26 @@
 
 def process_request(doc, cgidata):
     # Lowercase the listname since this is treated as the "internal" name.
-    listname = cgidata.getvalue('listname', '').strip().lower()
-    owner    = cgidata.getvalue('owner', '').strip()
+    listname = cgidata.getfirst('listname', '').strip().lower()
+    owner    = cgidata.getfirst('owner', '').strip()
     try:
-        autogen  = int(cgidata.getvalue('autogen', '0'))
+        autogen  = int(cgidata.getfirst('autogen', '0'))
     except ValueError:
         autogen = 0
     try:
-        notify  = int(cgidata.getvalue('notify', '0'))
+        notify  = int(cgidata.getfirst('notify', '0'))
     except ValueError:
         notify = 0
     try:
-        moderate = int(cgidata.getvalue('moderate',
+        moderate = int(cgidata.getfirst('moderate',
                        mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION))
     except ValueError:
         moderate = mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION
 
-    password = cgidata.getvalue('password', '').strip()
-    confirm  = cgidata.getvalue('confirm', '').strip()
-    auth     = cgidata.getvalue('auth', '').strip()
-    langs    = cgidata.getvalue('langs', [mm_cfg.DEFAULT_SERVER_LANGUAGE])
+    password = cgidata.getfirst('password', '').strip()
+    confirm  = cgidata.getfirst('confirm', '').strip()
+    auth     = cgidata.getfirst('auth', '').strip()
+    langs    = cgidata.getfirst('langs', [mm_cfg.DEFAULT_SERVER_LANGUAGE])
 
     if not isinstance(langs, ListType):
         langs = [langs]
@@ -342,14 +342,14 @@
     ftable.AddRow([Center(Italic(_('List Identity')))])
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
 
-    listname = cgidata.getvalue('listname', '')
+    listname = cgidata.getfirst('listname', '')
     # MAS: Don't websafe twice.  TextBox does it.
     ftable.AddRow([Label(_('Name of list:')),
                    TextBox('listname', listname)])
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
 
-    owner = cgidata.getvalue('owner', '')
+    owner = cgidata.getfirst('owner', '')
     # MAS: Don't websafe twice.  TextBox does it.
     ftable.AddRow([Label(_('Initial list owner address:')),
                    TextBox('owner', owner)])
@@ -357,7 +357,7 @@
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
 
     try:
-        autogen = int(cgidata.getvalue('autogen', '0'))
+        autogen = int(cgidata.getfirst('autogen', '0'))
     except ValueError:
         autogen = 0
     ftable.AddRow([Label(_('Auto-generate initial list password?')),
@@ -367,24 +367,24 @@
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
 
-    safepasswd = Utils.websafe(cgidata.getvalue('password', ''))
+    safepasswd = Utils.websafe(cgidata.getfirst('password', ''))
     ftable.AddRow([Label(_('Initial list password:')),
                    PasswordBox('password', safepasswd)])
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
 
-    safeconfirm = Utils.websafe(cgidata.getvalue('confirm', ''))
+    safeconfirm = Utils.websafe(cgidata.getfirst('confirm', ''))
     ftable.AddRow([Label(_('Confirm initial password:')),
                    PasswordBox('confirm', safeconfirm)])
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
     ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
 
     try:
-        notify = int(cgidata.getvalue('notify', '1'))
+        notify = int(cgidata.getfirst('notify', '1'))
     except ValueError:
         notify = 1
     try:
-        moderate = int(cgidata.getvalue('moderate',
+        moderate = int(cgidata.getfirst('moderate',
                        mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION))
     except ValueError:
         moderate = mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION

=== modified file 'Mailman/Cgi/edithtml.py'
--- Mailman/Cgi/edithtml.py	2016-08-27 03:47:49 +0000
+++ Mailman/Cgi/edithtml.py	2017-06-06 03:48:34 +0000
@@ -97,7 +97,7 @@
     # Must be authenticated to get any farther
     cgidata = cgi.FieldStorage()
     try:
-        cgidata.getvalue('adminpw', '')
+        cgidata.getfirst('adminpw', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
@@ -111,18 +111,18 @@
     safe_params = ['VARHELP', 'adminpw', 'admlogin']
     params = cgidata.keys()
     if set(params) - set(safe_params):
-        csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token'))
+        csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'))
     else:
         csrf_checked = True
     # if password is present, void cookie to force password authentication.
-    if cgidata.getvalue('adminpw'):
+    if cgidata.getfirst('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),
-                                 cgidata.getvalue('adminpw', '')):
+                                 cgidata.getfirst('adminpw', '')):
         if cgidata.has_key('admlogin'):
             # This is a re-authorization attempt
             msg = Bold(FontSize('+1', _('Authorization failed.'))).Format()

=== modified file 'Mailman/Cgi/listinfo.py'
--- Mailman/Cgi/listinfo.py	2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/listinfo.py	2017-06-06 03:48:34 +0000
@@ -59,7 +59,7 @@
     # See if the user want to see this page in other language
     cgidata = cgi.FieldStorage()
     try:
-        language = cgidata.getvalue('language')
+        language = cgidata.getfirst('language')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc = Document()

=== modified file 'Mailman/Cgi/options.py'
--- Mailman/Cgi/options.py	2017-06-05 01:52:11 +0000
+++ Mailman/Cgi/options.py	2017-06-06 03:48:34 +0000
@@ -122,11 +122,11 @@
         return
 
     if set(params) - set(safe_params):
-        csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token'))
+        csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'))
     else:
         csrf_checked = True
     # if password is present, void cookie to force password authentication.
-    if cgidata.getvalue('password'):
+    if cgidata.getfirst('password'):
         os.environ['HTTP_COOKIE'] = ''
         csrf_checked = True
 
@@ -134,21 +134,21 @@
     # 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
     # not available, use the list's default language.
-    language = cgidata.getvalue('language')
+    language = cgidata.getfirst('language')
     if not Utils.IsLanguage(language):
         language = mlist.preferred_language
     i18n.set_language(language)
     doc.set_language(language)
 
     if lenparts < 2:
-        user = cgidata.getvalue('email')
+        user = cgidata.getfirst('email')
         if not user:
             # If we're coming from the listinfo page and we left the email
             # address field blank, it's not an error.  Likewise if we're
             # coming from anywhere else. Only issue the error if we came
             # via one of our buttons.
-            if (cgidata.getvalue('login') or cgidata.getvalue('login-unsub')
-                    or cgidata.getvalue('login-remind')):
+            if (cgidata.getfirst('login') or cgidata.getfirst('login-unsub')
+                    or cgidata.getfirst('login-remind')):
                 doc.addError(_('No address given'))
             loginpage(mlist, doc, None, language)
             print doc.Format()
@@ -194,7 +194,7 @@
     # And now we know the user making the request, so set things up to for the
     # user's stored preferred language, overridden by any form settings for
     # their new language preference.
-    userlang = cgidata.getvalue('language')
+    userlang = cgidata.getfirst('language')
     if not Utils.IsLanguage(userlang):
         userlang = mlist.getMemberLanguage(user)
     doc.set_language(userlang)
@@ -279,7 +279,7 @@
         return
 
     # Get the password from the form.
-    password = cgidata.getvalue('password', '').strip()
+    password = cgidata.getfirst('password', '').strip()
     # Check authentication.  We need to know if the credentials match the user
     # or the site admin, because they are the only ones who are allowed to
     # change things globally.  Specifically, the list admin may not change
@@ -391,18 +391,18 @@
     if cgidata.has_key('change-of-address'):
         # We could be changing the user's full name, email address, or both.
         # Watch out for non-ASCII characters in the member's name.
-        membername = cgidata.getvalue('fullname')
+        membername = cgidata.getfirst('fullname')
         # Canonicalize the member's name
         membername = Utils.canonstr(membername, language)
-        newaddr = cgidata.getvalue('new-address')
-        confirmaddr = cgidata.getvalue('confirm-address')
+        newaddr = cgidata.getfirst('new-address')
+        confirmaddr = cgidata.getfirst('confirm-address')
 
         oldname = mlist.getMemberName(user)
         set_address = set_membername = 0
 
         # See if the user wants to change their email address globally.  The
         # list admin is /not/ allowed to make global changes.
-        globally = cgidata.getvalue('changeaddr-globally')
+        globally = cgidata.getfirst('changeaddr-globally')
         if globally and not is_user_or_siteadmin:
             doc.addError(_("""The list administrator may not change the names
             or addresses for this user's other subscriptions.  However, the
@@ -515,8 +515,8 @@
             options_page(mlist, doc, user, cpuser, userlang)
             print doc.Format()
             return
-        newpw = cgidata.getvalue('newpw', '').strip()
-        confirmpw = cgidata.getvalue('confpw', '').strip()
+        newpw = cgidata.getfirst('newpw', '').strip()
+        confirmpw = cgidata.getfirst('confpw', '').strip()
         if not newpw or not confirmpw:
             options_page(mlist, doc, user, cpuser, userlang,
                          _('Passwords may not be blank'))
@@ -530,7 +530,7 @@
 
         # See if the user wants to change their passwords globally, however
         # the list admin is /not/ allowed to change passwords globally.
-        pw_globally = cgidata.getvalue('pw-globally')
+        pw_globally = cgidata.getfirst('pw-globally')
         if pw_globally and not is_user_or_siteadmin:
             doc.addError(_("""The list administrator may not change the
             password for this user's other subscriptions.  However, the
@@ -555,7 +555,7 @@
 
     if cgidata.has_key('unsub'):
         # Was the confirming check box turned on?
-        if not cgidata.getvalue('unsubconfirm'):
+        if not cgidata.getfirst('unsubconfirm'):
             options_page(
                 mlist, doc, user, cpuser, userlang,
                 _('''You must confirm your unsubscription request by turning
@@ -635,7 +635,7 @@
                            ('nodupes',     mm_cfg.DontReceiveDuplicates),
                            ):
             try:
-                newval = int(cgidata.getvalue(item))
+                newval = int(cgidata.getfirst(item))
             except (TypeError, ValueError):
                 newval = None
 
@@ -671,7 +671,7 @@
         # Process user selected topics, but don't make the changes to the
         # MailList object; we must do that down below when the list is
         # locked.
-        topicnames = cgidata.getvalue('usertopic')
+        topicnames = cgidata.getfirst('usertopic')
         if topicnames:
             # Some topics were selected.  topicnames can actually be a string
             # or a list of strings depending on whether more than one topic
@@ -725,7 +725,7 @@
 
         # The enable/disable option and the password remind option may have
         # their global flags sets.
-        if cgidata.getvalue('deliver-globally'):
+        if cgidata.getfirst('deliver-globally'):
             # Yes, this is inefficient, but the list is so small it shouldn't
             # make much of a difference.
             for flag, newval in newvals:
@@ -733,19 +733,19 @@
                     globalopts.enable = newval
                     break
 
-        if cgidata.getvalue('remind-globally'):
+        if cgidata.getfirst('remind-globally'):
             for flag, newval in newvals:
                 if flag == mm_cfg.SuppressPasswordReminder:
                     globalopts.remind = newval
                     break
 
-        if cgidata.getvalue('nodupes-globally'):
+        if cgidata.getfirst('nodupes-globally'):
             for flag, newval in newvals:
                 if flag == mm_cfg.DontReceiveDuplicates:
                     globalopts.nodupes = newval
                     break
 
-        if cgidata.getvalue('mime-globally'):
+        if cgidata.getfirst('mime-globally'):
             for flag, newval in newvals:
                 if flag == mm_cfg.DisableMime:
                     globalopts.mime = newval

=== modified file 'Mailman/Cgi/private.py'
--- Mailman/Cgi/private.py	2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/private.py	2017-06-06 03:48:34 +0000
@@ -119,7 +119,7 @@
 
     cgidata = cgi.FieldStorage()
     try:
-        username = cgidata.getvalue('username', '')
+        username = cgidata.getfirst('username', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
@@ -128,7 +128,7 @@
         print 'Status: 400 Bad Request'
         print doc.Format()
         return
-    password = cgidata.getvalue('password', '')
+    password = cgidata.getfirst('password', '')
 
     is_auth = 0
     realname = mlist.real_name

=== modified file 'Mailman/Cgi/rmlist.py'
--- Mailman/Cgi/rmlist.py	2016-07-14 21:27:49 +0000
+++ Mailman/Cgi/rmlist.py	2017-06-06 03:48:34 +0000
@@ -42,7 +42,7 @@
 
     cgidata = cgi.FieldStorage()
     try:
-        cgidata.getvalue('password', '')
+        cgidata.getfirst('password', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
@@ -113,9 +113,9 @@
 
 
 def process_request(doc, cgidata, mlist):
-    password = cgidata.getvalue('password', '').strip()
+    password = cgidata.getfirst('password', '').strip()
     try:
-        delarchives = int(cgidata.getvalue('delarchives', '0'))
+        delarchives = int(cgidata.getfirst('delarchives', '0'))
     except ValueError:
         delarchives = 0
 

=== modified file 'Mailman/Cgi/roster.py'
--- Mailman/Cgi/roster.py	2017-02-15 08:35:23 +0000
+++ Mailman/Cgi/roster.py	2017-06-06 03:48:34 +0000
@@ -64,7 +64,7 @@
 
     # messages in form should go in selected language (if any...)
     try:
-        lang = cgidata.getvalue('language')
+        lang = cgidata.getfirst('language')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc = Document()
@@ -85,8 +85,8 @@
     # "admin"-only, then we try to cookie authenticate the user, and failing
     # that, we check roster-email and roster-pw fields for a valid password.
     # (also allowed: the list moderator, the list admin, and the site admin).
-    password = cgidata.getvalue('roster-pw', '').strip()
-    addr = cgidata.getvalue('roster-email', '').strip()
+    password = cgidata.getfirst('roster-pw', '').strip()
+    addr = cgidata.getfirst('roster-email', '').strip()
     list_hidden = (not mlist.WebAuthenticate((mm_cfg.AuthUser,),
                                              password, addr)
                    and mlist.WebAuthenticate((mm_cfg.AuthListModerator,

=== modified file 'Mailman/Cgi/subscribe.py'
--- Mailman/Cgi/subscribe.py	2017-02-23 06:05:56 +0000
+++ Mailman/Cgi/subscribe.py	2017-06-06 03:48:34 +0000
@@ -71,7 +71,7 @@
     # for the results.  If not, use the list's preferred language.
     cgidata = cgi.FieldStorage()
     try:
-        language = cgidata.getvalue('language', '')
+        language = cgidata.getfirst('language', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
@@ -119,11 +119,11 @@
     results = []
 
     # The email address being subscribed, required
-    email = cgidata.getvalue('email', '').strip()
+    email = cgidata.getfirst('email', '').strip()
     if not email:
         results.append(_('You must supply a valid email address.'))
 
-    fullname = cgidata.getvalue('fullname', '')
+    fullname = cgidata.getfirst('fullname', '')
     # Canonicalize the full name
     fullname = Utils.canonstr(fullname, lang)
     # Who was doing the subscribing?
@@ -144,7 +144,7 @@
             #        for our hash so it doesn't matter.
             remote1 = remote.rsplit(':', 1)[0]
         try:
-            ftime, fhash = cgidata.getvalue('sub_form_token', '').split(':')
+            ftime, fhash = cgidata.getfirst('sub_form_token', '').split(':')
             then = int(ftime)
         except ValueError:
             ftime = fhash = ''
@@ -170,8 +170,8 @@
         syslog('mischief', 'Attempt to self subscribe %s: %s', email, remote)
         results.append(_('You may not subscribe a list to itself!'))
     # If the user did not supply a password, generate one for him
-    password = cgidata.getvalue('pw', '').strip()
-    confirmed = cgidata.getvalue('pw-conf', '').strip()
+    password = cgidata.getfirst('pw', '').strip()
+    confirmed = cgidata.getfirst('pw-conf', '').strip()
 
     if not password and not confirmed:
         password = Utils.MakeRandomPassword()
@@ -181,7 +181,7 @@
         results.append(_('Your passwords did not match.'))
 
     # Get the digest option for the subscription.
-    digestflag = cgidata.getvalue('digest')
+    digestflag = cgidata.getfirst('digest')
     if digestflag:
         try:
             digest = int(digestflag)

=== modified file 'Mailman/Gui/Privacy.py'
--- Mailman/Gui/Privacy.py	2016-07-15 02:10:24 +0000
+++ Mailman/Gui/Privacy.py	2017-06-06 03:48:34 +0000
@@ -641,9 +641,9 @@
             if cgidata.has_key(deltag):
                 continue
             # Get the data for the current box
-            pattern = cgidata.getvalue(reboxtag)
+            pattern = cgidata.getfirst(reboxtag)
             try:
-                action  = int(cgidata.getvalue(actiontag))
+                action  = int(cgidata.getfirst(actiontag))
                 # We'll get a TypeError when the actiontag is missing and the
                 # .getvalue() call returns None.
             except (ValueError, TypeError):
@@ -682,7 +682,7 @@
             # Was this an add item?
             if cgidata.has_key(addtag):
                 # Where should the new one be added?
-                where = cgidata.getvalue(wheretag)
+                where = cgidata.getfirst(wheretag)
                 if where == 'before':
                     # Add a new empty rule box before the current one
                     rules.append(('', mm_cfg.DEFER, True))

=== modified file 'Mailman/Gui/Topics.py'
--- Mailman/Gui/Topics.py	2015-01-23 00:09:03 +0000
+++ Mailman/Gui/Topics.py	2017-06-06 03:48:34 +0000
@@ -114,9 +114,9 @@
             if cgidata.has_key(deltag):
                 continue
             # Get the data for the current box
-            name  = cgidata.getvalue(boxtag)
-            pattern = cgidata.getvalue(reboxtag)
-            desc  = cgidata.getvalue(desctag)
+            name  = cgidata.getfirst(boxtag)
+            pattern = cgidata.getfirst(reboxtag)
+            desc  = cgidata.getfirst(desctag)
             if name is None:
                 # We came to the end of the boxes
                 break
@@ -138,7 +138,7 @@
             # Was this an add item?
             if cgidata.has_key(addtag):
                 # Where should the new one be added?
-                where = cgidata.getvalue(wheretag)
+                where = cgidata.getfirst(wheretag)
                 if where == 'before':
                     # Add a new empty topics box before the current one
                     topics.append(('', '', '', True))
@@ -154,14 +154,14 @@
         # options.
         mlist.topics = topics
         try:
-            mlist.topics_enabled = int(cgidata.getvalue(
+            mlist.topics_enabled = int(cgidata.getfirst(
                 'topics_enabled',
                 mlist.topics_enabled))
         except ValueError:
             # BAW: should really print a warning
             pass
         try:
-            mlist.topics_bodylines_limit = int(cgidata.getvalue(
+            mlist.topics_bodylines_limit = int(cgidata.getfirst(
                 'topics_bodylines_limit',
                 mlist.topics_bodylines_limit))
         except ValueError:

=== modified file 'NEWS'
--- NEWS	2017-06-05 01:52:11 +0000
+++ NEWS	2017-06-06 03:48:34 +0000
@@ -9,6 +9,10 @@
 
   Bug fixes and other patches
 
+    - Defended against certain web attacks that cause exceptions and "we hit
+      a bug" responses when POST data or query fragments contain multiple
+      values for the same parameter.  (LP: #1695667)
+
     - The fix for LP: #1614841 caused a regression in the options CGI.  This
       has been fixed.  (LP: #1602608)
 

_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to