diff options
author | Mark Sapiro <mark@msapiro.net> | 2016-08-26 20:47:49 -0700 |
---|---|---|
committer | Mark Sapiro <mark@msapiro.net> | 2016-08-26 20:47:49 -0700 |
commit | 97991cac52b604276f8751828d291c1b2002d9f7 (patch) | |
tree | c6d8e4c94644797b7ac1839a6e9c11a101358606 /Mailman/Cgi | |
parent | 62d0aee0c31a90669bb053c5abeb9228c5a35996 (diff) | |
download | mailman2-97991cac52b604276f8751828d291c1b2002d9f7.tar.gz mailman2-97991cac52b604276f8751828d291c1b2002d9f7.tar.xz mailman2-97991cac52b604276f8751828d291c1b2002d9f7.zip |
Fixes for CVE-2016-6893 and more.
Diffstat (limited to 'Mailman/Cgi')
-rw-r--r-- | Mailman/Cgi/admindb.py | 24 | ||||
-rw-r--r-- | Mailman/Cgi/edithtml.py | 24 | ||||
-rw-r--r-- | Mailman/Cgi/options.py | 28 |
3 files changed, 71 insertions, 5 deletions
diff --git a/Mailman/Cgi/admindb.py b/Mailman/Cgi/admindb.py index 1e9fad0f..3c9f4002 100644 --- a/Mailman/Cgi/admindb.py +++ b/Mailman/Cgi/admindb.py @@ -39,6 +39,7 @@ from Mailman.ListAdmin import readMessage from Mailman.Cgi import Auth from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog +from Mailman.CSRFcheck import csrf_check EMPTYSTRING = '' NL = '\n' @@ -58,6 +59,9 @@ if mm_cfg.DISPLAY_HELD_SUMMARY_SORT_BUTTONS in (SSENDERTIME, STIME): else: ssort = SSENDER +AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin, + mm_cfg.AuthListModerator) + def helds_by_skey(mlist, ssort=SSENDER): @@ -135,6 +139,18 @@ def main(): print doc.Format() return + # CSRF check + 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')) + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. + if cgidata.getvalue('adminpw'): + os.environ['HTTP_COOKIE'] = '' + csrf_checked = True + if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthListModerator, mm_cfg.AuthSiteAdmin), @@ -212,7 +228,11 @@ def main(): elif not details: # This is a form submission doc.SetTitle(_('%(realname)s Administrative Database Results')) - process_form(mlist, doc, cgidata) + if csrf_checked: + process_form(mlist, doc, cgidata) + else: + doc.addError( + _('The form lifetime has expired. (request forgery check)')) # Now print the results and we're done. Short circuit for when there # are no pending requests, but be sure to save the results! admindburl = mlist.GetScriptURL('admindb', absolute=1) @@ -234,7 +254,7 @@ def main(): mlist.Save() return - form = Form(admindburl) + form = Form(admindburl, mlist=mlist, contexts=AUTH_CONTEXTS) # Add the instructions template if details == 'instructions': doc.AddItem(Header( diff --git a/Mailman/Cgi/edithtml.py b/Mailman/Cgi/edithtml.py index 6eb65d6a..0628f30b 100644 --- a/Mailman/Cgi/edithtml.py +++ b/Mailman/Cgi/edithtml.py @@ -30,9 +30,12 @@ from Mailman import Errors from Mailman.Cgi import Auth from Mailman.Logging.Syslog import syslog from Mailman import i18n +from Mailman.CSRFcheck import csrf_check _ = i18n._ +AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin) + def main(): @@ -104,6 +107,18 @@ def main(): print doc.Format() return + # CSRF check + safe_params = ['VARHELP', 'adminpw', 'admlogin'] + params = cgidata.keys() + if set(params) - set(safe_params): + csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. + if cgidata.getvalue('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), @@ -148,7 +163,11 @@ def main(): try: if cgidata.keys(): - ChangeHTML(mlist, cgidata, template_name, doc) + if csrf_checked: + ChangeHTML(mlist, cgidata, template_name, doc) + else: + doc.addError( + _('The form lifetime has expired. (request forgery check)')) FormatHTML(mlist, doc, template_name, template_info) finally: doc.AddItem(mlist.GetMailmanFooter()) @@ -167,7 +186,8 @@ def FormatHTML(mlist, doc, template_name, template_info): doc.AddItem(FontSize("+1", link)) doc.AddItem('<p>') doc.AddItem('<hr>') - form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name) + form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name, + mlist=mlist, contexts=AUTH_CONTEXTS) text = Utils.maketext(template_name, raw=1, mlist=mlist) # MAS: Don't websafe twice. TextArea does it. form.AddItem(TextArea('html_code', text, rows=40, cols=75)) diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py index 38b34fd1..faf732da 100644 --- a/Mailman/Cgi/options.py +++ b/Mailman/Cgi/options.py @@ -33,6 +33,7 @@ from Mailman import MemberAdaptor from Mailman import i18n from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog +from Mailman.CSRFcheck import csrf_check OR = '|' SLASH = '/' @@ -51,6 +52,8 @@ except NameError: True = 1 False = 0 +AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin, + mm_cfg.AuthListModerator, mm_cfg.AuthUser) def main(): @@ -104,6 +107,19 @@ def main(): # The total contents of the user's response cgidata = cgi.FieldStorage(keep_blank_values=1) + # CSRF check + safe_params = ['displang-button', 'language', 'email', 'password', 'login', + 'login-unsub', 'login-remind', 'VARHELP', 'UserOptions'] + params = cgidata.keys() + if set(params) - set(safe_params): + csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. + if cgidata.getvalue('password'): + os.environ['HTTP_COOKIE'] = '' + csrf_checked = True + # Set the language for the page. If we're coming from the listinfo cgi, # 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 @@ -315,6 +331,15 @@ def main(): print doc.Format() return + # Before going further, get the result of CSRF check and do nothing + # if it has failed. + if csrf_checked == False: + doc.addError( + _('The form lifetime has expired. (request forgery check)')) + options_page(mlist, doc, user, cpuser, userlang) + print doc.Format() + return + if cgidata.has_key('logout'): print mlist.ZapCookie(mm_cfg.AuthUser, user) loginpage(mlist, doc, user, language) @@ -832,7 +857,8 @@ def options_page(mlist, doc, user, cpuser, userlang, message=''): mlist.FormatButton('othersubs', _('List my other subscriptions'))) replacements['<mm-form-start>'] = ( - mlist.FormatFormStart('options', user)) + mlist.FormatFormStart('options', user, mlist=mlist, + contexts=AUTH_CONTEXTS, user=user)) replacements['<mm-user>'] = user replacements['<mm-presentable-user>'] = presentable_user replacements['<mm-email-my-pw>'] = mlist.FormatButton( |