home *** CD-ROM | disk | FTP | other *** search
- /*++
- /* NAME
- /* opcom 1
- /* SUMMARY
- /* execute an operator command.
- /* SYNOPSIS
- /* .fi
- /* .B opcom
- /* command [ arguments ]
- /* DESCRIPTION
- /* .I Opcom
- /* enables users belonging to a special group (as defined in /etc/group)
- /* to execute a limited set of commands with another userid (e.g. root)
- /* or groupid. The file
- /* .I XCOMMANDS
- /* describes which commands are allowed for which groups and which
- /* userid (groupid) will be used.
- /* .br
- /* .I Command
- /* is a valid operator command if it matches an entry in
- /* .I XCOMMANDS.
- /* Those entries have the following form:
- /*
- /* path-name : operator-group : [ new-userid ] [ : [ new-groupid ]]
- /*
- /* .I Command
- /* matches an entry, if it is the basename of
- /* .I path-name
- /* and if at least one of the following conditions holds:
- /* .IP \- 2.4
- /* .I operator-group
- /* is "*".
- /* .IP \- 2.4
- /* The real userid executing
- /* .I opcom
- /* is a member of the group
- /* .I operator-group
- /* (as defined in /etc/group).
- /* .IP \- 2.4
- /* The default group of the real userid, as defined in /etc/passwd,
- /* is equal to
- /* .I operator-group.
- /* .IP \- 2.4
- /* The effective groupid is that of
- /* .I operator-group.
- /* .RE
- /*
- /* If
- /* .I command
- /* matches more than one entry, the first matching entry is selected.
- /* This allows multiple levels of privilege.
- /* If no match is found,
- /* .I opcom
- /* aborts with the message "access denied.".
- /*
- /* The matching command is executed with the given
- /* .I arguments
- /* unless they contain characters which are special to /bin/sh (e.g. ;).
- /* .br
- /* If
- /* .I new-userid (new-groupid)
- /* is not empty, the effective and real userid (groupid) are set to
- /* .I new-userid (new-groupid).
- /* .br
- /* If
- /* .I new-userid (new-groupid)
- /* is empty, the effective and real userid (groupid)
- /* are set to the real userid (groupid).
- /*
- /* The current environment is cleared and the following environment
- /* variables are set:
- /* .IP \- 2.4
- /* COMMAND to
- /* .I command.
- /* .IP \-
- /* ORGGROUP to the group name of the real groupid of the invoker of
- /* .I opcom.
- /* .IP \-
- /* GROUP to the group name of the real groupid executing
- /* .I command.
- /* .IP \-
- /* IFS to blank, tab and new line.
- /* .IP \-
- /* ORGUSER to the login name of the real userid of the invoker of
- /* .I opcom.
- /* .IP \-
- /* PATH to /
- /* .IP \-
- /* USER and LOGNAME to the login name of the real userid executing
- /* .I command.
- /* .RE
- /*
- /* If the script
- /* .I XPROFILE
- /* exists, this script will be executed
- /* within the (Bourne) shell that executes
- /* .I command.
- /* So changes to the environment (e.g. PATH) can be put there.
- /*
- /* If
- /* .I opcom
- /* is called with no arguments, a list of valid commands for the
- /* user executing
- /* .I opcom
- /* is printed.
- /* FILES
- /* XCOMMANDS
- /* XPROFILE
- /* /etc/group
- /* /etc/passwd
- /* CAVEAT
- /* Beware of Trojan horses: don't allow commands with shell escapes.
- /* BUGS
- /* Invalid entries in
- /* .I XCOMMANDS
- /* are skipped without warning.
- /* DIAGNOSTICS
- /* In case of error
- /* .I opcom
- /* prints an error message on standard error and terminates
- /* with nonzero status.
- /* .br
- /* Commands executed by \fIopcom\fP are optionally reported via syslogd(8).
- /* EXAMPLES
- /*
- /*
- /* .I XCOMMANDS:
- /*
- /* .nf
- /* .ft C
- /* /bin/fsck:operators:root
- /* /bin/dump:operators:root
- /* /bin/kill:operators:root
- /* /etc/lpc:operators:root
- /* /usr/ucb/lprm:operators:root
- /* /bin/mount:operators:root
- /* /bin/restore:operators:root
- /* /bin/shutdown:operators:root
- /* /bin/umount:operators:root
- /* /mgt/cas/bin/menu:ucadmin:root
- /* /usr/local/bin/lp-cancel:bcf:daemon:daemon
- /* /usr/local/bin/lp-move:bcf:daemon:daemon
- /* /usr/local/bin/lp-force:bcf:daemon:daemon
- /* /usr/local/bin/lp-sched:bcf:daemon:daemon
- /* /usr/local/bin/lp-shut:bcf:daemon:daemon
- /* /usr/local/lib/opcom/testenv:*:
- /*
- /* .ft
- /* Invocation examples:
- /*
- /* opcom lpc start lp
- /* opcom shutdown
- /* opcom lp-shut
- /* opcom testenv
- /* AUTHOR(S)
- /*
- /* Carel Braam (rccarel@urc.tue.nl)
- /* Eindhoven University of Technology
- /* Computing Centre
- /* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
- /* BUG REPORTS TO
- /* rccarel@urc.tue.nl
- /* CREATION DATE
- /* Wed Jan 4 14:12:59 MET 1989
- /* LAST MODIFICATION
- /* Thu Jan 10 10:02:52 MET 1991
- /* VERSION/RELEASE
- /* 2.1
- /*--*/
-
- #include <stdio.h>
- #include "sysdep.h"
- #include <grp.h>
- #include <pwd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <varargs.h>
- #include <ctype.h>
- #ifdef USE_SYSLOG
- #include <syslog.h>
- extern int openlog ();
- extern void syslog ();
- #endif
-
- extern void exit ();
- extern void perror ();
- extern int errno;
- extern char *sys_errlist [];
- extern int sys_nerr;
-
- extern int getegid ();
- extern int geteuid ();
- extern int getgid ();
- extern int setuid ();
- extern struct group *getgrgid ();
- extern struct group *getgrnam ();
- extern struct passwd *getpwnam ();
- extern struct passwd *getpwuid ();
- extern char *malloc ();
- extern int system ();
-
- #define WARN 0
- #define ABORT 1
-
- struct command {
- char name [64];
- char path [1024];
- char group [32];
- char new_group [32];
- char new_user [32];
- };
-
- void PERROR ();
- int check_group ();
- int copy_args ();
- void display_commands ();
- void error ();
- void exec_cmnd ();
- struct command *get_command ();
- struct command *parse_command ();
-
- FILE *command_file;
- int my_egid,
- my_gid,
- my_euid,
- my_uid;
- char orguser [32] = {0};
- char orggroup [32] = {0};
- char *nextitem ();
- void set_env ();
- void set_ids ();
-
- char *progname; /* used for diagnostics */
-
- main (argc, argv)
- int argc;
- char **argv;
- {
- int fd;
- struct command *command;
- struct group *group;
- struct passwd *passwd;
-
- /* close all file descriptors > 2 */
- for (fd = 3; fd <= _NFILE; fd++)
- (void) close (fd);
- if (progname = strrchr (*argv, '/'))
- progname++;
- else
- progname = *argv;
- argv++;
- argc--;
-
- my_euid = geteuid();
- if (my_euid != 0)
- error (WARN, "Warning: the effective userid is not root.");
- my_uid = getuid();
- if ((passwd = getpwuid (my_uid)) == NULL)
- error (ABORT, "cannot find passwd entry for userid %d.", my_uid);
- (void) strcpy (orguser, passwd->pw_name);
-
- my_egid = getegid();
- my_gid = getgid();
- if ((group = getgrgid (my_gid)) != NULL)
- (void) strcpy (orggroup, group->gr_name);
-
- if ((command_file = fopen (COMMANDS, "r")) == NULL)
- error (ABORT, "cannot open %s for reading.", COMMANDS);
- if (argc == 0) {
- display_commands ();
- exit (0);
- }
-
- command = get_command (*argv);
- set_ids (command->new_user, command->new_group);
- (void) fclose (command_file);
- exec_cmnd (command, argc-1, argv+1);
- return (0); /* makes lint happy */
- }
-
- void display_commands ()
- {
- struct command *command;
- int first = 1;
- int printit;
-
- while ((command = parse_command ()) != NULL) {
- if (my_uid == 0) { /* root has access to all commands */
- printit = 1;
- } else {
- printit = check_group (command->group);
- }
- if (printit) {
- if (first) {
- first = 0;
- (void) printf ("Valid opcom commands are:\n\n");
- }
- (void) printf ("%s (group %s)\n", command->name, command->group);
- }
- }
- if (first) /* We didn't find valid commands */
- error (ABORT, "access denied.");
- }
-
-
- struct command *get_command (name)
- char *name;
- {
- struct command *command;
-
- while ((command = parse_command ()) != NULL) {
- if (strcmp (name, command->name) == 0) {
- if (my_uid == 0) /* root has access to all commands */
- return (command);
- if (check_group (command->group))
- return (command);
- }
- }
- error (ABORT, "access denied.");
- return (NULL);
- }
-
- struct command *parse_command ()
- {
- static char cmndbuf [BUFSIZ];
- static struct command command;
- char ch,
- *cmnd,
- *pc,
- *ptail,
- *pt;
-
- while (fgets (cmndbuf, BUFSIZ, command_file) != NULL) {
- for (cmnd = cmndbuf; isspace (*cmnd); cmnd++);
- pt = command.path;
- ptail = cmnd;
- while (! (isspace (ch = *ptail) || (ch == ':') || (ch == 0))) {
- *pt++ = ch;
- ptail++;
- }
- *pt = 0;
- while (isspace (*ptail))
- ptail++;
- if (*ptail != ':') /* invalid entry */
- continue;
- pt = command.path;
- if ((pc = strrchr (pt, '/')) == NULL)
- pc = pt;
- else
- pc++;
- (void) strcpy (command.name, pc);
- ptail = nextitem (ptail+1, command.group);
- ptail = nextitem (ptail, command.new_user);
- ptail = nextitem (ptail, command.new_group);
- return (&command);
- }
- return (NULL);
- }
-
- char *nextitem (pstart, target)
- char *pstart,
- *target;
- {
- char ch,
- *ps = pstart,
- *pt = target;
-
- while (isspace (*ps))
- ps++;
- if (*ps == 0) {
- *pt = 0;
- return (ps);
- }
- for (ch = *ps; (ch != ':') & (ch != 0); ch = *++ps)
- *pt++ = ch;
- *pt-- = 0;
- for (ch = *pt; isspace (ch); ch = *pt--)
- *pt = 0;
- if (*ps == 0)
- return (ps);
- else
- return (ps+1);
- }
-
- int check_group (groupname)
- char *groupname;
- {
- char **gr_list;
- struct group *group;
-
- if (strcmp (groupname, "*") == 0) /* matches everything */
- return (1);
- if ((group = getgrnam (groupname)) != NULL) {
- if (my_egid == group->gr_gid) /* effective groupid */
- return (1);
- for (gr_list = group->gr_mem; *gr_list != NULL; gr_list++) {
- if (strcmp (orguser, *gr_list) == 0)
- return (1);
- }
- }
- return (0);
- }
-
- void set_ids (new_user, new_group)
- char *new_user,
- *new_group;
- {
- struct group *group;
- struct passwd *passwd;
-
- if (*new_group != 0) {
- /* not empty, must be set before uid is set */
- if ((group = getgrnam (new_group)) == NULL)
- error (ABORT, "cannot find group entry for groupid %s.", new_group);
- if (setgid (group->gr_gid) < 0)
- PERROR (ABORT);
- }
- if (*new_user != 0) { /* not empty */
- if ((passwd = getpwnam (new_user)) == NULL)
- error (ABORT, "cannot find passwd entry for userid %s.", new_user);
- if (setuid (passwd->pw_uid) < 0)
- PERROR (ABORT);
- } else if (setuid (getuid ()) < 0)
- PERROR (ABORT);
- }
-
- void exec_cmnd (command, argc, argv)
- struct command *command;
- int argc;
- char **argv;
- {
- unsigned cmnd_size = 0;
- int i,
- rslt;
- struct stat prstat;
- char
- special,
- *args,
- *cmnd,
- *pa,
- *pc;
-
- set_env (command->name, command->new_group, command->new_user);
- cmnd_size = strlen (command->path)+32;
- for (i = 0; i < argc; i++)
- cmnd_size += strlen (argv [i])+1;
- pc = cmnd = malloc (cmnd_size+strlen (PROFILE));
- if ((stat (PROFILE, &prstat) == 0) && (prstat.st_mode & 0400)) {
- /* We must execute the profile */
- (void) sprintf (pc, ". %s;", PROFILE);
- pc += strlen (pc);
- }
- pa = command->path;
- while (*pa != 0)
- *pc++ = *pa++;
- *pc++ = ' ';
- args = pc;
- special = copy_args (argc, argv, args);
-
- #ifdef USE_SYSLOG
- # ifdef LOG_AUTH
- (void) openlog (progname, LOG_PID, LOG_AUTH);
- # else
- (void) openlog (progname, LOG_PID);
- # endif /* LOG_AUTH */
- syslog (LOG_NOTICE, "%s: \"%s %s\"", orguser, command, args);
- #endif USE_SYSLOG
-
- if (special != 0) /* Special characters are not allowed */
- error (ABORT, "'%c' illegal in argument list.", special);
- if ((rslt = system (cmnd)) < 0)
- PERROR (ABORT);
- exit (rslt);
- }
-
- int copy_args (argc, argv, args)
- int argc;
- char **argv;
- char *args;
- {
- char *pa,
- special = 0;
-
- while (argc-- > 0) {
- for (pa = *argv++; *pa != 0; pa++) {
- *args++ = *pa;
- /* Look for special characters and return the first one. */
- if (special == 0) {
- switch (*pa & 0177) {
- case ';':
- case '>':
- case '`':
- case '&':
- case '|':
- case '^':
- case '\n':
- special = *pa; /* We got one */
- }
- }
- }
- *args++ = ' ';
- }
- *args = 0; /* close string */
- return (special);
-
- }
-
- void set_env (name, new_group, new_user)
- char *name,
- *new_group,
- *new_user;
- {
- extern char **environ;
-
- static char envbuf [2048] = {0};
- char *penv = envbuf;
- static char *newenv [16] = {0};
- int next = 0;
-
- #define put_env {newenv [next++] = penv; penv += strlen (penv) + 1;}
-
- (void) sprintf (penv, "IFS= \t\n");
- put_env;
- (void) sprintf (penv, "PATH=/");
- put_env;
- (void) sprintf (penv, "COMMAND=%s", name);
- if (orggroup [0] != 0) {
- (void) sprintf (penv, "ORGGROUP=%s", orggroup);
- put_env;
- }
- if (new_group [0] != 0) {
- (void) sprintf (penv, "GROUP=%s", new_group);
- put_env;
- }
- (void) sprintf (penv, "ORGUSER=%s", orguser);
- put_env;
- if (*new_user == 0) {
- (void) sprintf (penv, "USER=%s", orguser);
- put_env;
- (void) sprintf (penv, "LOGNAME=%s", orguser);
- put_env;
- } else {
- (void) sprintf (penv, "USER=%s", new_user);
- put_env;
- (void) sprintf (penv, "LOGNAME=%s", new_user);
- put_env;
- }
- environ = newenv;
- }
-
- void PERROR (why)
- int why;
- {
- perror (progname);
- if (why == ABORT)
- exit (1);
- }
-
- /* error - barf and quit */
-
- /* VARARGS1 */
-
- void error (va_alist)
- va_dcl
- {
- va_list s;
- int why;
- char *fmt;
-
- va_start(s);
-
- why = va_arg (s, int);
- fmt = va_arg (s, char *);
- (void) fprintf (stderr, "%s: ", progname);
- for (/* void */; *fmt; fmt++) {
- if (*fmt != '%') {
- (void) putc(*fmt,stderr);
- } else if (*++fmt == 's') {
- (void) fputs(va_arg(s,char *),stderr);
- } else if (*fmt == 'c') {
- (void) putc(va_arg(s,int),stderr);
- } else if (*fmt == 'u') {
- (void) fprintf(stderr,"%u",va_arg(s,unsigned));
- } else if (*fmt == 'd') {
- (void) fprintf(stderr,"%d",va_arg(s,int));
- }
- }
- va_end(s);
- (void) fprintf (stderr, "\n");
- if (why != WARN)
- exit (why);
- (void) fflush (stderr);
- }
-