[alt.sources] root

timcc@csv.viccol.edu.au (Tim Cook) (04/30/91)

In article <muts.672650807@fysaj>, muts@fysak.fys.ruu.nl (Peter Mutsaers)
writes:
> Now that we are discussing a su encancer etc., here is a 'root' program that
> I've been using the last 1.5 year.

Here is my version, which I have been using for about a year.

> The syntax is 'root command [args]' and runs one command with su privilege.

Ditto.

> It is quite safe, and checks if the uid is right. (only works for one user).

My version checks you are in a particular group ("root" by default), just
like su(1) does.

> Without args it executes a shell with su privilege.

I have to put in a bit more work and say "root csh", but my version
automatically adds "/etc:/usr/etc" (this is configurable) to your PATH (if
they are not already there).

We have operators, including part-time operators who are allowed to use
this, so I prefer them not to be given a super-user shell unless they
explicitly ask for one.

Another feature that my version has that I have not seen in others, is that
it does an initgroups(3), to give the user _all_ super-user privileges.

> Of course this program must also be owned by root and be setuid.

Ditto.

> Change the number on the first line of main() to your own uid.

Compile mine with -DAUTHORIZED_GROUP='"wheel"', or whatever, if you prefer
that to "root".  (By the way, what does the group name "wheel" mean?)

Also note that if you are on a system that does not have setenv(3) and
strstr(3) (like the one I developed this on), you will have to find
versions of these routines to be able to compile this.  Look in the
bsd-sources directory on uunet, or ask me if you can't find these items.


#!/bin/sh
# This is a shell archive (shar 3.47)
# made 04/29/1991 02:17 UTC by timcc@admin.viccol.edu.au
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   3857 -rw-r----- root.c
#   1265 -r--r----- perror2.c
#
# ============= root.c ==============
if test -f 'root.c' -a X"$1" != X"-c"; then
	echo 'x - skipping root.c (File already exists)'
else
echo 'x - extracting root.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'root.c' &&
/* root.c -	Execute a command as superuser
X *
X * SYNOPSIS
X *	root <command> [<parameter> ...]
X */
X
static char rcsid[] = "$Header: root.c 1.2 91/04/29 $" ;
X
#include <grp.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>		/* For NGROUPS */
X
extern char *getenv () ;
extern int setenv () ;
extern int setuid () ;
extern int setgid () ;
extern int initgroups () ;
extern char *search_path () ;
extern char *malloc () ;
X
#ifndef TRUE
#define TRUE		1
#define FALSE		0
#endif
#define NULL_CP		(char *) 0
#define EOS		'\0'
#define fprint(f, s)	fputs (s, f)
X
static char *program_name ;
X
/*
X * If this is defined, the user must be a member of AUTHORIZED_GROUP
X * before they are allowed to execute their desired command as
X * super-user.
X */
#ifndef	AUTHORIZED_GROUP
#define	AUTHORIZED_GROUP	"root"
#endif
X
/*
X * These directories are added to the PATH environment variable before
X * the specified command is executed.  This must be terminated by a
X * null pointer.
X */
static char *dirs_for_path[] = { "/etc", "/usr/etc", NULL_CP } ;
X
X
static void add_to_path (path, element)
X   char *path, *element ;
{
X   char *path_p ;
X   char *elem_p ;
X   int elem_l = strlen (element) ;
X
X   if ((path_p = malloc (strlen (path) + 3) + elem_l) == NULL_CP) {
X      perror2 (program_name, "malloc") ;
X      exit (1) ; }
X   if ((elem_p = malloc (elem_l + 3)) == NULL_CP) {
X      perror2 (program_name, "malloc") ;
X      exit (1) ; }
X
X   *path_p++ = ':' ;
X   strcpy (path_p, path) ;
X   strcat (path_p, ":") ;
X   *elem_p++ = ':' ;
X   strcpy (elem_p, element) ;
X   strcat (elem_p, ":") ;
X   if (strstr (--path_p, --elem_p) == NULL_CP) {
X      /* Not already in path, so add it */
X      strcat (path_p, element) ;
X      strcpy (path, ++path_p) ; }
X   free (path_p) ;
X   free (elem_p) ;
X   }
X
X
int main (argc, argv)
X   int argc ;
X   char *argv[] ;
X   {
X      static char path[256] ;		/* This may need to be bigger */
X      char *exec_this ;
X      char **dirs ;
#ifdef AUTHORIZED_GROUP
X      int no_groups ;
X      int group_list[NGROUPS] ;
X      struct group *auth_group ;
X      int i ;
X      int member ;
#endif
X
X      program_name = argv[0] ;
X      if (argc < 2) {
X	 fprint (stderr, "usage: ") ;
X	 fprint (stderr, program_name) ;
X	 fprint (stderr, " <command> [<parameter> ...]\n") ;
X         exit (2) ; }
X
#ifdef AUTHORIZED_GROUP
X      /* If this code is used, only members of the AUTHORIZED_GROUP are
X	 given the privileges offered by this program. */
X
X      if ((no_groups = getgroups (NGROUPS, group_list)) == -1) {
X	 perror2 (program_name, "getgroups") ;
X	 exit (1) ; }
X      if ((auth_group = getgrnam (AUTHORIZED_GROUP))
X            == (struct group *) NULL) {
X	 perror2 (program_name, "getgrnam") ;
X	 exit (1) ; }
X
X      for (i = 0, member = FALSE ; i < no_groups && (! member) ; i++) {
X	 member = group_list[i] == auth_group->gr_gid ; }
X
X      if (! member) {
X	 fprint (stderr, program_name) ;
#ifdef NOT_SECRET
X	 fprint (stderr, ": not a member of \"") ;
X	 fprint (stderr, AUTHORIZED_GROUP) ;
X	 fprint (stderr, "\" group\n") ;
#else
X	 /* Perhaps the method of authorization is privileged information? */
X	 fprint (stderr, ": not authorized\n") ;
#endif
X	 exit (1) ; }
#endif
X
X      if (initgroups ("root", 0) == 1) {
X	 fprint (stderr, program_name) ;
X	 fprint (stderr, ": not super-user\n") ;
X	 exit (1) ; }
X      if (setgid (0) == -1) {
X	 perror2 (program_name, "setgid") ;
X         exit (1) ; }
X      if (setuid (0) == -1) {
X         perror2 (program_name, "setuid") ;
X         exit (1) ; }
X
X      strcat (path, getenv ("PATH")) ;
X      for (dirs = dirs_for_path ; *dirs != NULL_CP ; dirs++)
X	 add_to_path (path, *dirs) ;
X      if (setenv ("PATH", path, TRUE) == -1) {
X	 perror2 (program_name, "setenv") ;
X	 exit (1) ; }
X
X      if (execvp (argv[1], &argv[1]) != 0) {
X         perror2 (program_name, argv[1]) ;
X         exit (1) ; }
X      exit (0) ;
X      }
SHAR_EOF
chmod 0640 root.c ||
echo 'restore of root.c failed'
fi
# ============= perror2.c ==============
if test -f 'perror2.c' -a X"$1" != X"-c"; then
	echo 'x - skipping perror2.c (File already exists)'
else
echo 'x - extracting perror2.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'perror2.c' &&
/* perror2 -	Like perror(3), but with two string prefixes
X *
X * SYNOPSIS
X *	void perror2 (const char *str1, const char *str2) ;
X *
X * DESCRIPTION
X *	Prints str1, then a colon and a space, then str2, then a colon and
X *	a space, then the error message corresponding to the contents of
X *	errno, then a newline on stderr.
X */
X
static char rcsid[] = "$Header: perror2.c 1.1 91/03/22 $" ;
X
extern int strlen () ;
X
X
void perror2 (str1, str2)
X   char *str1, *str2 ;
{
X   extern int errno ;
X   extern char *sys_errlist[] ;
X   extern int sys_nerr ;
X   register int save_errno = errno ;
X   static char unknown_error[] = "Unknown error" ;
X   static char colon_space[2] = {':', ' '} ;
X   static char newline = '\n' ;
X   char *p ;
X
X   if (save_errno < 0 || save_errno >= sys_nerr)
X      p = unknown_error ;
X   else
X      p = sys_errlist[save_errno] ;
X   write (2, str1, strlen (str1)) ;
X   write (2, colon_space, sizeof (colon_space)) ;
X   write (2, str2, strlen (str2)) ;
X   write (2, colon_space, sizeof (colon_space)) ;
X   write (2, p, strlen (p)) ;
X   write (2, &newline, 1) ;
X   }
X
X
#ifdef TEST
X
int main (argc, argv)
X   int argc ;
X   char **argv ;
{
X   extern int errno ;
X
X   errno = atoi (argv[1]) ;
X   perror2 (argv[2], argv[3]) ;
X   exit (1) ;
X   }
X
#endif	/* TEST */
SHAR_EOF
chmod 0440 perror2.c ||
echo 'restore of perror2.c failed'
fi
exit 0