Somehow, my /home/mailman/lists directory managed to get set with permissions check_perms didn't check for; it was set to d-wx-ws--x/mailman/mailman, which caused mailcmd to be unable to list the directory (no read perms).
I fixed it once I figured it out, but I had a lot of check_perms runs in there before I finally figured it out...mostly because I was confused about what 'x' really means on a directory. But check_perms could have saved me time if it had checked for 'owner-and-group-read'; I think it should.
"DM" == Dan Mick <Dan.Mick@West.Sun.COM> writes:
DM> Somehow, my /home/mailman/lists directory managed to get set
DM> with permissions check_perms didn't check for; it was set to
DM> d-wx-ws--x/mailman/mailman, which caused mailcmd to be unable
DM> to list the directory (no read perms).
DM> I fixed it once I figured it out, but I had a lot of
DM> check_perms runs in there before I finally figured it
DM> out...mostly because I was confused about what 'x' really
DM> means on a directory. But check_perms could have saved me
DM> time if it had checked for 'owner-and-group-read'; I think it
DM> should.
You're right. Here's the check_perms that will be in 2.0beta2.
-Barry
-------------------- snip snip -------------------- #! /usr/bin/env python # # Copyright (C) 1998,1999,2000 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.
"""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 sys import os import errno import getopt import grp from stat import * import paths from Mailman import mm_cfg
MAILMAN_GRPNAME = 'mailman' MAILMAN_GID = grp.getgrnam(MAILMAN_GRPNAME)[2]
PROGRAM = sys.argv[0]
class State: FIX = 0 VERBOSE = 0 ERRORS = 0
STATE = State()
DIRPERMS = S_ISGID | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH
def statmode(path): return os.stat(path)[ST_MODE]
def statgidmode(path): stat = os.stat(path) return stat[ST_MODE], stat[ST_GID]
def checkwalk(arg, dirname, names): 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 os.error, (code, msg): if code == errno.ENOENT: continue raise if gid <> MAILMAN_GID: try: groupname = grp.getgrgid(gid)[0] except KeyError: groupname = '<anon gid %d>' % gid arg.ERRORS = arg.ERRORS + 1 print path, 'bad gid (has: %s, expected %s)' % ( groupname, MAILMAN_GRPNAME), if STATE.FIX: print '(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 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 if S_ISDIR(mode) and (mode & DIRPERMS) <> DIRPERMS: arg.ERRORS = arg.ERRORS + 1 print 'directory must be at least 02775:', path, if STATE.FIX: print '(fixing)' os.chmod(path, mode | DIRPERMS) else: print
def checkall(): # first check PREFIX if STATE.VERBOSE: print 'checking mode for', mm_cfg.PREFIX, mode = statmode(mm_cfg.PREFIX) if (mode & DIRPERMS) <> DIRPERMS: STATE.ERRORS = STATE.ERRORS + 1 print 'directory must be at least 02775:', mm_cfg.PREFIX, if STATE.FIX: print '(fixing)' os.chmod(mm_cfg.PREFIX, mode | DIRPERMS) else: print # check all subdirs os.path.walk(mm_cfg.PREFIX, checkwalk, STATE)
def checkarchives(): private = mm_cfg.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 = STATE.ERRORS + 1 print private, 'must not be other-readable', if STATE.FIX: print '(fixing)' os.chmod(private, mode & ~S_IROTH) 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[-5:] == '.mbox': continue dbdir = os.path.join(mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, dir, 'database') try: mode = statmode(dbdir) except os.error, (code, msg): if code == errno.ENOENT: continue raise if mode & S_IRWXO: STATE.ERRORS = STATE.ERRORS + 1 print dbdir, 'must be other 000', if STATE.FIX: print '(fixing)' os.chmod(dbdir, mode & ~S_IRWXO) else: print
def checkcgi(): exes = os.listdir(mm_cfg.CGI_DIR) for f in exes: path = os.path.join(mm_cfg.CGI_DIR, f) if STATE.VERBOSE: print 'checking set-gid for', path mode = statmode(path) if mode & S_IXGRP and not mode & S_ISGID: STATE.ERRORS = 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(mm_cfg.WRAPPER_DIR, 'wrapper') if STATE.VERBOSE: print 'checking set-gid for', wrapper mode = statmode(wrapper) if not mode & S_ISGID: STATE.ERRORS = STATE.ERRORS + 1 print wrapper, 'must be set-gid', if STATE.FIX: print '(fixing)' os.chmod(wrapper, mode | S_ISGID)
def checkadminpw(): adminpw = os.path.join(mm_cfg.DATA_DIR, 'adm.pw') targetmode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP if STATE.VERBOSE: print 'checking perms on', adminpw try: mode = statmode(adminpw) except os.error, (code, msg): # adm.pw may not exist if code == errno.ENOENT: return raise if mode <> targetmode: STATE.ERRORS = STATE.ERRORS + 1 print adminpw, 'permissions must be exactly 0640 (got %s)' % oct(mode) if STATE.FIX: print '(fixing)' os.chmod(adminpw, targetmode)
def usage(code=0, msg=''): print __doc__ % globals() if msg: print 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()
elif opt in ('-f', '--fix'):
STATE.FIX = 1
elif opt in ('-v', '--verbose'):
STATE.VERBOSE = 1
checkall()
checkarchives()
checkarchivedbs()
checkcgi()
checkmail()
checkadminpw()
if not STATE.ERRORS:
print 'No problems found'
else:
print 'Problems found:', STATE.ERRORS
print 'Re-run as root with -f flag until no errors are found'
participants (2)
-
Barry A. Warsaw
-
Dan Mick