[rt-users] Using the majordomo wrapper program to get around setuid problem

Bruce Campbell bruce_campbell at ripe.net
Tue Dec 4 08:46:54 EST 2001


( Subject changed to make it easier for people searching the archives )

This is a brief rundown on how to use a setuid program to invoke
rt-mailgate, when your OS's perl cannot (or won't) do setuid properly.

Firstly, compile and install RT.

Secondly, retrieve and extract majordomo from
ftp://ftp.greatcircle.com/pub/majordomo .  I used version 1.94.5 for this.
I've also attached to this message a cut-down Makefile and the original
wrapper.c .

Thirdly, edit the Majordomo Makefile (or the attached one) and change the
following variables:

	W_HOME = /path/to/rt2/bin
	W_USER = NUMERIC_ID_OF_RT_USER
	W_GROUP = NUMERIC_ID_OF_RT_GROUP

Next, run 'make wrapper', and 'make install-wrapper' as root.

Finally, put it in your /etc/aliases (or appropriate MTA location) as (on
one line of course):

	rt-comment: "|/path/to/rt2/bin/wrapper rt-mailgate --queue
		QUEUE_NAME --action comment"
and
	rt:         "|/path/to/rt2/bin/wrapper rt-mailgate --queue
		QUEUE_NAME --action correspond"

( Note that wrapper only looks for the program (1st argument) in the HOME
  directory defined below.  You don't need to put
  '/path/to/rt2/bin/rt-mailgate' in the alias file )

When fault-finding, note that /path/to/rt2/bin/wrapper should be setuid,
be owned by root and the RT group, and the /path/to/rt2/bin should be
within the wrapper binary, ie:

	$ strings -a /path/to/rt2/bin/wrapper
	 HOME
	HOME=/path/to/rt2/bin
	    HOME is %s,

If HOME=/something/else, then you've probably ended up with your majordomo
version of wrapper.

Your next port of call is ensuring that /path/to/rt2/bin/rt-mailgate
/path/to/rt2/bin/rt-mailgate is executable by the RT user, that the
directory tree all the way to the / is accessible by the RT user, and the
perl indicated by the first '#!' line is executable by the RT user.  Then
further fault-find by judicious application of perl -c and checking that
the RT user can access all the libraries, *including*
/path/to/rt2/etc/config.pm .

I hope this helps.

Regards,

-- 
                             Bruce Campbell                            RIPE
                                                                        NCC
                                                                 Operations
-------------- next part --------------

# Makefile solely for majordomo wrapper for use by RT2

#-------------  Configure these items ----------------# 
#
 
# Put the location of your Perl binary here:
PERL = /bin/perl

# What do you call your C compiler?
CC = cc
 
# Where should the wrapper program to be installed, and the directory that
# the wrapper can execute programs from.
W_HOME = /path/to/rt2/bin
 
# Remember to create an RT user and group!
# You need to have or create a user and group which the wrapper will run as.
# Enter the numeric UID and GID (not their names!) here:
W_USER = 97
W_GROUP = 97

# These set the permissions for all installed files and executables (except
# the wrapper), respectively.  Some sites may wish to make these more
# lenient, or more restrictive.
FILE_MODE = 644
EXEC_MODE = 755
HOME_MODE = 751

# If your system is POSIX (e.g. Sun Solaris, SGI Irix 5 and 6, Dec Ultrix MIPS,
# BSDI or other 4.4-based BSD, Linux) use the following four lines.  Do not
# change these values!
WRAPPER_OWNER = root
WRAPPER_GROUP = $(W_GROUP)
WRAPPER_MODE = 4755
POSIX = -DPOSIX_UID=$(W_USER) -DPOSIX_GID=$(W_GROUP)

# Otherwise, if your system is NOT POSIX (e.g. SunOS 4.x, SGI Irix 4,
# HP DomainOS) then comment out the above four lines and uncomment
# the following four lines.
# WRAPPER_OWNER = $(W_USER)
# WRAPPER_GROUP = $(W_GROUP)
# WRAPPER_MODE = 6755
# POSIX = 

# This is the environment that (along with LOGNAME and USER inherited from the
# parent process, and without the leading "W_" in the variable names) gets
# passed to processes run by "wrapper"
W_SHELL = /bin/sh
W_PATH = /bin:/usr/bin:/usr/ucb
W_MAJORDOMO_CF = $(W_HOME)/majordomo.cf

# A directory for temp files..
TMPDIR = /usr/tmp

#--------YOU SHOULDN'T HAVE TO CHANGE ANYTHING BELOW THIS LINE.-------------

VERSION =	1.94.5

# For those stupid machines that try to use csh. Doh!
SHELL = /bin/sh

WRAPPER_FLAGS = -DBIN=\"$(W_HOME)\" -DPATH=\"PATH=$(W_PATH)\" \
	-DHOME=\"HOME=$(W_HOME)\" -DSHELL=\"SHELL=$(W_SHELL)\" \
	-DMAJORDOMO_CF=\"MAJORDOMO_CF=$(W_MAJORDOMO_CF)\"      \
	$(POSIX)

INSTALL = ./install.sh

TMP = $(TMPDIR)/mj-install-$(VERSION)

default: 
	@echo "make what?"
	@echo "    install-wrapper: only install wrapper."
	@echo "    wrapper: only make wrapper."

install-wrapper: wrapper
	$(INSTALL) -o $(WRAPPER_OWNER) -g $(WRAPPER_GROUP) \
		-m $(WRAPPER_MODE) wrapper $(W_HOME)/wrapper
	@echo ""
	@echo "To verify that all the permissions and etc are correct,"
	@echo "run the command"
	@echo ""
	@echo "	     cd $(W_HOME); ./wrapper config-test"

wrapper: wrapper.c
	$(CC)  $(WRAPPER_FLAGS) -o wrapper wrapper.c

clean:
	rm -f  wrapper *~
-------------- next part --------------
/*
 *  $Source: /sources/cvsrepos/majordomo/wrapper.c,v $
 *  $Revision: 1.8 $
 *  $Date: 1997/08/27 15:01:12 $
 *  $Author: cwilson $
 *  $State: Exp $
 *
 *  $Locker:  $
 *  
 */

#ifndef lint
static char rcs_header[] = "$Header: /sources/cvsrepos/majordomo/wrapper.c,v 1.8 1997/08/27 15:01:12 cwilson Exp $";
#endif

#include <stdio.h>
#include <sysexits.h>

#if defined(sun) && defined(sparc)
#include <stdlib.h>
#endif


#ifndef STRCHR
#  include <string.h>
#  define STRCHR(s,c) strchr(s,c)
#endif

#ifndef BIN
#  define BIN "/usr/local/mail/majordomo"
#endif

#ifndef PATH
#  define PATH "PATH=/bin:/usr/bin:/usr/ucb"
#endif

#ifndef HOME
#  define HOME "HOME=/usr/local/mail/majordomo"
#endif

#ifndef SHELL
#  define SHELL "SHELL=/bin/sh"
#endif

char * new_env[] = {
    HOME,		/* 0 */
    PATH,		/* 1 */
    SHELL,		/* 2 */
#ifdef MAJORDOMO_CF
    MAJORDOMO_CF,	/* 3 */
#endif
    0,		/* possibly for USER or LOGNAME */
    0,		/* possible for LOGNAME */
    0,          /* possibly for timezone */
    0
};
    
int new_env_size = 7;				/* to prevent overflow problems */

main(argc, argv, env)
    int argc;
    char * argv[];
    char * env[];

{
    char * prog;
    int e, i;

    if (argc < 2) {
	fprintf(stderr, "USAGE: %s program [<arg> ...]\n", argv[0]);
	exit(EX_USAGE);
    }

    /* if the command contains a /, then don't allow it */
    if (STRCHR(argv[1], '/') != (char *) NULL) {
	/* this error message is intentionally cryptic */
	fprintf(stderr, "%s: error: insecure usage\n", argv[0]);
	exit(EX_NOPERM);
    }

    if ((prog = (char *) malloc(strlen(BIN) + strlen(argv[1]) + 2)) == NULL) {
	fprintf(stderr, "%s: error: malloc failed\n", argv[0]);
	exit(EX_OSERR);
    }

    sprintf(prog, "%s/%s", BIN, argv[1]);

    /*  copy the "USER=" and "LOGNAME=" envariables into the new environment,
     *  if they exist.
     */

#ifdef MAJORDOMO_CF
    e = 4; /* the first unused slot in new_env[] */
#else
    e = 3; /* the first unused slot in new_env[] */
#endif

    for (i = 0 ; env[i] != NULL && e <= new_env_size; i++) {
	if ((strncmp(env[i], "USER=", 5) == 0) ||
	    (strncmp(env[i], "TZ=", 3) == 0) ||
	    (strncmp(env[i], "LOGNAME=", 8) == 0)) {
	    new_env[e++] = env[i];
	}
    }


#if defined(SETGROUP)
/* renounce any previous group memberships if we are running as root */
    if (geteuid() == 0) { /* Should I exit if this test fails? */
    char *setgroups_used = "setgroups_was_included"; /* give strings a hint */
#if defined(MAIL_GID)
    int groups[] =  { POSIX_GID, MAIL_GID, 0 };
    if (setgroups(2, groups) == -1) {
#else
    int groups[] =  { POSIX_GID, 0 };
    if (setgroups(1, groups) == -1) {
#endif
	extern int errno;

	fprintf(stderr, "%s: error setgroups failed errno %d", argv[0],
		errno);
	}
}
#endif
	  

#ifdef POSIX_GID
    setgid(POSIX_GID);
#else
    setgid(getegid());
#endif

#ifdef POSIX_UID
    setuid(POSIX_UID);
#else
    setuid(geteuid());
#endif

    if ((getuid() != geteuid()) || (getgid() != getegid())) {
	fprintf(stderr, "%s: error: Not running with proper UID and GID.\n", argv[0]);
	fprintf(stderr, "    Make certain that wrapper is installed setuid, and if so,\n");
	fprintf(stderr, "    recompile with POSIX flags.\n");
	exit(EX_SOFTWARE);
    }

    execve(prog, argv+1, new_env);

    /* the exec should never return */
    fprintf(stderr, "wrapper: Trying to exec %s failed: ", prog);
    perror(NULL);
    fprintf(stderr, "    Did you define PERL correctly in the Makefile?\n");
    fprintf(stderr, "    HOME is %s,\n", HOME);
    fprintf(stderr, "    PATH is %s,\n", PATH);
    fprintf(stderr, "    SHELL is %s,\n", SHELL);
    fprintf(stderr, "    MAJORDOMO_CF is %s\n", MAJORDOMO_CF);
    exit(EX_OSERR);
}


More information about the rt-users mailing list