/*
** User-group id manipulation program plus chroot
**
** This MUST NOT EVER be installed SUID
**
** Seth Robertson
** seth@soscorp.com
**
** Copyright (c) 1992, 1195.  Seth Robertson
** All rights reserved.
*/

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>

main(argc, argv, envp)
int argc;
char **argv, **envp;
{
  int uid;
  int gid;
  int err = 0;
  int resetXtraGroups = 1;
  int InitXtraGroups = 0;
  char *user_init = NULL;
  int errflg = 0;
  int c;
  char *dir = NULL;
  char *shell, *getenv();
  extern char *optarg;
  extern int optind;

  if ((uid = getuidbyargv("nobody",&err)) == -1)
    if ((uid = getuidbyargv("noone",&err)) == -1)
      uid = 65534;

  if ((gid = getgidbyargv("nobody",&err)) == -1)
    if ((gid = getgidbyargv("noone",&err)) == -1)
      if ((gid = getgidbyargv("nogroup",&err)) == -1)
	gid = 65534;

  if (!(shell = getenv("SHELL")))
    shell = "/bin/sh";

  while ((c=getopt(argc,argv,"Zc:G:g:u:U:")) != -1)
    switch(c)
      {

      case 'Z':
	resetXtraGroups = 0;
        break;

      case 'u':
	uid = getuidbyargv(optarg,NULL);
	break;

      case 'U':			/* fall through case */
	uid = getuidbyargv(optarg,NULL);

      case 'G':
	resetXtraGroups = 1;
	InitXtraGroups = 1;
	user_init = optarg;
        break;

      case 'g':
	gid = getgidbyargv(optarg,NULL);
        break;

      case 'c':
	dir = optarg;
	break;

      case '?':
        errflg++;
      }

  if (errflg)
    {
      fprintf(stderr,"Usage: %s [-u <uid>] [-Z] [-U <user>] [-G <user>] [-g <gid>] [-c <chroot>] [program] [argv] ...\n",argv[0]);
      exit(2);
    }
  
  /* Initialize the extended group list */
  if (resetXtraGroups && (initgroups(InitXtraGroups?user_init:"zzzError",gid) < 0))
    {
      perror("initgroups");
      exit(3);
    }

  if (dir && chroot(dir) < 0)
    {
      perror("chroot");
      exit(3);
    }
  if (dir && chdir("/") < 0)
    {
      perror("Cannot chdir to new root");
      exit(3);
    }

  /* reset user and group */
  if ((setgid(gid) < 0) || (setuid(uid) < 0))
    {
      perror("setid");
      exit(3);
    }

  /* exec the program in question */
  if (optind >= argc)
    execl(shell, shell, 0);
  else
    execvp(argv[optind], argv+optind);
        
  perror("funi: exec");
  exit(-1);
}


/*
** Find out the gid if the argument is a number or a group name
*/
getgidbyargv(char *argv, int *error)
{
  int gid;

  if (isdigit(*argv))
    {
      gid = atoi(argv);
    }
  else
    {
      struct group *EE;
      
      EE = getgrnam(argv);
      if (!EE)
	{
	  if (error)
	    {
	      *error = 1;
	      return(-1);
	    }
	  else
	    {
	      perror("getpwnam");
	      exit(2);
	    }
	}
      gid = EE->gr_gid;
    }

  return(gid);
}

/*
** Find of the uid if the argument is a number or a user name
*/
getuidbyargv(char *argv, int *error)
{
  int uid;

  if (isdigit(*argv))
    {
      uid = atoi(argv);
    }
  else
    {
      struct passwd *pwd;
      
      pwd = getpwnam(argv);
      
      if (!pwd)
	{
	  if (error)
	    {
	      *error = 1;
	      return(-1);
	    }
	  else
	    {
	      perror("getpwnam");
	      exit(2);
	    }
	}
      uid = pwd->pw_uid;
    }

  return(uid);
}
