diff -urN Zope-2.5.1-src.orig/lib/Components/initgroups/initgroups.c Zope-2.5.1-src/lib/Components/initgroups/initgroups.c --- Zope-2.5.1-src.orig/lib/Components/initgroups/initgroups.c Wed Dec 31 19:00:00 1969 +++ Zope-2.5.1-src/lib/Components/initgroups/initgroups.c Mon Mar 25 21:08:41 2002 @@ -0,0 +1,48 @@ +/***************************************************************************** + + Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved. + + This software is subject to the provisions of the Zope Public License, + Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED + WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS + FOR A PARTICULAR PURPOSE + + ****************************************************************************/ + +#include "Python.h" + +#include +#include +#include + +static PyObject * +initgroups_initgroups(self, args) +PyObject *self; +PyObject *args; +{ + char *username; + gid_t gid; + + if(!PyArg_ParseTuple(args, "sl", &username, &gid)) + return NULL; + + if(initgroups(username, gid) == -1) + return PyErr_SetFromErrno(PyExc_OSError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef InitgroupsMethods[] = { + {"initgroups", initgroups_initgroups, METH_VARARGS}, + {NULL, NULL} +}; + +void +initinitgroups() +{ + (void)Py_InitModule("initgroups", InitgroupsMethods); +} + diff -urN Zope-2.5.1-src.orig/lib/python/Setup20 Zope-2.5.1-src/lib/python/Setup20 --- Zope-2.5.1-src.orig/lib/python/Setup20 Fri Feb 16 14:54:25 2001 +++ Zope-2.5.1-src/lib/python/Setup20 Mon Mar 25 20:52:46 2002 @@ -18,3 +18,5 @@ zlib ../Components/zlib/zlib.c -I../Components/zlib ../Components/zlib/adler32.c ../Components/zlib/compress.c ../Components/zlib/crc32.c ../Components/zlib/gzio.c ../Components/zlib/uncompr.c ../Components/zlib/deflate.c ../Components/zlib/trees.c ../Components/zlib/zutil.c ../Components/zlib/inflate.c ../Components/zlib/infblock.c ../Components/zlib/inftrees.c ../Components/zlib/infcodes.c ../Components/zlib/infutil.c ../Components/zlib/inffast.c + +initgroups ../Components/initgroups/initgroups.c diff -urN Zope-2.5.1-src.orig/z2.py Zope-2.5.1-src/z2.py --- Zope-2.5.1-src.orig/z2.py Wed Nov 28 10:50:49 2001 +++ Zope-2.5.1-src/z2.py Mon Mar 25 21:01:28 2002 @@ -72,9 +72,9 @@ -u username or uid number - The username to run ZServer as. You may want to run ZServer as 'nobody' - or some other user with limited resouces. The only works under Unix, and - if ZServer is started by root. The default is: %(UID)s + The username to run ZServer as. You may want to run ZServer as + a dedicated user. This only works under Unix, and if ZServer + is started as root, and is required in that case. -P [ipaddress:]number @@ -250,8 +250,9 @@ DNS_IP='' # User id to run ZServer as. Note that this only works under Unix, and if -# ZServer is started by root. -UID='nobody' +# ZServer is started by root. This no longer defaults to 'nobody' since +# that can lead to a Zope file compromise. +UID=None # Log file location. If this is a relative path, then it is joined the # the 'var' directory. @@ -473,6 +474,11 @@ zdaemon.run(sys.argv, os.path.join(CLIENT_HOME, Zpid)) +def _warn_nobody(): + zLOG.LOG("z2", zLOG.INFO, "Running Zope as 'nobody' can compromise " + \ + "your Zope files; consider using a " + \ + "dedicated user account for Zope") + try: # Import logging support import zLOG @@ -650,47 +656,89 @@ hostname=address, port=port) - # Try to set uid to "-u" -provided uid. - # Try to set gid to "-u" user's primary group. - # This will only work if this script is run by root. - try: - import pwd - try: - try: UID = string.atoi(UID) - except: pass - gid = None - if type(UID) == type(""): - uid = pwd.getpwnam(UID)[2] - gid = pwd.getpwnam(UID)[3] - elif type(UID) == type(1): - uid = pwd.getpwuid(UID)[2] - gid = pwd.getpwuid(UID)[3] - else: - raise KeyError - try: - if gid is not None: - try: - os.setgid(gid) - except OSError: - pass - os.setuid(uid) - except OSError: - pass - except KeyError: - zLOG.LOG("z2", zLOG.ERROR, ("can't find UID %s" % UID)) - except: - pass - - - # if it hasn't failed at this point, create a .pid file. if not READ_ONLY: + if os.path.exists(PID_FILE): os.unlink(PID_FILE) pf = open(PID_FILE, 'w') pid=str(os.getpid()) try: pid=str(os.getppid())+' '+pid except: pass pf.write(pid) pf.close() + + # Warn if we were started as nobody. + try: + import pwd + if os.getuid(): + if pwd.getpwuid(os.getuid())[0] == 'nobody': + _warn_nobody() + except: + pass + + # Drop root privileges if we have them, and do some sanity checking + # to make sure we're not starting with an obviously insecure setup. + try: + if os.getuid() == 0: + try: + import initgroups + except: + raise SystemExit, 'initgroups is required to safely setuid' + if UID == None: + raise SystemExit, 'A user was not specified to setuid ' + \ + 'to; fix this to start as root' + import stat + client_home_stat = os.stat(CLIENT_HOME) + client_home_faults = [] + if not (client_home_stat[stat.ST_MODE]&01000): + client_home_faults.append('does not have the sticky bit set') + if client_home_stat[stat.ST_UID] != 0: + client_home_faults.append('is not owned by root') + if len(client_home_faults) > 0: + raise SystemExit, CLIENT_HOME + ' ' + \ + string.join(client_home_faults, ', ') + \ + '; fix this to start as root' + try: + try: UID = string.atoi(UID) + except: pass + gid = None + if type(UID) == type(""): + uid = pwd.getpwnam(UID)[2] + gid = pwd.getpwnam(UID)[3] + elif type(UID) == type(1): + uid = pwd.getpwuid(UID)[2] + gid = pwd.getpwuid(UID)[3] + UID = pwd.getpwuid(UID)[0] + else: + raise KeyError + if UID == 'nobody': + _warn_nobody() + try: + initgroups.initgroups(UID, gid) + if gid is not None: + try: + os.setgid(gid) + except OSError: + pass + os.setuid(uid) + except OSError: + pass + except KeyError: + zLOG.LOG("z2", zLOG.ERROR, ("Can't find UID %s" % UID)) + except AttributeError: + pass + except: + raise + + # Check umask sanity. + if os.name == 'posix': + # umask is silly, blame POSIX. We have to set it to get its value. + current_umask = os.umask(0) + os.umask(current_umask) + if current_umask != 077: + current_umask = '%03o' % current_umask + zLOG.LOG("z2", zLOG.INFO, 'Your umask of ' + current_umask + \ + ' may be too permissive; for the security of your ' + \ + 'Zope data, it is recommended you use 077') except: # Log startup exception and tell zdaemon not to restart us.