aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/check_perms_grsecurity.py
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/check_perms_grsecurity.py')
-rw-r--r--contrib/check_perms_grsecurity.py180
1 files changed, 180 insertions, 0 deletions
diff --git a/contrib/check_perms_grsecurity.py b/contrib/check_perms_grsecurity.py
new file mode 100644
index 00000000..7b27a19f
--- /dev/null
+++ b/contrib/check_perms_grsecurity.py
@@ -0,0 +1,180 @@
+#! @PYTHON@
+#
+# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+"""Fixes for running Mailman under the `secure-linux' patch or grsecurity.
+
+Run check_perms -f and only then check_perms_grsecurity.py -f
+Note that you will have to re-run this script after a mailman upgrade and
+that check_perms will undo part of what this script does
+
+If you use Solar Designer's secure-linux patch, it prevents a process from
+linking (hard link) to a file it doesn't own.
+Grsecurity (http://grsecurity.net/) can have the same restriction depending
+on how it was built, including other restrictions like preventing you to run
+a program if it is located in a directory writable by a non root user.
+
+As a result Mailman has to be changed so that the whole tree is owned by
+Mailman, and the CGIs and some of the programs in the bin tree (the ones
+that lock config.pck files) are SUID Mailman. The idea is that config.pck
+files have to be owned by the mailman UID and only touched by programs that
+are UID mailman.
+At the same time, We have to make sure that at least 3 directories under
+~mailman aren't writable by mailman: mail, cgi-bin, and bin
+
+Binary commands that are changed to be SUID mailman are also made unreadable
+and unrunnable by people who aren't in the mailman group. This shouldn't
+affect much since most of those commands would fail work if you weren't part
+of the mailman group anyway.
+Scripts in ~mailman/bin/ are not made suid or sgid, they need to be run by
+user mailman or root to work.
+
+Marc <marc_soft@merlins.org>/<marc_bts@vasoftware.com>
+2000/10/27 - Initial version for secure_linux/openwall and mailman 2.0
+2001/12/09 - Updated version for grsecurity and mailman 2.1
+"""
+
+import sys
+import os
+import paths
+import re
+import glob
+import pwd
+import grp
+from Mailman import mm_cfg
+from Mailman.mm_cfg import MAILMAN_USER, MAILMAN_GROUP
+from stat import *
+
+# Directories that we don't want writable by mailman.
+dirstochownroot= ( 'mail', 'cgi-bin', 'bin' )
+
+# Those are the programs that we patch so that they insist being run under the
+# mailman uid or as root.
+binfilestopatch= ( 'add_members', 'change_pw', 'check_db', 'clone_member',
+ 'config_list', 'newlist', 'qrunner', 'remove_members',
+ 'rmlist', 'sync_members', 'update', 'withlist' )
+
+def main(argv):
+ binpath = paths.prefix + '/bin/'
+ droplib = binpath + 'CheckFixUid.py'
+
+ if len(argv) < 2 or argv[1] != "-f":
+ print __doc__
+ sys.exit(1)
+
+ print "Making select directories owned and writable by root only"
+ for dir in dirstochownroot:
+ dirpath = paths.prefix + '/' + dir
+ os.chown(dirpath, 0, MAILMAN_GID)
+ os.chmod(dirpath, 02755)
+ print dirpath
+
+ print
+
+ file = paths.prefix + '/data/last_mailman_version'
+ print "Making" + file + "owned by mailman (not root)"
+ uid = pwd.getpwnam(MAILMAN_USER)[2]
+ gid = grp.getgrnam(MAILMAN_GROUP)[2]
+ os.chown(file, uid, gid)
+ print
+
+ if not os.path.exists(droplib):
+ print "Creating " + droplib
+ fp = open(droplib, 'w', 0644)
+ fp.write("""import sys
+import os
+import grp, pwd
+from Mailman.mm_cfg import MAILMAN_USER, MAILMAN_GROUP
+
+class CheckFixUid:
+ if os.geteuid() == 0:
+ uid = pwd.getpwnam(MAILMAN_USER)[2]
+ gid = grp.getgrnam(MAILMAN_GROUP)[2]
+ os.setgid(gid)
+ os.setuid(uid)
+ if os.geteuid() != uid:
+ print "You need to run this script as root or mailman because it was configured to run"
+ print "on a linux system with a security patch which restricts hard links"
+ sys.exit()
+""")
+ fp.close()
+ else:
+ print "Skipping creation of " + droplib
+
+
+ print "\nMaking cgis setuid mailman"
+ cgis = glob.glob(paths.prefix + '/cgi-bin/*')
+
+ for file in cgis:
+ print file
+ os.chown(file, uid, gid)
+ os.chmod(file, 06755)
+
+ print "\nMaking mail wrapper setuid mailman"
+ file= paths.prefix + '/mail/mailman'
+ os.chown(file, uid, gid)
+ os.chmod(file, 06755)
+ print file
+
+ print "\nEnsuring that all config.db/pck files are owned by Mailman"
+ cdbs = glob.glob(paths.prefix + '/lists/*/config.db*')
+ cpcks = glob.glob(paths.prefix + '/lists/*/config.pck*')
+
+ for file in cdbs + cpcks:
+ stat = os.stat(file)
+ if (stat[ST_UID] != uid or stat[ST_GID] != gid):
+ print file
+ os.chown(file, uid, gid)
+
+ print "\nPatching mailman scripts to change the uid to mailman"
+
+ for script in binfilestopatch:
+ filefd = open(script, "r")
+ file = filefd.readlines()
+ filefd.close()
+
+ patched = 0
+ try:
+ file.index("import CheckFixUid\n")
+ print "Not patching " + script + ", already patched"
+ except ValueError:
+ file.insert(file.index("import paths\n")+1, "import CheckFixUid\n")
+ for i in range(len(file)-1, 0, -1):
+ object=re.compile("^([ ]*)main\(").search(file[i])
+ # Special hack to support patching of update
+ object2=re.compile("^([ ]*).*=[ ]*main\(").search(file[i])
+ if object:
+ print "Patching " + script
+ file.insert(i,
+ object.group(1) + "CheckFixUid.CheckFixUid()\n")
+ patched=1
+ break
+ if object2:
+ print "Patching " + script
+ file.insert(i,
+ object2.group(1) + "CheckFixUid.CheckFixUid()\n")
+ patched=1
+ break
+
+ if patched==0:
+ print "Warning, file "+script+" couldn't be patched."
+ print "If you use it, mailman may not function properly"
+ else:
+ filefd=open(script, "w")
+ filefd.writelines(file)
+
+main(sys.argv)