/* This tool suite was written by and is copyrighted by Brad Powell, Matt */
/* Archibald, and Dan Farmer 1999, 2000, 2001                             */
/* The copyright holder disclaims all responsibility or liability with    */
/* respect to its usage or its effect upon hardware or computer           */
/* systems, and maintains copyright as set out in the "LICENSE"           */
/* document which accompanies distribution.                               */
/* Titan version 4.0.10 March 25 10:01:02 PDT 2001                        */
/*                                                                        */
/* Special Thanks to Keith Watson for rewriting this and cleaning up my   */
/* numerous errors -bpowell                                               */
/*                                                                        */

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

 
#ifndef lint 
static char sccsid[] = "@(#)runas.c  Titan 4.0.10 3/25/01";
#endif /* not lint */ 

extern int errno;

int main(argc, argv, envp)
int     argc;
char  **argv;
char  **envp;
{

    struct passwd *pwEntry;
    struct group  *grEntry;
    uid_t newUID;
    gid_t newGID;

    if (argc < 4) {
	fprintf(stderr, "Titan Usage: %s username/UID groupname/GID command [opts]\n",
	    argv[0]);
	return(1);
    }

    /* check that we are running as root */
    if (geteuid() != 0) {
	fprintf(stderr, "Titan warning %s: must be run as root.\n", argv[0]);
	return(1);
    }

    /* null out all environment variables */
    envp[0] = NULL;

    /* eliminate all supplementary groups */
    if (setgroups(0, NULL) < 0) {
	fprintf(stderr, "%s: unable to eliminate supplementary groups: %s\n",
	    argv[0], strerror(errno));
	return(1);
    }

    /* check GID argument; only allow a numeric argument */
    if (isdigit((int)argv[2][0]))
	newGID = atoi(argv[2]);
    else {
	if ((grEntry = getgrnam(argv[2])) == NULL) {
	    fprintf(stderr, "%s: Invalid group name: %s\n", argv[0], argv[2]);
	    return(1);
	}
	newGID = grEntry->gr_gid;
    }

    /* switch GID */
    if (setgid(newGID) < 0) {
	fprintf(stderr, "%s: unable to set group ID: %s\n", argv[0],
	    strerror(errno));
	return(1);
    }

    /* check UID argument; only allow a numeric argument */
    if (isdigit((int)argv[1][0]))
	newUID = atoi(argv[1]);
    else {
	if ((pwEntry = getpwnam(argv[1])) == NULL) {
	    fprintf(stderr, "%s: Invalid user name: %s\n", argv[0], argv[1]);
	    return(1);
	}
	newUID = pwEntry->pw_uid;
    }
    
    /* check that we are not setting the UID to root (0) */
    if (newUID == 0) {
	fprintf(stderr, "%s: UID 0 (root) not allowed.\n", argv[0]);
	return(1);
    }

    /* switch UID; this must be done last or eliminating supplementary
	groups and switching GIDs will not work.
     */
    if (setuid(newUID) < 0) {
	fprintf(stderr, "%s: unable to set user ID: %s\n", argv[0],
	    strerror(errno));
	return(1);
    }

    /* execute the command */
    execvp(argv[3], argv + 3);

    /* this point is only reached if execvp() fails */
    fprintf(stderr, "%s: unable to execute command: %s\n", argv[0],
	strerror(errno));
    return(1);
}
