8cf1bd
diff --git a/Mailman/Cgi/admindb.py b/Mailman/Cgi/admindb.py
8cf1bd
index d187332..defc58d 100644
8cf1bd
--- a/Mailman/Cgi/admindb.py
8cf1bd
+++ b/Mailman/Cgi/admindb.py
8cf1bd
@@ -39,6 +39,7 @@ from Mailman.ListAdmin import readMessage
8cf1bd
 from Mailman.Cgi import Auth
8cf1bd
 from Mailman.htmlformat import *
8cf1bd
 from Mailman.Logging.Syslog import syslog
8cf1bd
+from Mailman.CSRFcheck import csrf_check
8cf1bd
 
8cf1bd
 EMPTYSTRING = ''
8cf1bd
 NL = '\n'
8cf1bd
@@ -51,6 +52,9 @@ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
8cf1bd
 EXCERPT_HEIGHT = 10
8cf1bd
 EXCERPT_WIDTH = 76
8cf1bd
 
8cf1bd
+AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin,
8cf1bd
+                 mm_cfg.AuthListModerator)
8cf1bd
+
8cf1bd
 
8cf1bd
 
8cf1bd
 def helds_by_sender(mlist):
8cf1bd
@@ -100,6 +104,18 @@ def main():
8cf1bd
     # Make sure the user is authorized to see this page.
8cf1bd
     cgidata = cgi.FieldStorage(keep_blank_values=1)
8cf1bd
 
8cf1bd
+    # CSRF check
8cf1bd
+    safe_params = ['adminpw', 'admlogin', 'msgid', 'sender', 'details']
8cf1bd
+    params = cgidata.keys()
8cf1bd
+    if set(params) - set(safe_params):
8cf1bd
+        csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token'))
8cf1bd
+    else:
8cf1bd
+        csrf_checked = True
8cf1bd
+    # if password is present, void cookie to force password authentication.
8cf1bd
+    if cgidata.getvalue('adminpw'):
8cf1bd
+        os.environ['HTTP_COOKIE'] = ''
8cf1bd
+        csrf_checked = True
8cf1bd
+
8cf1bd
     if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin,
8cf1bd
                                   mm_cfg.AuthListModerator,
8cf1bd
                                   mm_cfg.AuthSiteAdmin),
8cf1bd
@@ -177,7 +193,11 @@ def main():
8cf1bd
         elif not details:
8cf1bd
             # This is a form submission
8cf1bd
             doc.SetTitle(_('%(realname)s Administrative Database Results'))
8cf1bd
-            process_form(mlist, doc, cgidata)
8cf1bd
+            if csrf_checked:
8cf1bd
+                process_form(mlist, doc, cgidata)
8cf1bd
+            else:
8cf1bd
+                doc.addError(
8cf1bd
+                    _('The form lifetime has expired. (request forgery check)'))
8cf1bd
         # Now print the results and we're done.  Short circuit for when there
8cf1bd
         # are no pending requests, but be sure to save the results!
8cf1bd
         admindburl = mlist.GetScriptURL('admindb', absolute=1)
8cf1bd
@@ -198,7 +218,7 @@ def main():
8cf1bd
             mlist.Save()
8cf1bd
             return
8cf1bd
 
8cf1bd
-        form = Form(admindburl)
8cf1bd
+        form = Form(admindburl, mlist=mlist, contexts=AUTH_CONTEXTS)
8cf1bd
         # Add the instructions template
8cf1bd
         if details == 'instructions':
8cf1bd
             doc.AddItem(Header(
8cf1bd
diff --git a/Mailman/Cgi/edithtml.py b/Mailman/Cgi/edithtml.py
8cf1bd
index ee1ccd0..5c9416e 100644
8cf1bd
--- a/Mailman/Cgi/edithtml.py
8cf1bd
+++ b/Mailman/Cgi/edithtml.py
8cf1bd
@@ -30,9 +30,12 @@ from Mailman import Errors
8cf1bd
 from Mailman.Cgi import Auth
8cf1bd
 from Mailman.Logging.Syslog import syslog
8cf1bd
 from Mailman import i18n
8cf1bd
+from Mailman.CSRFcheck import csrf_check
8cf1bd
 
8cf1bd
 _ = i18n._
8cf1bd
 
8cf1bd
+AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin)
8cf1bd
+
8cf1bd
 
8cf1bd
 
8cf1bd
 def main():
8cf1bd
@@ -82,6 +85,18 @@ def main():
8cf1bd
     # Must be authenticated to get any farther
8cf1bd
     cgidata = cgi.FieldStorage()
8cf1bd
 
8cf1bd
+    # CSRF check
8cf1bd
+    safe_params = ['VARHELP', 'adminpw', 'admlogin']
8cf1bd
+    params = cgidata.keys()
8cf1bd
+    if set(params) - set(safe_params):
8cf1bd
+        csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token'))
8cf1bd
+    else:
8cf1bd
+        csrf_checked = True
8cf1bd
+    # if password is present, void cookie to force password authentication.
8cf1bd
+    if cgidata.getvalue('adminpw'):
8cf1bd
+        os.environ['HTTP_COOKIE'] = ''
8cf1bd
+        csrf_checked = True
8cf1bd
+
8cf1bd
     # Editing the html for a list is limited to the list admin and site admin.
8cf1bd
     if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin,
8cf1bd
                                   mm_cfg.AuthSiteAdmin),
8cf1bd
@@ -126,7 +141,11 @@ def main():
8cf1bd
 
8cf1bd
     try:
8cf1bd
         if cgidata.keys():
8cf1bd
-            ChangeHTML(mlist, cgidata, template_name, doc)
8cf1bd
+            if csrf_checked:
8cf1bd
+                ChangeHTML(mlist, cgidata, template_name, doc)
8cf1bd
+            else:
8cf1bd
+                doc.addError(
8cf1bd
+                  _('The form lifetime has expired. (request forgery check)'))
8cf1bd
         FormatHTML(mlist, doc, template_name, template_info)
8cf1bd
     finally:
8cf1bd
         doc.AddItem(mlist.GetMailmanFooter())
8cf1bd
@@ -145,7 +164,8 @@ def FormatHTML(mlist, doc, template_name, template_info):
8cf1bd
     doc.AddItem(FontSize("+1", link))
8cf1bd
     doc.AddItem('

')

8cf1bd
     doc.AddItem('
')
8cf1bd
-    form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name)
8cf1bd
+    form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name,
8cf1bd
+               mlist=mlist, contexts=AUTH_CONTEXTS)
8cf1bd
     text = Utils.maketext(template_name, raw=1, mlist=mlist)
8cf1bd
     # MAS: Don't websafe twice.  TextArea does it.
8cf1bd
     form.AddItem(TextArea('html_code', text, rows=40, cols=75))
8cf1bd
diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py
8cf1bd
index ae701a7..328d9d5 100644
8cf1bd
--- a/Mailman/Cgi/options.py
8cf1bd
+++ b/Mailman/Cgi/options.py
8cf1bd
@@ -33,6 +33,7 @@ from Mailman import MemberAdaptor
8cf1bd
 from Mailman import i18n
8cf1bd
 from Mailman.htmlformat import *
8cf1bd
 from Mailman.Logging.Syslog import syslog
8cf1bd
+from Mailman.CSRFcheck import csrf_check
8cf1bd
 
8cf1bd
 SLASH = '/'
8cf1bd
 SETLANGUAGE = -1
8cf1bd
@@ -47,6 +48,8 @@ except NameError:
8cf1bd
     True = 1
8cf1bd
     False = 0
8cf1bd
 
8cf1bd
+AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin,
8cf1bd
+                 mm_cfg.AuthListModerator, mm_cfg.AuthUser)
8cf1bd
 
8cf1bd
 
8cf1bd
 def main():
8cf1bd
@@ -88,6 +91,19 @@ def main():
8cf1bd
     # The total contents of the user's response
8cf1bd
     cgidata = cgi.FieldStorage(keep_blank_values=1)
8cf1bd
 
8cf1bd
+    # CSRF check
8cf1bd
+    safe_params = ['displang-button', 'language', 'email', 'password', 'login',
8cf1bd
+                   'login-unsub', 'login-remind', 'VARHELP', 'UserOptions']
8cf1bd
+    params = cgidata.keys()
8cf1bd
+    if set(params) - set(safe_params):
8cf1bd
+        csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token'))
8cf1bd
+    else:
8cf1bd
+        csrf_checked = True
8cf1bd
+    # if password is present, void cookie to force password authentication.
8cf1bd
+    if cgidata.getvalue('password'):
8cf1bd
+        os.environ['HTTP_COOKIE'] = ''
8cf1bd
+        csrf_checked = True
8cf1bd
+
8cf1bd
     # Set the language for the page.  If we're coming from the listinfo cgi,
8cf1bd
     # we might have a 'language' key in the cgi data.  That was an explicit
8cf1bd
     # preference to view the page in, so we should honor that here.  If that's
8cf1bd
@@ -269,6 +285,15 @@ def main():
8cf1bd
     # options.  The first set of checks does not require the list to be
8cf1bd
     # locked.
8cf1bd
 
8cf1bd
+    # Before going further, get the result of CSRF check and do nothing 
8cf1bd
+    # if it has failed.
8cf1bd
+    if csrf_checked == False:
8cf1bd
+        doc.addError(
8cf1bd
+            _('The form lifetime has expired. (request forgery check)'))
8cf1bd
+        options_page(mlist, doc, user, cpuser, userlang)
8cf1bd
+        print doc.Format()
8cf1bd
+        return
8cf1bd
+
8cf1bd
     if cgidata.has_key('logout'):
8cf1bd
         print mlist.ZapCookie(mm_cfg.AuthUser, user)
8cf1bd
         loginpage(mlist, doc, user, language)
8cf1bd
@@ -779,7 +804,8 @@ def options_page(mlist, doc, user, cpuser, userlang, message=''):
8cf1bd
         mlist.FormatButton('othersubs',
8cf1bd
                            _('List my other subscriptions')))
8cf1bd
     replacements['<mm-form-start>'] = (
8cf1bd
-        mlist.FormatFormStart('options', user))
8cf1bd
+        mlist.FormatFormStart('options', user, mlist=mlist, 
8cf1bd
+            contexts=AUTH_CONTEXTS, user=user))
8cf1bd
     replacements['<mm-user>'] = user
8cf1bd
     replacements['<mm-presentable-user>'] = presentable_user
8cf1bd
     replacements['<mm-email-my-pw>'] = mlist.FormatButton(
8cf1bd
diff --git a/Mailman/HTMLFormatter.py b/Mailman/HTMLFormatter.py
8cf1bd
index dad51e7..3afe4bb 100644
8cf1bd
--- a/Mailman/HTMLFormatter.py
8cf1bd
+++ b/Mailman/HTMLFormatter.py
8cf1bd
@@ -28,6 +28,8 @@ from Mailman.htmlformat import *
8cf1bd
 
8cf1bd
 from Mailman.i18n import _
8cf1bd
 
8cf1bd
+from Mailman.CSRFcheck import csrf_token
8cf1bd
+
8cf1bd
 
8cf1bd
 EMPTYSTRING = ''
8cf1bd
 BR = '
'
8cf1bd
@@ -314,12 +316,17 @@ class HTMLFormatter:
8cf1bd
             container.AddItem("</center>")
8cf1bd
         return container
8cf1bd
 
8cf1bd
-    def FormatFormStart(self, name, extra=''):
8cf1bd
+    def FormatFormStart(self, name, extra='',
8cf1bd
+                        mlist=None, contexts=None, user=None):
8cf1bd
         base_url = self.GetScriptURL(name)
8cf1bd
         if extra:
8cf1bd
             full_url = "%s/%s" % (base_url, extra)
8cf1bd
         else:
8cf1bd
             full_url = base_url
8cf1bd
+        if mlist:
8cf1bd
+            return ("""<form method="POST" action="%s">
8cf1bd
+<input type="hidden" name="csrf_token" value="%s">""" 
8cf1bd
+                % (full_url, csrf_token(mlist, contexts, user)))
8cf1bd
         return ('<FORM Method=POST ACTION="%s">' % full_url)
8cf1bd
 
8cf1bd
     def FormatArchiveAnchor(self):
8cf1bd
diff --git a/Mailman/htmlformat.py b/Mailman/htmlformat.py
8cf1bd
index 2387096..e2ad2e4 100644
8cf1bd
--- a/Mailman/htmlformat.py
8cf1bd
+++ b/Mailman/htmlformat.py
8cf1bd
@@ -405,13 +405,14 @@ class Center(StdContainer):
8cf1bd
 
8cf1bd
 class Form(Container):
8cf1bd
     def __init__(self, action='', method='POST', encoding=None, 
8cf1bd
-                       mlist=None, contexts=None, *items):
8cf1bd
+                       mlist=None, contexts=None, user=None, *items):
8cf1bd
         apply(Container.__init__, (self,) +  items)
8cf1bd
         self.action = action
8cf1bd
         self.method = method
8cf1bd
         self.encoding = encoding
8cf1bd
         self.mlist = mlist
8cf1bd
         self.contexts = contexts
8cf1bd
+        self.user = user
8cf1bd
 
8cf1bd
     def set_action(self, action):
8cf1bd
         self.action = action
8cf1bd
@@ -426,7 +427,7 @@ class Form(Container):
8cf1bd
         if self.mlist:
8cf1bd
             output = output + \
8cf1bd
                 '<input type="hidden" name="csrf_token" value="%s">\n' \
8cf1bd
-                % csrf_token(self.mlist, self.contexts)
8cf1bd
+                % csrf_token(self.mlist, self.contexts, self.user)
8cf1bd
         output = output + Container.Format(self, indent+2)
8cf1bd
         output = '%s\n%s</FORM>\n' % (output, spaces)
8cf1bd
         return output