checking permissions for mailman
Todd Zullinger
tmz at pobox.com
Sun Jul 8 18:50:29 UTC 2007
Scott Berry wrote:
> Tod could you send me a copy of your check_perms please. I tried
> changing that but it still provides me with the same error.
It's attached. You can check that the only difference in the
attached file and the installed is the _( change by using diff. That
way you don't have to worry that I've slipped in something nefarious.
diff -u /usr/lib/mailman/bin/check_perms /path/to/attached/check_perms
The ouput should look like this:
--- check_perms~ 2007-01-29 09:20:43.000000000 -0500
+++ check_perms 2007-07-08 12:09:34.000000000 -0400
@@ -213,7 +213,7 @@
# 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 _("""\
+ print C_("""\
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
The minus represents lines from the original file, the plus is for
lines from the new file.
> Also what permission is check_perms supposed to be set at?
It should be 755, owner root, group mailman. You can check what it
should be using rpm like so:
$ rpm -qlv mailman | grep /usr/lib/mailman/bin/check_perms
-rwxr-xr-x 1 root mailman 12904 Jan 29 09:20 /usr/lib/mailman/bin/check_perms
If you're using SELinux you should be careful if you copy the attached
file that the security contexts get set properly. I'm sure there are
a few ways to do this. I think one of the easier ways is:
# restorecon -v /usr/lib/mailman/bin/check_perms
And, if you're using SELinux, there may be other issues you'll run
into while getting mailman and postfix integrated. I did when I tried
a quick test in FC6. I didn't take the time to resolve those issues
then because I was only doing some quick testing. The production
systems I have using mailman have not yet been upgraded to anything
new enough to have SELinux on them yet. (They will be soon though and
I'm planning to work out whatever issues I have then so that I can
keep SELinux enabled.)
--
Todd OpenPGP -> KeyID: 0xBEAF0CE3 | URL: www.pobox.com/~tmz/pgp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes I think I understand everything, then I regain
consciousness.
-------------- next part --------------
#! /usr/bin/python
#
# Copyright (C) 1998-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Check the permissions for the Mailman installation.
Usage: %(PROGRAM)s [-f] [-v] [-h]
With no arguments, just check and report all the files that have bogus
permissions or group ownership. With -f (and run as root), fix all the
permission problems found. With -v be verbose.
"""
import os
import sys
import pwd
import grp
import errno
import getopt
from stat import *
try:
import paths
except ImportError:
print '''Could not import paths!
This probably means that you are trying to run check_perms from the source
directory. You must run this from the installation directory instead.
'''
raise
from Mailman import mm_cfg
from Mailman.mm_cfg import MAILMAN_USER, MAILMAN_GROUP
from Mailman.i18n import C_
# Let KeyErrors percolate
MAILMAN_GID = grp.getgrnam(MAILMAN_GROUP)[2]
MAILMAN_UID = pwd.getpwnam(MAILMAN_USER)[2]
PROGRAM = sys.argv[0]
# Gotta check the archives/private/*/database/* files
try:
True, False
except NameError:
True = 1
False = 0
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
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 C_(' checking gid and mode for %(path)s')
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 C_('%(path)s bad group (has: %(groupname)s, '
'expected %(MAILMAN_GROUP)s)'),
if STATE.FIX:
print C_('(fixing)')
os.chown(path, -1, MAILMAN_GID)
else:
print
# all directories must be at least rwxrwsr-x. Don't check the private
# archive directory or database directory themselves since these are
# checked in checkarchives() and checkarchivedbs() below.
private = mm_cfg.PRIVATE_ARCHIVE_FILE_DIR
if path == private or (os.path.commonprefix((path, private)) == private
and os.path.split(path)[1] == 'database'):
continue
# The directories under qfiles should have a more limited permission
if os.path.commonprefix((path, mm_cfg.QUEUE_DIR)) == mm_cfg.QUEUE_DIR:
targetperms = QFILEPERMS
octperms = oct(targetperms)
else:
targetperms = DIRPERMS
octperms = oct(targetperms)
if S_ISDIR(mode) and (mode & targetperms) <> targetperms:
arg.ERRORS += 1
print C_('directory permissions must be %(octperms)s: %(path)s'),
if STATE.FIX:
print C_('(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 C_('source perms must be %(octperms)s: %(path)s'),
arg.ERRORS += 1
if STATE.FIX:
print C_('(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 C_('article db files must be %(octperms)s: %(path)s'),
arg.ERRORS += 1
if STATE.FIX:
print C_('(fixing)')
os.chmod(path, mode | ARTICLEFILEPERMS)
else:
print
def checkall():
# first check PREFIX
if STATE.VERBOSE:
prefix = mm_cfg.PREFIX
print C_('checking mode for %(prefix)s')
dirs = {}
for d in (mm_cfg.PREFIX, mm_cfg.EXEC_PREFIX, mm_cfg.VAR_PREFIX,
mm_cfg.CONFIG_DIR, mm_cfg.DATA_DIR, mm_cfg.LOCK_DIR,
mm_cfg.LOG_DIR, mm_cfg.QUEUE_DIR, mm_cfg.PID_DIR):
dirs[d] = True
for d in dirs.keys():
try:
mode = statmode(d)
except OSError, e:
if e.errno <> errno.ENOENT: raise
print C_('WARNING: directory does not exist: %(d)s')
continue
if (mode & DIRPERMS) <> DIRPERMS:
STATE.ERRORS += 1
print C_('directory must be at least 02775: %(d)s'),
if STATE.FIX:
print C_('(fixing)')
os.chmod(d, mode | DIRPERMS)
else:
print
# check all subdirs
os.path.walk(d, checkwalk, STATE)
def checkarchives():
private = mm_cfg.PRIVATE_ARCHIVE_FILE_DIR
if STATE.VERBOSE:
print C_('checking perms on %(private)s')
# private archives must not be other readable
mode = statmode(private)
if mode & S_IROTH:
STATE.ERRORS += 1
print C_('%(private)s must not be other-readable'),
if STATE.FIX:
print C_('(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 C_("""\
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.""")
MBOXPERMS = S_IRGRP | S_IWGRP | S_IRUSR | S_IWUSR
def checkmboxfile(mboxdir):
absdir = os.path.join(mm_cfg.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 C_('mbox file must be at least 0660:'), mboxfile
if STATE.FIX:
print C_('(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(mm_cfg.PRIVATE_ARCHIVE_FILE_DIR):
if dir.endswith('.mbox'):
checkmboxfile(dir)
dbdir = os.path.join(mm_cfg.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 C_('%(dbdir)s "other" perms must be 000'),
if STATE.FIX:
print C_('(fixing)')
os.chmod(dbdir, mode & ~S_IRWXO)
else:
print
def checkcgi():
cgidir = os.path.join(mm_cfg.EXEC_PREFIX, 'cgi-bin')
if STATE.VERBOSE:
print C_('checking cgi-bin permissions')
exes = os.listdir(cgidir)
for f in exes:
path = os.path.join(cgidir, f)
if STATE.VERBOSE:
print C_(' checking set-gid for %(path)s')
mode = statmode(path)
if mode & S_IXGRP and not mode & S_ISGID:
STATE.ERRORS += 1
print C_('%(path)s must be set-gid'),
if STATE.FIX:
print C_('(fixing)')
os.chmod(path, mode | S_ISGID)
else:
print
def checkmail():
wrapper = os.path.join(mm_cfg.WRAPPER_DIR, 'mailman')
if STATE.VERBOSE:
print C_('checking set-gid for %(wrapper)s')
mode = statmode(wrapper)
if not mode & S_ISGID:
STATE.ERRORS += 1
print C_('%(wrapper)s must be set-gid'),
if STATE.FIX:
print C_('(fixing)')
os.chmod(wrapper, mode | S_ISGID)
def checkadminpw():
for pwfile in (os.path.join(mm_cfg.DATA_DIR, 'adm.pw'),
os.path.join(mm_cfg.DATA_DIR, 'creator.pw')):
targetmode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP
if STATE.VERBOSE:
print C_('checking permissions on %(pwfile)s')
try:
mode = statmode(pwfile)
except OSError, e:
if e.errno <> errno.ENOENT: raise
return
if mode <> targetmode:
STATE.ERRORS += 1
octmode = oct(mode)
print C_('%(pwfile)s permissions must be exactly 0640 '
'(got %(octmode)s)'),
if STATE.FIX:
print C_('(fixing)')
os.chmod(pwfile, targetmode)
else:
print
def checkmta():
if mm_cfg.MTA:
modname = 'Mailman.MTA.' + mm_cfg.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',
'request.db', 'request.db.tmp')
if STATE.VERBOSE:
print C_('checking permissions on list data')
# BAW: This needs to be converted to the Site module abstraction
for dir in os.listdir(mm_cfg.LIST_DATA_DIR):
for file in checkfiles:
path = os.path.join(mm_cfg.LIST_DATA_DIR, dir, file)
if STATE.VERBOSE:
print C_(' checking permissions on: %(path)s')
try:
mode = statmode(path)
except OSError, e:
if e.errno <> errno.ENOENT: raise
continue
if (mode & targetmode) <> targetmode:
STATE.ERRORS += 1
print C_('file permissions must be at least 660: %(path)s'),
if STATE.FIX:
print C_('(fixing)')
os.chmod(path, mode | targetmode)
else:
print
def usage(code, msg=''):
if code:
fd = sys.stderr
else:
fd = sys.stdout
print >> fd, C_(__doc__)
if msg:
print >> fd, msg
sys.exit(code)
if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], 'fvh',
['fix', 'verbose', 'help'])
except getopt.error, msg:
usage(1, msg)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-f', '--fix'):
STATE.FIX = True
elif opt in ('-v', '--verbose'):
STATE.VERBOSE = True
checkall()
checkarchives()
checkarchivedbs()
checkcgi()
checkmail()
checkdata()
checkadminpw()
checkmta()
if not STATE.ERRORS:
print C_('No problems found')
else:
print C_('Problems found:'), STATE.ERRORS
print C_('Re-run as %(MAILMAN_USER)s (or root) with -f flag to fix')
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 542 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/fedora-list/attachments/20070708/f9ee3e13/attachment-0001.sig>
More information about the fedora-list
mailing list