Description

It can be useful to run moin.cgi with a setgid wraper so user installed moin can run without unsafe world writable setup.

I wrote a simple setgid wrapper to run moin.cgi under Apache, so that my "data" and "underlay" directories didn't have to be writeable by "nogroup". However, "multiconfig.py" uses the "os.access" function to check access to these directories, and this uses the real UID and GID, and not the effective ones, which are the important ones in this case.

  1. Make a new instance.
  2. Compile this wrapper, editing the path as necessary:

/* moin.c -- wrapper to allow moin.cgi to run setgid
 *
 * Richard Brooksby, 2005-02-10
 *
 * This is a simple wrapper that executes the normal moin.cgi
 * script.  Since this is a binary, it can have its setgid bit set
 * and therefore have access to the necessary wiki directories
 * without compromising general security or having to have files
 * writeable by the Apache server in general.
 *
 * 2005-02-10  RB  Created.
 */

#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
  execv("/home/rb/www/www.brooksby.org/moin/bin/moin.cgi", argv);
  perror("execv");
  return 1;
}
  1. Move "moin.cgi" to the path you set, and replace it with the compiled wrapper.
  2. Make the wrapper setgid some group, and make the "data" and "underlay" directories writeable by that group and _not_ by the web server group.
  3. Try to look at the new wiki. You will get something like: "data_dir" does not exists at "/home/rb/www/www.brooksby.org/moin/data/", or has incorrect ownership and

permissions.

Environment details

MoinMoin Version

1.3.3

1.8.2

1.8.2

OS and Version

FreeBSD 4.7

linux-2.6.13-15

linux-2.4.18

Python Version

2.2.2

2.4.1

2.4.3

Server Setup

Apache version?

2.0.54-10

2.2.3

Server Details

Discussion

The access check in multiconfig is just a sanity check to help you catch configuration errors, and you can safely disable it. But I'm not sure that other code will work under you non-standard way of running moin.

Are you sure that this is the best way to run moin in this case? Why not configure apache so the cgi run as the correct user? See http://httpd.apache.org/docs/suexec.html

I don't have access to rebuild and reconfigure Apache. This very small setgid wrapper is a simple and, more importantly, user local method which allows us to run a user-installed moin. I submitted this as a bug because it might be a useful type of configuration for other moin users, and so you might want to consider supporting it.

UPDATE: I have discovered that the moin code has a lot of calls to os.access in order to determine whether files are accessible to the current process. This is incorrect. os.access is there to find out if a file is accessible to the "real" user/group _before_ a setuid/setgid process is run. It's intended for use by setuid/setgid programs in order to determine whether their caller has rights. It's being used erroneously in moin to find out something different. Unfortunately there's no easy replacement. What's needed is a compatible version of os.access that checks using the effective uid/gid. I plan to write one and will post it here. It should then be possible to fix this problem and all the related ones by replacing calls to os.access with it. RichardBrooksby 2005-02-11 16:01:07

Also note the BSD manual page for access(2) says:

     Access() is a potential security hole and should never be used.

We should fix this ... -- AlexanderSchremmer 2005-02-11 17:45:53

Here's my prototype code for a replacement for "os.access". Not tested thoroughly. RichardBrooksby 2005-02-11 17:50:43

# access.py -- functions for testing access to files
#
# Richard Brooksby, 2005-02-11
#
# This function supplement those in os.path with tests for readability and
# writeability of a filesystem object by the current _effective_ user and
# group.  Note that this is quite different to os.access, which checks the
# entire path using the _real_ user and group, and is intended as a utility
# for setuid/setgid programs.
#
# NOTES
#
# 1. This is prototype code and has not been tested in a production system.
#
# 2. Only looks at permissions bits and not whether the filesystem is
# read-only, for example.
#
# 3. Assumes root's uid is zero.
#
#
# 2005-02-11  RB  Created as a solution for
# <http://moinmoin.wikiwikiweb.de/FeatureRequests/UseSetgidWraper>.

import os
import os.path
import stat

def access(path, mode):

    try:
        s = os.stat(path)
    except OSError:
        return False

    euid = os.geteuid()
    egid = os.getegid()

    mask = stat.S_IRWXO

    if s.st_gid == egid:
        mask = mask | stat.S_IRWXG

    if s.st_uid == euid:
        mask = mask | stat.S_IRWXU

    # If you're asking about readability
    if mode & os.R_OK:
        # If you're root, or any relevant readability bits are set, then
        # you can read it.
        if (euid == 0 or
            (s.st_mode & mask & (stat.S_IROTH | stat.S_IRGRP | stat.S_IRUSR))):
            pass
        else:
            return False

    # See above for comments, but s/readable/writeable/.
    if mode & os.W_OK:
        if (euid == 0 or
            (s.st_mode & mask & (stat.S_IWOTH | stat.S_IWGRP | stat.S_IWUSR))):
            pass
        else:
            return False

    if mode & os.X_OK:
        # Root can't execute things that aren't executable.  That would be a
        # very nasty security hole.  However, root _can_ execute things that
        # _anyone_ can execute.
        if euid == 0:
            if s.st_mode & (stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR):
                pass
            else:
                return False
        elif s.st_mode & mask & (stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR):
            pass
        else:
            return False

    return True

If its really a security problem to use os.access, then we should open a bug about this, and review the code for os.access usage.

I would like to make the problem more clear:

-- NirSoffer 2005-02-11 18:11:24

Yes, that's right. See the Python manual for os.access or see the man page for access(2). Both tell you the same thing. In addition, the man page explains why it's a security hole. It looks like there's an "eaccess" function in some versions of BSD, but it's not available in Python. RichardBrooksby 2005-02-11 18:36:30

I've attached a patch against MoinMoin 1.3.3 to this page. See MoinMoin-access.patch for details. I'm using this patch on the Brooksby Family Wiki at the moment to see how it goes. RichardBrooksby 2005-02-11 18:38:27

Please do not use this patch!. Testing permissions by using stat and examining the mode bits is not a reliable way of testing permissions. Here are some reasons:

  1. There may be extra features of the underlying operation system like extended ACLs that grant permissions differently than the mode bits.
  2. The file system may be a network file system that implements file permissions differently than you expect. Filesystems generally try to map the permissions they expect to grant into the mode bits but exotic features or permission models other than POSIX do not always permit this.
  3. The permissions might have changed between the time you do the stat and the time you actually try to use the file. You will then either deny the user access they should have got, or end up having to deal with a permission error later on anyway.

  4. As someone already observed, it's not portable.

The correct way to try and see if you can do a given operation on a file is to just try to do it. If you get an error then, well, you can't do it. That's why there's no system call in POSIX to do what you are trying to do. access exists, as someone already observed, for privileged programs to be able to determine whether the user would have had access to the file had the program not been privileged. Programs that use it in this capacity must be extra careful about what the security implications of the inhertent race conditions are. --pkv 2005-07-12 21:29:51

I wrote the patch and I agree that it's no good for general purpose use. It allowed me to get MoinMoin working in my setup, and I thought I'd share it, but that's about it. It may work for you if you know what you're doing. MoinMoin needs changing to do as pkv suggests, and simply try to access files. In any case it should not use the access syscall. RB

--

    Why not configure apache so the cgi run as the correct user?
    See http://httpd.apache.org/docs/suexec.html

Using apache suExec is a good solution, but suExec is not supported by all web servers, and security policy may prohibit editting and recompiling apache source already vetted.

    This very small setgid wrapper is a simple and ... useful type of configuration
    for other moin users, and so you might want to consider supporting it.

setuid(2) is a POSIX compliant (i.e. unix standard) security choice that is web server independent, and widely used for both web and non-web gateways.

    The correct way ... is to just try to do it.

Please consider http://moinmo.in/MoinMoinPatch/SetuidFixReplaceOsAccess .

Hopefully helpful -- Unw 2009-04-25 22:09:24

--

sudo(8) is a popular tool (front-end for setuid(2)) to manage privilege for general purposes including web gateway configuration.

Instructions for installation with sudo(8), setuid, setgid, and this wrapper have been added as HowTo/Install MoinMoin under sudo, setuid, or setgid wrapper.

Hopefully helpful -- Unw 2010-10-24 22:05:01


CategoryFeatureRequest CategoryMoinMoinPatch

MoinMoin: FeatureRequests/UseSetgidWraper (last edited 2010-10-24 21:10:59 by Unw)