summaryrefslogtreecommitdiff
path: root/src/mailman/bin/check_perms.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/bin/check_perms.py')
-rw-r--r--src/mailman/bin/check_perms.py408
1 files changed, 0 insertions, 408 deletions
diff --git a/src/mailman/bin/check_perms.py b/src/mailman/bin/check_perms.py
deleted file mode 100644
index 6e0d761d9..000000000
--- a/src/mailman/bin/check_perms.py
+++ /dev/null
@@ -1,408 +0,0 @@
-# Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-import sys
-import pwd
-import grp
-import errno
-import optparse
-
-from stat import *
-
-from mailman.configuration import config
-from mailman.core.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-# XXX Need to check the archives/private/*/database/* files
-
-
-
-class State:
- FIX = False
- VERBOSE = False
- ERRORS = 0
-
-STATE = State()
-
-DIRPERMS = S_ISGID | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH
-QFILEPERMS = S_ISGID | S_IRWXU | S_IRWXG
-PYFILEPERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
-ARTICLEFILEPERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
-MBOXPERMS = S_IRGRP | S_IWGRP | S_IRUSR | S_IWUSR
-PRIVATEPERMS = QFILEPERMS
-
-
-
-def statmode(path):
- return os.stat(path).st_mode
-
-
-def statgidmode(path):
- stat = os.stat(path)
- return stat.st_mode, stat.st_gid
-
-
-seen = {}
-
-# libc's getgrgid re-opens /etc/group each time :(
-_gidcache = {}
-
-def getgrgid(gid):
- data = _gidcache.get(gid)
- if data is None:
- data = grp.getgrgid(gid)
- _gidcache[gid] = data
- return data
-
-
-
-def checkwalk(arg, dirname, names):
- # Short-circuit duplicates
- if seen.has_key(dirname):
- return
- seen[dirname] = True
- for name in names:
- path = os.path.join(dirname, name)
- if arg.VERBOSE:
- print _(' checking gid and mode for $path')
- try:
- mode, gid = statgidmode(path)
- except OSError, e:
- if e.errno != errno.ENOENT: raise
- continue
- if gid != MAILMAN_GID:
- try:
- groupname = getgrgid(gid)[0]
- except KeyError:
- groupname = '<anon gid %d>' % gid
- arg.ERRORS += 1
- print _(
- '$path bad group (has: $groupname, expected $MAILMAN_GROUP)'),
- if STATE.FIX:
- print _('(fixing)')
- os.chown(path, -1, MAILMAN_GID)
- else:
- print
- # Most directories must be at least rwxrwsr-x.
- # The private archive directory and database directory must be at
- # least rwxrws---. Their 'other' permissions are checked in
- # checkarchives() and checkarchivedbs() below. Their 'user' and
- # 'group' permissions are checked here.
- # The directories under qfiles should be rwxrws---. Their 'user' and
- # 'group' permissions are checked here. Their 'other' permissions
- # aren't checked.
- private = config.PRIVATE_ARCHIVE_FILE_DIR
- if path == private or (
- os.path.commonprefix((path, private)) == private
- and os.path.split(path)[1] == 'database'):
- # then...
- targetperms = PRIVATEPERMS
- elif (os.path.commonprefix((path, config.QUEUE_DIR))
- == config.QUEUE_DIR):
- targetperms = QFILEPERMS
- else:
- targetperms = DIRPERMS
- octperms = oct(targetperms)
- if S_ISDIR(mode) and (mode & targetperms) != targetperms:
- arg.ERRORS += 1
- print _('directory permissions must be $octperms: $path'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | targetperms)
- else:
- print
- elif os.path.splitext(path)[1] in ('.py', '.pyc', '.pyo'):
- octperms = oct(PYFILEPERMS)
- if mode & PYFILEPERMS != PYFILEPERMS:
- print _('source perms must be $octperms: $path'),
- arg.ERRORS += 1
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | PYFILEPERMS)
- else:
- print
- elif path.endswith('-article'):
- # Article files must be group writeable
- octperms = oct(ARTICLEFILEPERMS)
- if mode & ARTICLEFILEPERMS != ARTICLEFILEPERMS:
- print _('article db files must be $octperms: $path'),
- arg.ERRORS += 1
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | ARTICLEFILEPERMS)
- else:
- print
-
-
-
-def checkall():
- # first check PREFIX
- if STATE.VERBOSE:
- prefix = config.PREFIX
- print _('checking mode for $prefix')
- dirs = {}
- for d in (config.PREFIX, config.EXEC_PREFIX, config.VAR_PREFIX,
- config.LOG_DIR):
- dirs[d] = True
- for d in dirs.keys():
- try:
- mode = statmode(d)
- except OSError, e:
- if e.errno != errno.ENOENT: raise
- print _('WARNING: directory does not exist: $d')
- continue
- if (mode & DIRPERMS) != DIRPERMS:
- STATE.ERRORS += 1
- print _('directory must be at least 02775: $d'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(d, mode | DIRPERMS)
- else:
- print
- # check all subdirs
- os.path.walk(d, checkwalk, STATE)
-
-
-
-def checkarchives():
- private = config.PRIVATE_ARCHIVE_FILE_DIR
- if STATE.VERBOSE:
- print _('checking perms on $private')
- # private archives must not be other readable
- mode = statmode(private)
- if mode & S_IROTH:
- STATE.ERRORS += 1
- print _('$private must not be other-readable'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(private, mode & ~S_IROTH)
- else:
- print
- # In addition, on a multiuser system you may want to hide the private
- # archives so other users can't read them.
- if mode & S_IXOTH:
- print _("""\
-Warning: Private archive directory is other-executable (o+x).
- This could allow other users on your system to read private archives.
- If you're on a shared multiuser system, you should consult the
- installation manual on how to fix this.""")
-
-
-
-def checkmboxfile(mboxdir):
- absdir = os.path.join(config.PRIVATE_ARCHIVE_FILE_DIR, mboxdir)
- for f in os.listdir(absdir):
- if not f.endswith('.mbox'):
- continue
- mboxfile = os.path.join(absdir, f)
- mode = statmode(mboxfile)
- if (mode & MBOXPERMS) != MBOXPERMS:
- STATE.ERRORS = STATE.ERRORS + 1
- print _('mbox file must be at least 0660:'), mboxfile
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(mboxfile, mode | MBOXPERMS)
- else:
- print
-
-
-
-def checkarchivedbs():
- # The archives/private/listname/database file must not be other readable
- # or executable otherwise those files will be accessible when the archives
- # are public. That may not be a horrible breach, but let's close this off
- # anyway.
- for dir in os.listdir(config.PRIVATE_ARCHIVE_FILE_DIR):
- if dir.endswith('.mbox'):
- checkmboxfile(dir)
- dbdir = os.path.join(config.PRIVATE_ARCHIVE_FILE_DIR, dir, 'database')
- try:
- mode = statmode(dbdir)
- except OSError, e:
- if e.errno not in (errno.ENOENT, errno.ENOTDIR): raise
- continue
- if mode & S_IRWXO:
- STATE.ERRORS += 1
- print _('$dbdir "other" perms must be 000'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(dbdir, mode & ~S_IRWXO)
- else:
- print
-
-
-
-def checkcgi():
- cgidir = os.path.join(config.EXEC_PREFIX, 'cgi-bin')
- if STATE.VERBOSE:
- print _('checking cgi-bin permissions')
- exes = os.listdir(cgidir)
- for f in exes:
- path = os.path.join(cgidir, f)
- if STATE.VERBOSE:
- print _(' checking set-gid for $path')
- mode = statmode(path)
- if mode & S_IXGRP and not mode & S_ISGID:
- STATE.ERRORS += 1
- print _('$path must be set-gid'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | S_ISGID)
- else:
- print
-
-
-
-def checkmail():
- wrapper = os.path.join(config.WRAPPER_DIR, 'mailman')
- if STATE.VERBOSE:
- print _('checking set-gid for $wrapper')
- mode = statmode(wrapper)
- if not mode & S_ISGID:
- STATE.ERRORS += 1
- print _('$wrapper must be set-gid'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(wrapper, mode | S_ISGID)
-
-
-
-def checkadminpw():
- for pwfile in (os.path.join(config.DATA_DIR, 'adm.pw'),
- os.path.join(config.DATA_DIR, 'creator.pw')):
- targetmode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP
- if STATE.VERBOSE:
- print _('checking permissions on $pwfile')
- try:
- mode = statmode(pwfile)
- except OSError, e:
- if e.errno != errno.ENOENT:
- raise
- return
- if mode != targetmode:
- STATE.ERRORS += 1
- octmode = oct(mode)
- print _('$pwfile permissions must be exactly 0640 (got $octmode)'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(pwfile, targetmode)
- else:
- print
-
-
-def checkmta():
- if config.MTA:
- modname = 'mailman.MTA.' + config.MTA
- __import__(modname)
- try:
- sys.modules[modname].checkperms(STATE)
- except AttributeError:
- pass
-
-
-
-def checkdata():
- targetmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
- checkfiles = ('config.pck', 'config.pck.last',
- 'config.db', 'config.db.last',
- 'next-digest', 'next-digest-topics',
- 'digest.mbox', 'pending.pck',
- 'request.db', 'request.db.tmp')
- if STATE.VERBOSE:
- print _('checking permissions on list data')
- for dir in os.listdir(config.LIST_DATA_DIR):
- for file in checkfiles:
- path = os.path.join(config.LIST_DATA_DIR, dir, file)
- if STATE.VERBOSE:
- print _(' checking permissions on: $path')
- try:
- mode = statmode(path)
- except OSError, e:
- if e.errno != errno.ENOENT:
- raise
- continue
- if (mode & targetmode) != targetmode:
- STATE.ERRORS += 1
- print _('file permissions must be at least 660: $path'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | targetmode)
- else:
- print
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Check the permissions of all Mailman files. With no options, just report the
-permission and ownership problems found."""))
- parser.add_option('-f', '--fix',
- default=False, action='store_true', help=_("""\
-Fix all permission and ownership problems found. With this option, you must
-run check_perms as root."""))
- parser.add_option('-v', '--verbose',
- default=False, action='store_true',
- help=_('Produce more verbose output'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- print >> sys.stderr, _('Unexpected arguments')
- sys.exit(1)
- return parser, opts, args
-
-
-
-def main():
- global MAILMAN_USER, MAILMAN_GROUP, MAILMAN_UID, MAILMAN_GID
-
- parser, opts, args = parseargs()
- STATE.FIX = opts.fix
- STATE.VERBOSE = opts.verbose
-
- config.load(opts.config)
-
- MAILMAN_USER = config.MAILMAN_USER
- MAILMAN_GROUP = config.MAILMAN_GROUP
- # Let KeyErrors percolate
- MAILMAN_GID = grp.getgrnam(MAILMAN_GROUP).gr_gid
- MAILMAN_UID = pwd.getpwnam(MAILMAN_USER).pw_uid
-
- checkall()
- checkarchives()
- checkarchivedbs()
- checkcgi()
- checkmail()
- checkdata()
- checkadminpw()
- checkmta()
-
- if not STATE.ERRORS:
- print _('No problems found')
- else:
- print _('Problems found:'), STATE.ERRORS
- print _('Re-run as $MAILMAN_USER (or root) with -f flag to fix')
-
-
-if __name__ == '__main__':
- main()