aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Sapiro <mark@msapiro.net>2017-06-05 20:48:34 -0700
committerMark Sapiro <mark@msapiro.net>2017-06-05 20:48:34 -0700
commit0d11dc90ee6fc9cc61d32ca3ea6819ca95ac1c12 (patch)
treef7743c3b5fc245e214bc94da3266bd16f9d664e2
parent845dc52970be426af2a766be4609a8bef2bd1c05 (diff)
downloadmailman2-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.py48
-rw-r--r--Mailman/Cgi/admindb.py16
-rw-r--r--Mailman/Cgi/confirm.py28
-rw-r--r--Mailman/Cgi/create.py34
-rw-r--r--Mailman/Cgi/edithtml.py8
-rw-r--r--Mailman/Cgi/listinfo.py2
-rw-r--r--Mailman/Cgi/options.py44
-rwxr-xr-xMailman/Cgi/private.py4
-rw-r--r--Mailman/Cgi/rmlist.py6
-rw-r--r--Mailman/Cgi/roster.py6
-rwxr-xr-xMailman/Cgi/subscribe.py14
-rw-r--r--Mailman/Gui/Privacy.py6
-rw-r--r--Mailman/Gui/Topics.py12
-rw-r--r--NEWS4
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:
diff --git a/NEWS b/NEWS
index 8ac616bb..9b6d4753 100644
--- a/NEWS
+++ b/NEWS
@@ -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)