home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / opcom-2.1 / part01 / opcom.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-10  |  13.0 KB  |  594 lines

  1. /*++
  2. /* NAME
  3. /*      opcom 1
  4. /* SUMMARY
  5. /*      execute an operator command.
  6. /* SYNOPSIS
  7. /*    .fi
  8. /*    .B opcom 
  9. /*     command [ arguments ]
  10. /* DESCRIPTION
  11. /*    .I Opcom
  12. /*    enables users belonging to a special group (as defined in /etc/group)
  13. /*    to execute a limited set of commands with another userid (e.g. root)
  14. /*    or groupid. The file
  15. /*    .I XCOMMANDS
  16. /*    describes which commands are allowed for which groups and which
  17. /*    userid (groupid) will be used.
  18. /*    .br
  19. /*    .I Command
  20. /*    is a valid operator command if it matches an entry in
  21. /*    .I XCOMMANDS.
  22. /*    Those entries have the following form:
  23. /*    
  24. /*    path-name : operator-group : [ new-userid ] [ : [ new-groupid ]]
  25. /*    
  26. /*    .I Command
  27. /*    matches an entry, if it is the basename of 
  28. /*    .I path-name
  29. /*    and if at least one of the following conditions holds:
  30. /*    .IP \- 2.4
  31. /*    .I operator-group
  32. /*    is "*".
  33. /*    .IP \- 2.4
  34. /*    The real userid executing
  35. /*    .I opcom
  36. /*    is a member of the group
  37. /*    .I operator-group
  38. /*    (as defined in /etc/group).
  39. /*    .IP \- 2.4
  40. /*    The default group of the real userid, as defined in /etc/passwd,
  41. /*    is equal to
  42. /*    .I operator-group.
  43. /*    .IP \- 2.4
  44. /*    The effective groupid is that of
  45. /*    .I operator-group.
  46. /*    .RE
  47. /*
  48. /*    If
  49. /*    .I command
  50. /*    matches more than one entry, the first matching entry is selected.
  51. /*    This allows multiple levels of privilege.
  52. /*    If no match is found,
  53. /*    .I opcom
  54. /*    aborts with the message "access denied.".
  55. /*
  56. /*    The matching command is executed with the given
  57. /*    .I arguments
  58. /*    unless they contain characters which are special to /bin/sh (e.g. ;).
  59. /*    .br
  60. /*    If
  61. /*    .I new-userid (new-groupid)
  62. /*    is not empty, the effective and real userid (groupid) are set to
  63. /*    .I new-userid (new-groupid).
  64. /*    .br
  65. /*    If
  66. /*    .I new-userid (new-groupid)
  67. /*    is empty, the effective and real userid (groupid)
  68. /*    are set to the real userid (groupid).
  69. /*
  70. /*    The current environment is cleared and the following environment
  71. /*    variables are set:
  72. /*    .IP \- 2.4
  73. /*    COMMAND to
  74. /*    .I command.
  75. /*    .IP \-
  76. /*    ORGGROUP to the group name of the real groupid of the invoker of
  77. /*    .I opcom.
  78. /*    .IP \-
  79. /*    GROUP to the group name of the real groupid executing
  80. /*    .I command.
  81. /*    .IP \-
  82. /*    IFS to blank, tab and new line.
  83. /*    .IP \-
  84. /*    ORGUSER to the login name of the real userid of the invoker of
  85. /*    .I opcom.
  86. /*    .IP \-
  87. /*    PATH to /
  88. /*    .IP \-
  89. /*    USER and LOGNAME to the login name of the real userid executing
  90. /*    .I command.
  91. /*    .RE
  92. /*
  93. /*    If the script 
  94. /*    .I XPROFILE
  95. /*    exists, this script will be executed
  96. /*    within the (Bourne) shell that executes
  97. /*    .I command.
  98. /*    So changes to the environment (e.g. PATH) can be put there.
  99. /*
  100. /*    If
  101. /*    .I opcom
  102. /*    is called with no arguments, a list of valid commands for the
  103. /*    user executing
  104. /*    .I opcom
  105. /*    is printed.
  106. /* FILES
  107. /*    XCOMMANDS
  108. /*    XPROFILE
  109. /*    /etc/group
  110. /*    /etc/passwd
  111. /* CAVEAT
  112. /*    Beware of Trojan horses: don't allow commands with shell escapes.
  113. /* BUGS
  114. /*    Invalid entries in
  115. /*    .I XCOMMANDS
  116. /*    are skipped without warning.
  117. /* DIAGNOSTICS
  118. /*    In case of error
  119. /*    .I opcom
  120. /*    prints an error message on standard error and terminates
  121. /*    with nonzero status.
  122. /*    .br
  123. /*    Commands executed by \fIopcom\fP are optionally reported via syslogd(8).
  124. /* EXAMPLES
  125. /*
  126. /*    
  127. /*    .I XCOMMANDS:
  128. /*
  129. /*    .nf
  130. /*    .ft C
  131. /*    /bin/fsck:operators:root
  132. /*    /bin/dump:operators:root
  133. /*    /bin/kill:operators:root
  134. /*    /etc/lpc:operators:root
  135. /*    /usr/ucb/lprm:operators:root
  136. /*    /bin/mount:operators:root
  137. /*    /bin/restore:operators:root
  138. /*    /bin/shutdown:operators:root
  139. /*    /bin/umount:operators:root
  140. /*    /mgt/cas/bin/menu:ucadmin:root
  141. /*    /usr/local/bin/lp-cancel:bcf:daemon:daemon
  142. /*    /usr/local/bin/lp-move:bcf:daemon:daemon
  143. /*    /usr/local/bin/lp-force:bcf:daemon:daemon
  144. /*    /usr/local/bin/lp-sched:bcf:daemon:daemon
  145. /*    /usr/local/bin/lp-shut:bcf:daemon:daemon
  146. /*    /usr/local/lib/opcom/testenv:*:
  147. /*
  148. /*    .ft
  149. /*    Invocation examples:
  150. /*
  151. /*    opcom lpc start lp
  152. /*    opcom shutdown
  153. /*    opcom lp-shut
  154. /*    opcom testenv
  155. /* AUTHOR(S)
  156. /*
  157. /*      Carel Braam (rccarel@urc.tue.nl)
  158. /*      Eindhoven University of Technology
  159. /*      Computing Centre
  160. /*      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  161. /* BUG REPORTS TO
  162. /*    rccarel@urc.tue.nl
  163. /* CREATION DATE
  164. /*    Wed Jan  4 14:12:59 MET 1989
  165. /* LAST MODIFICATION
  166. /*    Thu Jan 10 10:02:52 MET 1991
  167. /* VERSION/RELEASE
  168. /*    2.1
  169. /*--*/
  170.  
  171. #include <stdio.h>
  172. #include "sysdep.h"
  173. #include <grp.h>
  174. #include <pwd.h>
  175. #include <sys/types.h>
  176. #include <sys/stat.h>
  177. #include <varargs.h>
  178. #include <ctype.h>
  179. #ifdef USE_SYSLOG
  180. #include <syslog.h>
  181. extern int openlog ();
  182. extern void syslog ();
  183. #endif
  184.  
  185. extern void exit ();
  186. extern void perror ();
  187. extern int errno;
  188. extern char *sys_errlist [];
  189. extern int sys_nerr;
  190.  
  191. extern int getegid ();
  192. extern int geteuid ();
  193. extern int getgid ();
  194. extern int setuid ();
  195. extern struct group *getgrgid ();
  196. extern struct group *getgrnam ();
  197. extern struct passwd *getpwnam ();
  198. extern struct passwd *getpwuid ();
  199. extern char *malloc ();
  200. extern int system ();
  201.  
  202. #define WARN    0
  203. #define ABORT    1
  204.  
  205. struct command {
  206.     char name [64];
  207.     char path [1024];
  208.     char group [32];
  209.     char new_group [32];
  210.     char new_user [32];
  211. };
  212.  
  213. void PERROR ();
  214. int check_group ();
  215. int copy_args ();
  216. void display_commands ();
  217. void error ();
  218. void exec_cmnd ();
  219. struct command *get_command ();
  220. struct command *parse_command ();
  221.  
  222. FILE *command_file;
  223. int my_egid,
  224.     my_gid,
  225.     my_euid,
  226.     my_uid;
  227. char orguser [32] = {0};
  228. char orggroup [32] = {0};
  229. char *nextitem ();
  230. void set_env ();
  231. void set_ids ();
  232.  
  233. char *progname;                /* used for diagnostics */
  234.  
  235. main (argc, argv)
  236. int argc;
  237. char **argv;
  238. {
  239.     int fd;
  240.     struct command *command;
  241.     struct group *group;
  242.     struct passwd *passwd;
  243.  
  244.     /* close all file descriptors > 2 */
  245.     for (fd = 3; fd <= _NFILE; fd++)
  246.     (void) close (fd);
  247.     if (progname = strrchr (*argv, '/'))
  248.     progname++;
  249.     else
  250.     progname = *argv;
  251.     argv++;
  252.     argc--;
  253.  
  254.     my_euid = geteuid();
  255.     if (my_euid != 0)
  256.     error (WARN, "Warning: the effective userid is not root.");
  257.     my_uid = getuid();
  258.     if ((passwd = getpwuid (my_uid)) == NULL)
  259.     error (ABORT, "cannot find passwd entry for userid %d.", my_uid);
  260.     (void) strcpy (orguser, passwd->pw_name);
  261.  
  262.     my_egid = getegid();
  263.     my_gid = getgid();
  264.     if ((group = getgrgid (my_gid)) != NULL)
  265.     (void) strcpy (orggroup, group->gr_name);
  266.  
  267.     if ((command_file = fopen (COMMANDS, "r")) == NULL)
  268.     error (ABORT, "cannot open %s for reading.", COMMANDS);
  269.     if (argc == 0) {
  270.     display_commands ();
  271.     exit (0);
  272.     }
  273.  
  274.     command = get_command (*argv);
  275.     set_ids (command->new_user, command->new_group);
  276.     (void) fclose (command_file);
  277.     exec_cmnd (command, argc-1, argv+1);
  278.     return (0);    /* makes lint happy    */
  279. }
  280.  
  281. void display_commands ()
  282. {
  283.     struct command *command;
  284.     int first = 1;
  285.     int printit;
  286.  
  287.     while ((command = parse_command ()) != NULL) {
  288.     if (my_uid == 0) {    /* root has access to all commands */
  289.         printit = 1;
  290.     } else {
  291.         printit = check_group (command->group);
  292.     }
  293.     if (printit) {
  294.         if (first) {
  295.         first = 0;
  296.         (void) printf ("Valid opcom commands are:\n\n");
  297.         }
  298.         (void) printf ("%s (group %s)\n", command->name, command->group);
  299.     }
  300.     }
  301.     if (first)    /* We didn't find valid commands    */
  302.     error (ABORT, "access denied.");
  303. }
  304.  
  305.  
  306. struct command *get_command (name)
  307. char *name;
  308. {
  309.     struct command *command;
  310.  
  311.     while ((command = parse_command ()) != NULL) {
  312.     if (strcmp (name, command->name) == 0) {
  313.         if (my_uid == 0)    /* root has access to all commands */
  314.         return (command);
  315.         if (check_group (command->group))
  316.         return (command);
  317.     }
  318.     }
  319.     error (ABORT, "access denied.");
  320.     return (NULL);
  321. }
  322.  
  323. struct command *parse_command ()
  324. {
  325.     static char cmndbuf [BUFSIZ];
  326.     static struct command command;
  327.     char ch,
  328.      *cmnd,
  329.      *pc,
  330.      *ptail,
  331.      *pt;
  332.  
  333.     while (fgets (cmndbuf, BUFSIZ, command_file) != NULL) {
  334.     for (cmnd = cmndbuf; isspace (*cmnd); cmnd++);
  335.     pt = command.path;
  336.     ptail = cmnd;
  337.     while (! (isspace (ch = *ptail) || (ch == ':') || (ch == 0))) {
  338.         *pt++ = ch;
  339.         ptail++;
  340.     }
  341.     *pt = 0;
  342.     while (isspace (*ptail))
  343.         ptail++;
  344.     if (*ptail != ':')    /* invalid entry    */
  345.         continue;
  346.     pt = command.path;
  347.     if ((pc = strrchr (pt, '/')) == NULL)
  348.         pc = pt;
  349.     else
  350.         pc++;
  351.     (void) strcpy (command.name, pc);
  352.     ptail = nextitem (ptail+1, command.group);
  353.     ptail = nextitem (ptail, command.new_user);
  354.     ptail = nextitem (ptail, command.new_group);
  355.     return (&command);
  356.     }
  357.     return (NULL);
  358. }
  359.  
  360. char *nextitem (pstart, target)
  361. char *pstart,
  362.      *target;
  363. {
  364.     char ch,
  365.      *ps = pstart,
  366.      *pt = target;
  367.  
  368.     while (isspace (*ps))
  369.         ps++;
  370.     if (*ps == 0) {
  371.         *pt = 0;
  372.         return (ps);
  373.     }
  374.     for (ch = *ps; (ch != ':') & (ch != 0); ch = *++ps)
  375.         *pt++ = ch;
  376.     *pt-- = 0;
  377.     for (ch = *pt; isspace (ch); ch = *pt--)
  378.         *pt = 0;
  379.     if (*ps == 0)
  380.         return (ps);
  381.     else
  382.         return (ps+1);
  383. }
  384.  
  385. int check_group (groupname)
  386. char *groupname;
  387. {
  388.     char **gr_list;
  389.     struct group *group;
  390.  
  391.     if (strcmp (groupname, "*") == 0)  /* matches everything */
  392.     return (1);
  393.     if ((group = getgrnam (groupname)) != NULL) {
  394.     if (my_egid == group->gr_gid)    /* effective groupid */
  395.         return (1);
  396.     for (gr_list = group->gr_mem; *gr_list != NULL; gr_list++) {
  397.         if (strcmp (orguser, *gr_list) == 0)
  398.         return (1);
  399.     }
  400.     }
  401.     return (0);
  402. }
  403.  
  404. void set_ids (new_user, new_group)
  405. char *new_user,
  406.      *new_group;
  407. {
  408.     struct group *group;
  409.     struct passwd *passwd;
  410.  
  411.     if (*new_group != 0) {
  412.     /* not empty, must be set before uid is set     */
  413.     if ((group = getgrnam (new_group)) == NULL)
  414.         error (ABORT, "cannot find group entry for groupid %s.", new_group);
  415.     if (setgid (group->gr_gid) < 0)
  416.         PERROR (ABORT);
  417.     }
  418.     if (*new_user != 0) {    /* not empty     */
  419.     if ((passwd = getpwnam (new_user)) == NULL)
  420.         error (ABORT, "cannot find passwd entry for userid %s.", new_user);
  421.     if (setuid (passwd->pw_uid) < 0)
  422.         PERROR (ABORT);
  423.     } else if (setuid (getuid ()) < 0)
  424.     PERROR (ABORT);
  425. }
  426.  
  427. void exec_cmnd (command, argc, argv)
  428. struct command *command;
  429. int argc;
  430. char **argv;
  431. {
  432.     unsigned cmnd_size = 0;
  433.     int i,
  434.     rslt;
  435.     struct stat prstat;
  436.     char 
  437.      special,
  438.      *args,
  439.      *cmnd,
  440.      *pa,
  441.      *pc;
  442.  
  443.     set_env (command->name, command->new_group, command->new_user);
  444.     cmnd_size = strlen (command->path)+32;
  445.     for (i = 0; i < argc; i++)
  446.     cmnd_size += strlen (argv [i])+1;
  447.     pc = cmnd = malloc (cmnd_size+strlen (PROFILE));
  448.     if ((stat (PROFILE, &prstat) == 0) && (prstat.st_mode & 0400)) {
  449.     /* We must execute the profile */
  450.     (void) sprintf (pc, ". %s;", PROFILE);
  451.     pc += strlen (pc);
  452.     }
  453.     pa = command->path;
  454.     while (*pa != 0)
  455.     *pc++ = *pa++;
  456.     *pc++ = ' ';
  457.     args = pc;
  458.     special = copy_args (argc, argv, args);
  459.  
  460. #ifdef USE_SYSLOG
  461. # ifdef LOG_AUTH
  462.     (void) openlog (progname, LOG_PID, LOG_AUTH);
  463. # else
  464.     (void) openlog (progname, LOG_PID);
  465. # endif /* LOG_AUTH */
  466.     syslog (LOG_NOTICE, "%s: \"%s %s\"", orguser, command, args);
  467. #endif USE_SYSLOG
  468.  
  469.     if (special != 0)    /* Special characters are not allowed    */
  470.     error (ABORT, "'%c' illegal in argument list.", special);
  471.     if ((rslt = system (cmnd)) < 0)
  472.     PERROR (ABORT);
  473.     exit (rslt);
  474. }
  475.  
  476. int copy_args (argc, argv, args)
  477. int argc;
  478. char **argv;
  479. char *args;
  480. {
  481.     char *pa,
  482.      special = 0;
  483.  
  484.     while (argc-- > 0) {
  485.     for (pa = *argv++; *pa != 0; pa++) {
  486.         *args++ = *pa;
  487.         /* Look for special characters and return the first one.    */
  488.         if (special == 0) {
  489.         switch (*pa & 0177) {
  490.         case ';':
  491.         case '>':
  492.         case '`':
  493.         case '&':
  494.         case '|':
  495.         case '^':
  496.         case '\n':
  497.             special = *pa; /* We got one */
  498.         }
  499.         }
  500.     }
  501.     *args++ = ' ';
  502.     }
  503.     *args = 0;    /* close string    */
  504.     return (special);
  505.  
  506. }
  507.  
  508. void set_env (name, new_group, new_user)
  509. char *name,
  510.      *new_group,
  511.      *new_user;
  512. {
  513.     extern char **environ;
  514.  
  515.     static char envbuf [2048] = {0};
  516.     char *penv = envbuf;
  517.     static char *newenv [16] = {0};
  518.     int next = 0;
  519.  
  520. #define put_env {newenv [next++] = penv; penv += strlen (penv) + 1;}
  521.  
  522.     (void) sprintf (penv, "IFS= \t\n");
  523.     put_env;
  524.     (void) sprintf (penv, "PATH=/");
  525.     put_env;
  526.     (void) sprintf (penv, "COMMAND=%s", name);
  527.     if (orggroup [0] != 0) {
  528.     (void) sprintf (penv, "ORGGROUP=%s", orggroup);
  529.     put_env;
  530.     }
  531.     if (new_group [0] != 0) {
  532.     (void) sprintf (penv, "GROUP=%s", new_group);
  533.     put_env;
  534.     }
  535.     (void) sprintf (penv, "ORGUSER=%s", orguser);
  536.     put_env;
  537.     if (*new_user == 0) {
  538.     (void) sprintf (penv, "USER=%s", orguser);
  539.     put_env;
  540.     (void) sprintf (penv, "LOGNAME=%s", orguser);
  541.     put_env;
  542.     } else {
  543.     (void) sprintf (penv, "USER=%s", new_user);
  544.     put_env;
  545.     (void) sprintf (penv, "LOGNAME=%s", new_user);
  546.     put_env;
  547.     }
  548.     environ = newenv;
  549. }
  550.  
  551. void PERROR (why)
  552. int why;
  553. {
  554.     perror (progname);
  555.     if (why == ABORT)
  556.     exit (1);
  557. }
  558.  
  559. /* error - barf and quit */
  560.  
  561. /* VARARGS1 */
  562.  
  563. void error (va_alist)
  564. va_dcl
  565. {
  566.     va_list s;
  567.     int why;
  568.     char *fmt;
  569.  
  570.     va_start(s);
  571.  
  572.     why = va_arg (s, int);
  573.     fmt = va_arg (s, char *);
  574.     (void) fprintf (stderr, "%s: ", progname);
  575.     for (/* void */; *fmt; fmt++) {
  576.     if (*fmt != '%') {
  577.         (void) putc(*fmt,stderr);
  578.     } else if (*++fmt == 's') {
  579.         (void) fputs(va_arg(s,char *),stderr);
  580.     } else if (*fmt == 'c') {
  581.         (void) putc(va_arg(s,int),stderr);
  582.     } else if (*fmt == 'u') {
  583.         (void) fprintf(stderr,"%u",va_arg(s,unsigned));
  584.     } else if (*fmt == 'd') {
  585.         (void) fprintf(stderr,"%d",va_arg(s,int));
  586.     }
  587.     }
  588.     va_end(s);
  589.     (void) fprintf (stderr, "\n");
  590.     if (why != WARN)
  591.     exit (why);
  592.     (void) fflush (stderr);
  593. }
  594.