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