diff options
author | Mark Sapiro <mark@msapiro.net> | 2017-06-05 20:48:34 -0700 |
---|---|---|
committer | Mark Sapiro <mark@msapiro.net> | 2017-06-05 20:48:34 -0700 |
commit | 0d11dc90ee6fc9cc61d32ca3ea6819ca95ac1c12 (patch) | |
tree | f7743c3b5fc245e214bc94da3266bd16f9d664e2 | |
parent | 845dc52970be426af2a766be4609a8bef2bd1c05 (diff) | |
download | mailman2-0d11dc90ee6fc9cc61d32ca3ea6819ca95ac1c12.tar.gz mailman2-0d11dc90ee6fc9cc61d32ca3ea6819ca95ac1c12.tar.xz mailman2-0d11dc90ee6fc9cc61d32ca3ea6819ca95ac1c12.zip |
Defend against CGI requests with multiple values for the same parameter.
-rw-r--r-- | Mailman/Cgi/admin.py | 48 | ||||
-rw-r--r-- | Mailman/Cgi/admindb.py | 16 | ||||
-rw-r--r-- | Mailman/Cgi/confirm.py | 28 | ||||
-rw-r--r-- | Mailman/Cgi/create.py | 34 | ||||
-rw-r--r-- | Mailman/Cgi/edithtml.py | 8 | ||||
-rw-r--r-- | Mailman/Cgi/listinfo.py | 2 | ||||
-rw-r--r-- | Mailman/Cgi/options.py | 44 | ||||
-rwxr-xr-x | Mailman/Cgi/private.py | 4 | ||||
-rw-r--r-- | Mailman/Cgi/rmlist.py | 6 | ||||
-rw-r--r-- | Mailman/Cgi/roster.py | 6 | ||||
-rwxr-xr-x | Mailman/Cgi/subscribe.py | 14 | ||||
-rw-r--r-- | Mailman/Gui/Privacy.py | 6 | ||||
-rw-r--r-- | Mailman/Gui/Topics.py | 12 | ||||
-rw-r--r-- | NEWS | 4 |
14 files changed, 118 insertions, 114 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py index 41875533..f96b175a 100644 --- a/Mailman/Cgi/admin.py +++ b/Mailman/Cgi/admin.py @@ -87,7 +87,7 @@ def main(): # 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 @@ def main(): '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 @@ def main(): 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 @@ def membership_options(mlist, subcat, cgidata, doc, form): _('(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 @@ def membership_options(mlist, subcat, cgidata, doc, form): 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 submit_button(name='submit'): 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 @@ def change_options(mlist, category, subcat, cgidata, doc): 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 @@ def change_options(mlist, category, subcat, cgidata, doc): 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 @@ def change_options(mlist, category, subcat, cgidata, doc): 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 @@ def change_options(mlist, category, subcat, cgidata, doc): 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 @@ def change_options(mlist, category, subcat, cgidata, doc): 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 @@ def change_options(mlist, category, subcat, cgidata, doc): """)) 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 @@ def change_options(mlist, category, subcat, cgidata, doc): ) 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 @@ def change_options(mlist, category, subcat, cgidata, doc): # 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 diff --git a/Mailman/Cgi/admindb.py b/Mailman/Cgi/admindb.py index 3c9f4002..5f72d9c1 100644 --- a/Mailman/Cgi/admindb.py +++ b/Mailman/Cgi/admindb.py @@ -127,7 +127,7 @@ def main(): # 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 @@ def main(): 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 @@ def process_form(mlist, doc, cgidata): 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 @@ def process_form(mlist, doc, cgidata): 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 diff --git a/Mailman/Cgi/confirm.py b/Mailman/Cgi/confirm.py index fec69dd2..1cb640b8 100644 --- a/Mailman/Cgi/confirm.py +++ b/Mailman/Cgi/confirm.py @@ -74,7 +74,7 @@ def main(): # 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 @@ def main(): 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 @@ def main(): # 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 @@ def main(): # 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 @@ def subscription_confirm(mlist, doc, cookie, cgidata): 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) diff --git a/Mailman/Cgi/create.py b/Mailman/Cgi/create.py index 3c2a7dc4..9421731f 100644 --- a/Mailman/Cgi/create.py +++ b/Mailman/Cgi/create.py @@ -44,7 +44,7 @@ def main(): 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 main(): 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 @@ def request_creation(doc, cgidata=dummy, errmsg=None): 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 @@ def request_creation(doc, cgidata=dummy, errmsg=None): 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 @@ def request_creation(doc, cgidata=dummy, errmsg=None): 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 diff --git a/Mailman/Cgi/edithtml.py b/Mailman/Cgi/edithtml.py index 0628f30b..f8ed6714 100644 --- a/Mailman/Cgi/edithtml.py +++ b/Mailman/Cgi/edithtml.py @@ -97,7 +97,7 @@ def main(): # 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 @@ def main(): 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() diff --git a/Mailman/Cgi/listinfo.py b/Mailman/Cgi/listinfo.py index 340f0fc1..6b967b7c 100644 --- a/Mailman/Cgi/listinfo.py +++ b/Mailman/Cgi/listinfo.py @@ -59,7 +59,7 @@ def main(): # 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() diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py index 0bfa510a..d7411f69 100644 --- a/Mailman/Cgi/options.py +++ b/Mailman/Cgi/options.py @@ -122,11 +122,11 @@ def main(): 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 @@ def main(): # 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 @@ def main(): # 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 @@ def main(): 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 @@ def main(): 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 @@ address. Upon confirmation, any other mailing list containing the address 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 @@ address. Upon confirmation, any other mailing list containing the address # 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 @@ address. Upon confirmation, any other mailing list containing the address 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 @@ address. Upon confirmation, any other mailing list containing the address ('nodupes', mm_cfg.DontReceiveDuplicates), ): try: - newval = int(cgidata.getvalue(item)) + newval = int(cgidata.getfirst(item)) except (TypeError, ValueError): newval = None @@ -671,7 +671,7 @@ address. Upon confirmation, any other mailing list containing the address # 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 @@ address. Upon confirmation, any other mailing list containing the address # 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 @@ address. Upon confirmation, any other mailing list containing the address 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 diff --git a/Mailman/Cgi/private.py b/Mailman/Cgi/private.py index 0f7597a2..ce3c6563 100755 --- a/Mailman/Cgi/private.py +++ b/Mailman/Cgi/private.py @@ -119,7 +119,7 @@ def main(): 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 @@ def main(): print 'Status: 400 Bad Request' print doc.Format() return - password = cgidata.getvalue('password', '') + password = cgidata.getfirst('password', '') is_auth = 0 realname = mlist.real_name diff --git a/Mailman/Cgi/rmlist.py b/Mailman/Cgi/rmlist.py index 3149700d..032b3ee5 100644 --- a/Mailman/Cgi/rmlist.py +++ b/Mailman/Cgi/rmlist.py @@ -42,7 +42,7 @@ def main(): 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 main(): 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 diff --git a/Mailman/Cgi/roster.py b/Mailman/Cgi/roster.py index cb6847af..739d4fff 100644 --- a/Mailman/Cgi/roster.py +++ b/Mailman/Cgi/roster.py @@ -64,7 +64,7 @@ def main(): # 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 @@ def main(): # "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, diff --git a/Mailman/Cgi/subscribe.py b/Mailman/Cgi/subscribe.py index b2f8925e..aa69e488 100755 --- a/Mailman/Cgi/subscribe.py +++ b/Mailman/Cgi/subscribe.py @@ -71,7 +71,7 @@ def main(): # 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 @@ def process_form(mlist, doc, cgidata, lang): 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 @@ def process_form(mlist, doc, cgidata, lang): # 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 @@ def process_form(mlist, doc, cgidata, lang): 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 @@ def process_form(mlist, doc, cgidata, lang): 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) diff --git a/Mailman/Gui/Privacy.py b/Mailman/Gui/Privacy.py index 2a9cca26..f6de8a32 100644 --- a/Mailman/Gui/Privacy.py +++ b/Mailman/Gui/Privacy.py @@ -641,9 +641,9 @@ class Privacy(GUIBase): 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 @@ class Privacy(GUIBase): # 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)) diff --git a/Mailman/Gui/Topics.py b/Mailman/Gui/Topics.py index ec60dbda..642059e5 100644 --- a/Mailman/Gui/Topics.py +++ b/Mailman/Gui/Topics.py @@ -114,9 +114,9 @@ class Topics(GUIBase): 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 @@ class Topics(GUIBase): # 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 @@ class Topics(GUIBase): # 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: @@ -9,6 +9,10 @@ Here is a history of user visible changes to Mailman. 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) |