home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume15 / surun < prev    next >
Encoding:
Text File  |  1989-01-13  |  14.6 KB  |  613 lines

  1. Subject:  v15i034:  Run commands as another (or super) user
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4.  
  5. Submitted-by: dirk <unido!altger!dirk>
  6. Posting-number: Volume 15, Issue 34
  7. Archive-name: surun
  8.  
  9. WHAT IT DOES:
  10.     Surun allows a user to run a command under root or another
  11.     user's privilege without typing the user's password. Permission
  12.     to use surun is given by a password file, usage is logged to
  13.     a logfile.
  14.  
  15. WHERE IT RUNS:
  16.     If use surun since 3/4 year under System V rel. 3.
  17.     If you plan to run it under BSD or XENIX just undefine the
  18.     TERMIO flag. If have not been testing surun under BSD or
  19.     XENIX for a long time now but it will run as well. 
  20.  
  21. WHAT TO DO:
  22.     I am to lazy to correct my manual page, if someone who's native
  23.     language is english could to so pls. send it back to me.
  24.  
  25. cu,
  26. Dirk Koeppen, DK-SOFT, West Germany
  27.  
  28. ------------------------> CUT HERE <------------------------
  29.  
  30. :    !/bin/sh
  31. # To unbundle, sh(ell) this file
  32. # ==============================
  33. echo README 1>&2
  34. cat >README <<'End of ===README==='
  35. #ident @(#) README v1.2 of 88/10/03 , dirk
  36. # This is Public-Domain. Military usage or resale forbidden.
  37. # 6.Jun'87, dirk ( .. unido!cosa!dirk )
  38.  
  39. WHAT IT DOES:
  40.     Surun allows a user to run a command under root or another
  41.     user's privilege without typing the user's password. Permission
  42.     to use surun is given by a password file, usage is logged to
  43.     a logfile.
  44.  
  45. WHERE IT RUNS:
  46.     If use surun since 3/4 year under System V rel. 3.
  47.     If you plan to run it under BSD or XENIX just undefine the
  48.     TERMIO flag. If have not been testing surun under BSD or
  49.     XENIX for a long time now but it will run as well. 
  50.  
  51. WHAT TO DO:
  52.     I am to lazy to correct my manual page, if someone who's native
  53.     language is english could to so pls. send it back to me.
  54.  
  55. cu,
  56. dirk :-)
  57.  
  58. Dirk Koeppen, DK-SOFT, West Germany
  59. End of ===README===
  60. echo Makefile 1>&2
  61. cat >Makefile <<'End of ===Makefile==='
  62. #ident @(#) Makefile v1.2 of 88/10/03 , dirk
  63. # Makefile for surun
  64. # This is Public-Domain. Military usage or resale forbidden.
  65. # 6.Jun'87, dirk ( .. unido!cosa!dirk )
  66.  
  67. SHELL        =/bin/sh
  68. CFLAGS        =-O -s -DTERMIO
  69. CLIBS        =-lc_s
  70. BINDIR        =/usr/local/bin
  71. MAN        =nroff -man
  72. MANDIR        =/usr/man/u_man/man1
  73. CATMANDIR    =/usr/catman/u_man/man1
  74. LOGFILE        =/usr/adm/surun.log
  75. ALLOWFILE    =/usr/adm/surun.allow
  76. SILENTCC    =YES
  77.  
  78. surun:    surun.c
  79.     cc $(CFLAGS) surun.c -o surun $(CLIBS)
  80.  
  81. # have to be root to install
  82. install:
  83.     make surun
  84.     mv ./surun $(BINDIR)/surun
  85.     chown root $(BINDIR)/surun
  86.     chgrp bin  $(BINDIR)/surun
  87.     chmod 4751 $(BINDIR)/surun
  88.     cp ./surun.1M $(MANDIR)/surun.1M
  89.     $(MAN) surun.1M > $(CATMANDIR)/surun.1M
  90.     pack -f $(CATMANDIR)/surun.1M
  91.     cp /dev/null $(LOGFILE)
  92.     chmod 640 $(LOGFILE)
  93.     echo "root::\nbin::" > $(ALLOWFILE)
  94.     chmod 640 $(ALLOWFILE)
  95.  
  96. clean:
  97.     rm -f foo Mout Lout a.out core *.i *.obj *.o
  98.     rm -f surun surun.allow surun.log surun.tmp
  99. End of ===Makefile===
  100. echo surun.c 1>&2
  101. cat >surun.c <<'End of ===surun.c==='
  102. char SCCS_id[]= "@(#) surun.c v2.2 of 88/10/03 , dirk";
  103.  
  104. /*
  105.  * SURUN
  106.  * run programs under root id
  107.  * usuage: surun [-] [-u username] [-p] .. command ..
  108.  *
  109.  * This is Public-Domain. Military usage or resale forbitten.
  110.  * 6.Jun'87, dirk ( .. unido!cosa!dirk )
  111.  */
  112.  
  113. #include <sys/types.h>
  114. #include <ctype.h>
  115. #include <stdio.h>
  116. #include <pwd.h>
  117. #include <time.h>
  118. #include <string.h>
  119. #include <signal.h>
  120.  
  121. #ifdef TERMIO
  122. # include <termio.h>
  123. #else
  124. # include <sgtty.h>
  125. #endif /* TERMIO */
  126.  
  127. #ifndef MAX_ARG
  128. # define MAX_ARG    1024
  129. #endif
  130.  
  131. #define    ALLOWFILE    "/usr/adm/surun.allow"
  132. #define    LOGFILE        "/usr/adm/surun.log"
  133. #define    TEMPFILE    "/usr/adm/surun.tmp"
  134. #define    ALLOWMASK    0640
  135.  
  136. #define CONSOLE        "/dev/console"
  137. #define SHELL        "/bin/sh"
  138.  
  139. #define    USAGE( str ) \
  140. { \
  141.     fprintf (stderr, "surun: %s\n", str ); \
  142.     fprintf (stderr, \
  143.          "usage: surun [-] [-u username] [-p] .. command ..\n"); \
  144.     exit (1); \
  145. } \
  146.  
  147. #define    ERROR( str ) \
  148. { \
  149.     fprintf (stderr, "surun: %s\n", str ); \
  150.     exit (1); \
  151. } \
  152.  
  153. #define    PERROR( str ) \
  154. { \
  155.     perror ( str ); \
  156.     exit (1); \
  157. } \
  158.  
  159. struct passwd    *getpwnam ();
  160. char        *crypt (), *getlogin (), *ttyname ();
  161. long        time ();
  162. void        exit ();
  163.  
  164. char    log_string[64];
  165. char    env_shell[64]=        "SHELL=";
  166. char    env_home[64]=        "HOME=";
  167. char    env_logname[17]=    "LOGNAME=";
  168. char    env_mail[64]=        "MAIL=/usr/mail/";
  169. char    env_path1[128]=        "PATH=:/bin:/usr/bin";    
  170. char    env_path2[128]=        "PATH=:/bin:/usr/bin:/etc";    
  171. char    username[64];
  172. char    *userpasswd;
  173. int    userretry;
  174.  
  175. main(argc, argv)
  176. char **argv;
  177. {
  178.     char        *user= "root"   , *file,
  179.             *Argv[ MAX_ARG ], *environ[6];
  180.     int        i, lflag= 0, pflag= 0;
  181.     long        clock;
  182.     struct tm    *date;
  183.     struct passwd    *user_pwd;
  184.  
  185.     /* check'n lookup args */
  186.     while ((**(++argv) == '-') && (*(*argv + 1) != '-'))
  187.         switch (*(*argv + 1))
  188.         {
  189.             case 'u'      :    user= *++argv;
  190.                     if (!user)
  191.                          USAGE( "no username" );
  192.                     break;
  193.             case 'p'      : pflag++; break;
  194.             case 'V'      : printf ("%s\n", SCCS_id);
  195.                     exit (0);
  196.             case NULL     :    lflag++; break;
  197.             default          : USAGE( "not a valid option" );
  198.         }
  199.     if (!*argv && !pflag)
  200.         USAGE( "no command" );
  201.  
  202.     /* create the time stamp */
  203.     clock= time ((long *)0);
  204.     date= gmtime (&clock);
  205.     sprintf (log_string, "SURUN %02d/%02d %02d:%02d + %s %s-%s",
  206.          date->tm_mday, date->tm_mon + 1, date->tm_hour, date->tm_min,
  207.          (strrchr (ttyname (0), '/') + 1), getlogin (), user);
  208.     
  209.     /* do we have an valid entry in the allowfile ? */
  210.     if (!check_permission(getlogin ()) || !userretry)
  211.     {
  212.         *strchr (log_string, '+')= '-';
  213.         log(argv);
  214.         if (userretry)
  215.             ERROR( "no permission" )
  216.         else
  217.             ERROR( "number of password-retries exeeded" )
  218.     }
  219.  
  220.     /* log it and change id*/
  221.     log(argv);
  222.     if (pflag)
  223.     {
  224.         setpasswd();
  225.         if (!*argv)
  226.             exit (0);
  227.     }
  228.     if ((user_pwd= getpwnam (user)) == NULL)
  229.         ERROR( "invalid username" );
  230.     if (setgid (user_pwd->pw_gid))
  231.         PERROR( user_pwd->pw_gid );
  232.     if (setuid (user_pwd->pw_uid))
  233.         PERROR( user_pwd->pw_uid );
  234.     
  235.     /* execute it */
  236.     if (lflag)
  237.     {
  238.         file= user_pwd->pw_shell;
  239.         if (!*file)
  240.             file= SHELL;
  241.         environ[0]= strcat (env_shell, file);
  242.         environ[1]= strcat (env_home, user_pwd->pw_dir);
  243.         environ[2]= strcat (env_logname, user_pwd->pw_name);
  244.         environ[3]= strcat (env_mail, user_pwd->pw_name);
  245.         if (user_pwd->pw_uid)
  246.             environ[4]= env_path1;
  247.         else
  248.             environ[4]= env_path2; 
  249.         environ[5]= NULL;
  250.         i= 0;
  251.         /* Argv[i++]= strrchr (file, '/') + 1; */
  252.         Argv[i++]= "-";
  253.         Argv[i++]= "-c";
  254.         while (Argv[i++]= *(argv++));
  255.         if (chdir (user_pwd->pw_dir) == -1)
  256.             PERROR( user_pwd->pw_dir );
  257.         if (execve (file, Argv, environ))
  258.             PERROR( file );
  259.     }
  260.     else
  261.     {
  262.         file= *argv;
  263.         if (execvp (file, argv))
  264.             PERROR( file );
  265.     }
  266.     
  267.     /* unreached */
  268.     exit (0);
  269. }
  270.  
  271.  
  272. /*
  273.  * CHECK_PERMISSION
  274.  * check if the user has permissions and left retries to execute surun
  275.  */
  276. int
  277. check_permission(lognam)
  278. char    *lognam;
  279. {
  280.     FILE        *allowfile;
  281.     char        passwd[3];
  282.     int        ok_flag= 0;
  283.  
  284.     if (!(allowfile= fopen (ALLOWFILE, "r")))
  285.         ERROR( "cannot open the allow file" );
  286.  
  287.     while (fgets (username, 64, allowfile) && !ok_flag)
  288.     {
  289.         if (isdigit (*(strrchr (username, ':') + 1)))
  290.             userretry= atoi (strrchr (username, ':') + 1);
  291.         else
  292.             userretry= 1;
  293.         userpasswd= strchr (username, ':') + 1;
  294.         *strrchr (username, ':')= NULL;    
  295.         *(userpasswd - 1)= NULL;    
  296.         if (!strcmp (username, lognam))
  297.             if (*userpasswd && userretry)
  298.             {
  299.                 printf ("passwd: ");
  300.                 raw(1);
  301.                 passwd[0]= getchar ();
  302.                 passwd[1]= getchar ();
  303.                 raw(0);
  304.                 passwd[2]= 0;
  305.                 putchar ('\n');
  306.                 if (!strcmp (crypt (passwd, userpasswd),
  307.                                 userpasswd))
  308.                     ok_flag++;
  309.                 else
  310.                 {
  311.                     userretry--;
  312.                     fclose (allowfile);
  313.                     update();
  314.                     return (ok_flag);
  315.                 }
  316.             }
  317.             else
  318.                 ok_flag++;
  319.     }
  320.     fclose (allowfile);
  321.     return (ok_flag);
  322. }
  323.  
  324.  
  325. /*
  326.  * LOG
  327.  * log to console and logfile
  328.  */
  329. log(cmd)
  330. char    **cmd;
  331. {
  332.     FILE    *logfile, *console;
  333.  
  334.     if (!(logfile= fopen (LOGFILE, "a+")))
  335.         ERROR( "cannot open the logfile" );    
  336.  
  337.     if (console= fopen (CONSOLE , "w"))
  338.     {
  339.         fprintf (console, "%s\n", log_string);
  340.         fclose (console);
  341.     }
  342.  
  343.     fprintf (logfile, "%s :", log_string);
  344.     while (*cmd)
  345.         fprintf (logfile, " %s", *(cmd++));
  346.     fprintf (logfile, "\n");
  347.     fclose (logfile);
  348. }
  349.  
  350.  
  351. /*
  352.  * RAW
  353.  * 1: set terminal to raw mode and no echo
  354.  * 0: reset old terminal status
  355.  */
  356. raw(mode)
  357. int    mode;
  358. {
  359. #ifdef TERMIO
  360.     static struct termio    t_save;
  361.     struct termio        t;
  362.  
  363.     if (mode)
  364.     {
  365.         ioctl (2, TCGETA, &t);
  366.         t_save= t;
  367.         t.c_lflag&= ~(ICANON|ECHO);
  368.         t.c_cc[VMIN]= 1;
  369.         t.c_cc[VTIME]= 0;
  370.     }
  371.     else
  372.         t= t_save;
  373.     ioctl (2, TCSETAW, &t);
  374. #else
  375.     static struct sgttyb    t_save;
  376.     struct sgttyb        t;
  377.  
  378.     if (mode)
  379.     {
  380.         ioctl (2, TIOCGETP, &t);
  381.         t_save= t;
  382.         t.sg_flags|= CBREAK;
  383.         t.sg_flags&= ~ECHO;
  384.     }
  385.     else
  386.         t= t_save;
  387.     ioctl (2, TIOCSETN, &t);
  388. #endif /* TERMIO */
  389. }    
  390.  
  391.  
  392. /*
  393.  * SETPASSWD 
  394.  * get a new passwd and crypt it
  395.  */
  396. setpasswd()
  397. {
  398.     long    salt;
  399.     char    salt_c[2], passwd[3];
  400.     int    i;
  401.  
  402.     printf ("New Password: ");
  403.     raw(1);
  404.     passwd[0]= getchar ();
  405.     passwd[1]= getchar ();
  406.     raw(0);
  407.     passwd[2]= 0;
  408.     putchar ('\n');
  409.     userretry= 0;
  410.     while ((userretry < 1) || (userretry > 9))
  411.     {
  412.         printf ("Number of retries (1-9): ");
  413.         scanf ("%d", &userretry);
  414.     }
  415.     time (&salt);
  416.     salt*= getpid ();
  417.     salt_c[0]= salt & 0x3f;
  418.     salt_c[1]= salt>>6 & 0x3f;    
  419.     for (i= 0; i < 2; i++)    
  420.     {
  421.         salt_c[i]+= '.';
  422.         if (salt_c[i] > '9')
  423.             salt_c[i]+= 'A' - ':';
  424.         if (salt_c[i] > 'Z')
  425.             salt_c[i]+= 'a' - '[';
  426.     } 
  427.     userpasswd= crypt (passwd, salt_c);
  428.     update();
  429. }
  430.  
  431.  
  432. /*
  433.  * UPDATE
  434.  * update the surun.allow file with the current user data
  435.  */
  436. update()
  437. {
  438.     FILE    *in, *out;
  439.     char    line[64];
  440.  
  441.     if (!(in= fopen (ALLOWFILE, "r")))
  442.         ERROR( "cannot open allowfile" )
  443.     if (!(out= fopen (TEMPFILE, "w")))
  444.         ERROR( "cannot open tempfile" )
  445.     while (fgets (line, 64, in))
  446.     {
  447.         if (!strcmp (strtok (line, ":"), username))
  448.             fprintf (out, "%s:%s:%d\n", username,
  449.                             userpasswd, userretry);
  450.         else
  451.         {
  452.             *(line+strlen (line))= ':';
  453.             fputs (line, out);
  454.         }
  455.     }
  456.     fclose (in);
  457.     fclose (out);
  458.     chmod (TEMPFILE, ALLOWMASK);
  459.     signal (SIGHUP, SIG_IGN);
  460.     signal (SIGINT, SIG_IGN);
  461.     signal (SIGQUIT, SIG_IGN);
  462.  
  463. #ifdef SIGSTP
  464.     signal (SIGSTP, SIG_IGN);
  465. #endif /* SIGSTP */
  466.  
  467.     unlink (ALLOWFILE);
  468.     if (link (TEMPFILE, ALLOWFILE))
  469.         ERROR( "cannot link tempfile to allowfile" );
  470.     unlink (TEMPFILE);
  471.     signal (SIGHUP, SIG_DFL);
  472.     signal (SIGINT, SIG_DFL);
  473.     signal (SIGQUIT, SIG_DFL);
  474.  
  475. #ifdef SIGSTP
  476.     signal (SIGSTP, SIG_DFL);
  477. #endif /* SIGSTP */
  478.  
  479. }
  480. End of ===surun.c===
  481. echo surun.1M 1>&2
  482. cat >surun.1M <<'End of ===surun.1M==='
  483. .\" @(#) surun.1M.sh v1.2 of 88/10/03 , dirk
  484. .TH SURUN 1M
  485. .SH NAME
  486. surun - run a command under root or another user's privilege 
  487. .SH SYNOPSIS
  488. surun [-] [-u username] [-p] .. command ..
  489. .SH OPTIONS
  490. .sp
  491. -            login as the specified user
  492. .sp
  493. -u username    set the userid to the one of user 'username'
  494. .sp
  495. -p            set password and retry-counter
  496. .sp
  497. command        commands to be executed
  498. .SH DESCRIPTION
  499. \fISurun\fR allows one to run a program under root or another 
  500. user's privilege without logging off or typing the user's
  501. password. The default username is root (i.e., superuser).
  502. For security reasons the user can set a short password of 2 characters
  503. as well.
  504. .PP
  505. To use \fIsurun\fR, the user must have permission to execute the
  506. \fIsurun\fR command. This is provided by an entry of the user's logname in the
  507. \fI/usr/adm/surun.allow\fR file. If the user has this permission,
  508. \fIsurun\fR executes the command or a new shell with the
  509. \fIreal\fR user and group ID set to that of the specified user.
  510. For security reasons the user has the possibility to setup a \fIpassword\fR
  511. with a length of 2 characters. If a \fIpassword\fI is set, \fIsurun\fR prompts
  512. for it first, this is done in 'cbreak/ no echo mode' so that the user does not
  513. need to press <RETURN> or <EOF>. A misstyped password causes \fIsurun\fR to
  514. decrement a counter in the \fI/usr/adm/surun.allow\fR file. If this counter
  515. reaches 0, \fIsurun\fR aborts every time it is called. The super-user must
  516. reset the counter to activate \fIsurun\fR for the misstyping user again.
  517. .sp
  518. The normal user ID is restored just after the \fIsurun\fR 
  519. command quits. Any additional arguments given on the command 
  520. line are passed to the program invoked by surun.
  521. .PP
  522. If the first argument to \fIsurun\fR is a -, the environment is changed
  523. to what would be expected if the user actually logged in as the
  524. specified user. This is done by invoking the program used as the
  525. shell with an arg0 value whose first character is -, thus
  526. causing first the system profile (/etc/profile, /etc/cshrc ) and then the
  527. specified user profile (.profile, .cshrc, .login in the new users $HOME
  528. directory) to be executed. Otherwise, the environment is passed along
  529. with the possible exception of $PATH, which is set to /bin:/usr/
  530. bin:/etc for root.
  531. .PP
  532. All attempts to execute a command via \fIsurun\fR are logged in the
  533. logfile \fI/usr/adm/surun.log\fR and the device \fI/dev/console\fR.
  534. The user's logname has to be an entry in the \fI/usr/adm/surun.allow\fR
  535. file otherwise surun will not execute.
  536. .SH FILE FORMAT
  537. \fI/usr/adm/surun.log\fR:
  538. .in +4
  539. .nf
  540. .sp
  541. SURUN 28/01 22:27 + vt01 dirk-root : ls
  542.        |      |   |  |    |    |      |
  543. date --+      |   |  |    |    |      +-- command executed 
  544. time ---------+   |  |    |    +--------- setuid to username
  545. valid   (+) ------+  |    +-------------- username 
  546. invalid (-)          |
  547. ttyname -------------+
  548. .sp
  549. .fi
  550. .in
  551. \fI/usr/adm/surun.log\fR:
  552. .in +4
  553. .nf
  554. .sp
  555. dirk:IX2.swq4zvjvU:2
  556.  |        |        |
  557.  |        |        +-- number of left retries for password
  558.  |        +----------- crypted password
  559.  +-------------------- users logname
  560. .sp
  561. .fi
  562. A missing password disables the password check only if the number of left
  563. retries is NULL (e.g. dirk::) or not 0 (e.g. dirk::1).
  564. .sp
  565. A missing retry-number is interpreted as 1 if there is no password set
  566. .PP
  567. .SH EXAMPLES
  568. To execute the command 'ls /usr/adm' under the user 'bin' its 
  569. privileges while retaining your previously exported environment,
  570. execute:
  571. .PP
  572. .in +12
  573. surun -u bin ls /usr/adm
  574. .PP
  575. To execute the same command but change the environment to what would
  576. be expected if 'bin' had originally logged in, execute:
  577. .PP
  578. .in +12
  579. surun - -u bin ls /usr/adm
  580. .PP
  581. To set a password, execute:
  582. .PP
  583. .in +12
  584. surun -p
  585. .SH FILES
  586. .nf
  587. /etc/passwd           system password file
  588. /usr/adm/surun.log    log file
  589. /usr/adm/surun.allow  list of users allowed to execute surun
  590. /usr/adm/surun.tmp    temporary file for updating the surun.allow file
  591. .if
  592. .PP
  593. .SH SEE ALSO
  594. su(1M), env(1), login(1), sh(1) \fIin the User's Reference Manual. \fR
  595. passwd(4), profile(4), environ(5) \fIin the Programmer's Reference Manual.\fR
  596. .SH CAVEATS
  597. Since the sh(1) which calls surun is doing the wildcarding, be aware of
  598. wildcards to directories which are not readable by you. Pass these wildcards
  599. using quotes.
  600. .sp
  601. Example:
  602. .in +4
  603. /usr/lib is not readable for you.
  604. .br
  605. entering 'surun vi /usr/lib/uucp/L.*' will result in '/usr/lib/uucp' and not
  606. in '/usr/lib/uucp/L.sys' due /usr/lib/uucp is not readable by you. Use the
  607. command 'surun vi "/usr/lib/uucp/L.*"' instead.
  608. .in -4
  609. .SH AUTHOR
  610. Dirk Koeppen, DK-SOFT, West Germany, .. unido!cosa!dirk
  611. End of ===surun.1M===
  612. exit 0
  613.