home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / shadow / part06 / newgrp.c < prev    next >
C/C++ Source or Header  |  1993-08-14  |  13KB  |  583 lines

  1. /*
  2.  * Copyright 1990, 1991, 1992, 1993, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  *
  11.  * This software is provided on an AS-IS basis and the author makes
  12.  * no warrantee of any kind.
  13.  */
  14.  
  15. #include <sys/types.h>
  16. #ifndef    BSD
  17. #include <string.h>
  18. #else
  19. #include <strings.h>
  20. #endif
  21. #include <stdio.h>
  22. #include <grp.h>
  23. #include "pwd.h"
  24. #include <termio.h>
  25. #ifdef SYS3
  26. #include <sys/ioctl.h>
  27. #endif
  28. #include "config.h"
  29.  
  30. #if !defined(BSD) && !defined(SUN) && !defined(SUN4)
  31. #define    bzero(p,n) memset(p, 0, n)
  32. #endif
  33.  
  34. #ifndef    lint
  35. static    char    sccsid[] = "@(#)newgrp.c    3.11    07:34:23    08 Apr 1993";
  36. #endif
  37.  
  38. #ifdef    NGROUPS
  39. int    ngroups;
  40. gid_t    groups[NGROUPS];
  41. #endif
  42.  
  43. char    *getpass();
  44. char    *getenv();
  45. char    *pw_encrypt();
  46. struct    passwd    *pwd;
  47. struct    passwd    *getpwuid();
  48. struct    passwd    *getpwnam();
  49.  
  50. #ifdef    SHADOWPWD
  51. #include "shadow.h"
  52. struct    spwd    *spwd;
  53. struct    spwd    *getspnam();
  54. #endif
  55. #ifdef    SHADOWGRP
  56. struct    sgrp    *sgrp;
  57. struct    sgrp    *getsgnam();
  58. #endif
  59.  
  60. #ifdef    USE_SYSLOG
  61. #include <syslog.h>
  62.  
  63. /*VARARGS*/ int syslog();
  64.  
  65. #ifndef    LOG_WARN
  66. #define    LOG_WARN LOG_WARNING
  67. #endif    /* !LOG_WARN */
  68. #endif    /* USE_SYSLOG */
  69.  
  70. struct    group    *grp;
  71. struct    group    *getgrgid();
  72. struct    group    *getgrnam();
  73.  
  74. char    *getlogin();
  75. char    *crypt();
  76. char    *getpass();
  77. char    *getenv();
  78. char    *pw_encrypt();
  79. void    shell();
  80.  
  81. char    *name;
  82. char    *group;
  83. gid_t    gid;
  84. int    cflag;
  85.  
  86. char    *Prog;
  87. char    prog[BUFSIZ];
  88. char    base[BUFSIZ];
  89. char    passwd[BUFSIZ];
  90. char    *cpasswd;
  91. char    *salt;
  92.  
  93. #ifndef    MAXENV
  94. #define    MAXENV    64
  95. #endif
  96.  
  97. char    *newenvp[MAXENV];
  98. int    newenvc = 0;
  99. int    maxenv = MAXENV;
  100.  
  101. /*
  102.  * usage - print command usage message
  103.  */
  104.  
  105. usage ()
  106. {
  107.     if (strcmp (Prog, "sg") != 0)
  108.         fprintf (stderr, "usage: newgrp [ - ] [ group ]\n");
  109.     else
  110.         fprintf (stderr, "usage: sg group [ command ]\n");
  111. }
  112.  
  113. /*
  114.  * newgrp - change the invokers current real and effective group id
  115.  */
  116.  
  117. main (argc, argv, envp)
  118. int    argc;
  119. char    **argv;
  120. char    **envp;
  121. {
  122.     int    initflag = 0;
  123.     int    needspasswd = 0;
  124.     int    i;
  125.     char    *cp;
  126.     char    *command;
  127.  
  128.     /*
  129.      * save my name for error messages and save my real gid incase
  130.      * of errors.  if there is an error i have to exec a new login
  131.      * shell for the user since her old shell won't have fork'd to
  132.      * create the process.  skip over the program name to the next
  133.      * command line argument.
  134.      */
  135.  
  136.     if (Prog = strrchr (argv[0], '/'))
  137.         Prog++;
  138.     else
  139.         Prog = argv[0];
  140.  
  141. #ifdef    USE_SYSLOG
  142.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  143. #endif
  144.  
  145.     gid = getgid ();
  146.     argc--; argv++;
  147.  
  148.     /*
  149.      * here i get to determine my current name.  i do this to validate
  150.      * my access to the requested group.  the validation works like
  151.      * this -
  152.      *    1) get the name associated with my current user id
  153.      *    2) get my login name, as told by getlogin().
  154.      *    3) if they match, my name is the login name
  155.      *    4) if they don't match, my name is the name in the
  156.      *       password file.
  157.      *
  158.      * this isn't perfect, but it works more often then not.  i have
  159.      * to do this here so i can get the login name to find the
  160.      * login group.
  161.      */
  162.  
  163.     pwd = getpwuid (getuid ());
  164.  
  165.     if (! (name = getlogin ()) || strcmp (name, pwd->pw_name) != 0)
  166.         name = pwd->pw_name;
  167.  
  168.     if (! (pwd = getpwnam (name))) {
  169.         fprintf (stderr, "unknown user: %s\n", name);
  170. #ifdef    USE_SYSLOG
  171.         syslog (LOG_WARN, "unknown user `%s', uid `%d'\n",
  172.             name, getuid ());
  173.         closelog ();
  174. #endif
  175.         goto failure;
  176.     }
  177.  
  178.     /*
  179.      * Parse the command line.  There are two accepted flags.  The
  180.      * first is "-", which for newgrp means to re-create the entire
  181.      * environment as though a login had been performed, and "-c",
  182.      * which for sg causes a command string to be executed.
  183.      *
  184.      * The next argument, if present, must be the new group name.
  185.      * Any remaining remaining arguments will be used to execute a
  186.      * command as the named group.  If the group name isn't present,
  187.      * I just use the login group ID of the current user.
  188.      *
  189.      * The valid syntax are
  190.      *    newgrp [ - ] [ groupid ]
  191.      *    sg [ - ]
  192.      *    sg [ - ] groupid [ command ]
  193.      */
  194.  
  195.     if (argc > 0 && argv[0][0] == '-' && argv[0][1] == '\0') {
  196.         argc--; argv++;
  197.         initflag = 1;
  198.     }
  199.     if (strcmp (Prog, "newgrp") != 0) {
  200.  
  201.         /* 
  202.          * Do the command line for everything that is
  203.          * not "newgrp".
  204.          */
  205.  
  206.         if (argc > 0 && argv[0][0] != '-') {
  207.             group = argv[0];
  208.             argc--; argv++;
  209.         } else {
  210.             usage ();
  211. #ifdef    USE_SYSLOG
  212.             closelog ();
  213. #endif
  214.             exit (1);
  215.         }
  216.         if (argc > 0) {
  217.             command = argv[1];
  218.             cflag++;
  219.         }
  220.     } else {
  221.  
  222.         /*
  223.          * Do the command line for "newgrp".  It's just
  224.          * making sure there aren't any flags and getting
  225.          * the new group name.
  226.          */
  227.  
  228.         if (argc > 0 && argv[0][0] == '-') {
  229.             usage ();
  230.             goto failure;
  231.         } else if (argv[0] != (char *) 0) {
  232.             group = argv[0];
  233.         } else {
  234.  
  235.             /*
  236.              * get the group file entry for her login group id.
  237.              * the entry must exist, simply to be annoying.
  238.              */
  239.  
  240.             if (! (grp = getgrgid (pwd->pw_gid))) {
  241.                 fprintf (stderr, "unknown gid: %d\n",
  242.                     pwd->pw_gid);
  243. #ifdef    USE_SYSLOG
  244.                 syslog (LOG_CRIT, "unknown gid: %d\n",
  245.                     pwd->pw_gid);
  246. #endif
  247.                 goto failure;
  248.             }
  249.         }
  250.     }
  251. #ifdef    NGROUPS
  252.  
  253.     /*
  254.      * get the current users groupset.  the new group will be
  255.      * added to the concurrent groupset if there is room, otherwise
  256.      * you get a nasty message but at least your real and effective
  257.      * group id's are set.
  258.      */
  259.  
  260.     ngroups = getgroups (0, 0);
  261.     if (ngroups > 0)
  262.         getgroups (ngroups, groups);
  263. #endif
  264.  
  265.     /*
  266.      * now we put her in the new group.  the password file entry for
  267.      * her current user id has been gotten.  if there was no optional
  268.      * group argument she will have her real and effective group id
  269.      * set to the value from her password file entry.  otherwise
  270.      * we validate her access to the specified group.
  271.      */
  272.  
  273.     if (group == (char *) 0) {
  274.         if (! (grp = getgrgid (pwd->pw_gid))) {
  275.             fprintf (stderr, "unknown gid: %d\n", pwd->pw_gid);
  276.             goto failure;
  277.         }
  278.     } else if (! (grp = getgrnam (group))) {
  279.         fprintf (stderr, "unknown group: %s\n", group);
  280.         goto failure;
  281.     }
  282. #ifdef    SHADOWGRP
  283.     sgrp = getsgnam (group);
  284. #endif
  285.  
  286.     /*
  287.      * see if she is a member of this group.
  288.      */
  289.  
  290.     for (i = 0;grp->gr_mem[i];i++)
  291.         if (strcmp (name, grp->gr_mem[i]) == 0)
  292.             break;
  293.  
  294.     /*
  295.      * if she isn't a member, she needs to provide the
  296.      * group password.  if there is no group password, she
  297.      * will be denied access anyway.
  298.      */
  299.  
  300.     if (grp->gr_mem[i] == (char *) 0)
  301.         needspasswd = 1;
  302.  
  303. #ifdef    SHADOWGRP
  304.     if (sgrp) {
  305.  
  306.         /*
  307.          * Do the tests again with the shadow group entry.
  308.          */
  309.  
  310.         for (i = 0;sgrp->sg_mem[i];i++)
  311.             if (strcmp (name, sgrp->sg_mem[i]) == 0)
  312.                 break;
  313.  
  314.         needspasswd = sgrp->sg_mem[i] == (char *) 0;
  315.     }
  316. #endif
  317. #ifdef    SHADOWPWD
  318.  
  319.     /*
  320.      * if she does not have either a shadowed password,
  321.      * or a regular password, and the group has a password,
  322.      * she needs to give the group password.
  323.      */
  324.  
  325.     if (spwd = getspnam (name)) {
  326.         if (spwd->sp_pwdp[0] == '\0' && grp->gr_passwd[0])
  327.             needspasswd = 1;
  328. #ifdef    SHADOWGRP
  329.         if (spwd->sp_pwdp[0] == '\0' && sgrp != 0)
  330.             needspasswd = sgrp->sg_passwd[0] != '\0';
  331. #endif
  332.     } else {
  333.         if (pwd->pw_passwd[0] == '\0' && grp->gr_passwd[0])
  334.             needspasswd = 1;
  335. #ifdef    SHADOWGRP
  336.         if (pwd->pw_passwd[0] == '\0' && sgrp != 0)
  337.             needspasswd = sgrp->sg_passwd[0] != '\0';
  338. #endif
  339.     }
  340. #else
  341.  
  342.     /*
  343.      * if she does not have a regular password she will have
  344.      * to give the group password, if one exists.
  345.      */
  346.  
  347.     if (pwd->pw_passwd[0] == '\0' && grp->gr_passwd[0])
  348.         needspasswd = 1;
  349. #ifdef    SHADOWGRP
  350.     if (pwd->pw_passwd[0] == '\0' && sgrp != 0)
  351.         needspasswd = sgrp->sg_passwd[0] != '\0';
  352. #endif
  353. #endif
  354.  
  355.     /*
  356.      * now i see about letting her into the group she requested.
  357.      * if she is the root user, i'll let her in without having to
  358.      * prompt for the password.  otherwise i ask for a password
  359.      * if she flunked one of the tests above.  note that she
  360.      * won't have to provide the password to her login group even
  361.      * if she isn't listed as a member.
  362.      */
  363.  
  364.     if (getuid () != 0 && needspasswd) {
  365.         char    *encrypted;
  366.  
  367.         encrypted = grp->gr_passwd;
  368. #ifdef    SHADOWGRP
  369.         if (sgrp)
  370.             encrypted = sgrp->sg_passwd;
  371. #endif
  372.         passwd[0] = '\0';
  373.  
  374.         if (encrypted[0]) {
  375.  
  376.         /*
  377.          * get the password from her, and set the salt for
  378.          * the decryption from the group file.
  379.          */
  380.  
  381.             if (! (cp = getpass ("Password:")))
  382.                 goto failure;
  383.  
  384.             strcpy (passwd, cp);
  385.             bzero (cp, strlen (cp));
  386.             salt = encrypted;
  387.         } else {
  388.  
  389.         /*
  390.          * there is no password, print out "Sorry" and give up
  391.          */
  392.  
  393.             fputs ("Sorry\n", stderr);
  394.             goto failure;
  395.         }
  396.  
  397.         /*
  398.          * encrypt the key she gave us using the salt from
  399.          * the password in the group file.  the result of
  400.          * this encryption must match the previously
  401.          * encrypted value in the file.
  402.          */
  403.  
  404.         cpasswd = pw_encrypt (passwd, salt);
  405.         bzero (passwd, sizeof passwd);
  406.  
  407.         if (strcmp (cpasswd, encrypted) != 0) {
  408.             fputs ("Sorry\n", stderr);
  409. #ifdef    USE_SYSLOG
  410.         syslog (LOG_INFO, "Invalid password for `%s' from `%s'\n",
  411.             group, name);
  412. #endif
  413.             goto failure;
  414.         }
  415.     }
  416.  
  417.     /*
  418.      * all successful validations pass through this point.  the
  419.      * group id will be set, and the group added to the concurrent
  420.      * groupset.
  421.      */
  422.  
  423. #ifdef    USE_SYSLOG
  424.     if (getdef_bool ("SYSLOG_SU_ENAB"))
  425.         syslog (LOG_INFO, "user `%s' switched to group `%s'\n", name, group);
  426. #endif
  427.     gid = grp->gr_gid;
  428. #ifdef    NGROUPS
  429.  
  430.     /*
  431.      * i am going to try to add her new group id to her concurrent
  432.      * group set.  if the group id is already present i'll just
  433.      * skip this part.  if the group doesn't fit, i'll complain
  434.      * loudly and skip this part ...
  435.      */
  436.  
  437.     for (i = 0;i < ngroups;i++) {
  438.         if (gid == groups[i])
  439.             break;
  440.     }
  441.     if (i == ngroups) {
  442.         if (ngroups == NGROUPS) {
  443.             fprintf (stderr, "too many groups\n");
  444.         } else {
  445.             groups[ngroups++] = gid;
  446.             if (setgroups (ngroups, groups)) {
  447.                 fprintf (stderr, "%s: ", Prog);
  448.                 perror ("unable to set groups");
  449.             }
  450.         }
  451.     }
  452. #endif
  453.  
  454. okay:
  455.  
  456.     /*
  457.      * i set her group id either to the value she requested, or
  458.      * to the original value if the newgrp failed.
  459.      */
  460.  
  461.     if (setgid (gid))
  462.         perror ("setgid");
  463.  
  464.     if (setuid (getuid ()))
  465.         perror ("setuid");
  466.  
  467.     /*
  468.      * see if the "-c" flag was used.  if it was, i just create a
  469.      * shell command for her using the argument that followed the
  470.      * "-c" flag.
  471.      */
  472.  
  473.     if (cflag) {
  474.         execl ("/bin/sh", "sh", "-c", command, (char *) 0);
  475.         perror ("/bin/sh");
  476. #ifdef    USE_SYSLOG
  477.         closelog ();
  478. #endif
  479.         exit (255);
  480.     }
  481.  
  482.     /*
  483.      * i have to get the pathname of her login shell.  as a favor,
  484.      * i'll try her environment for a $SHELL value first, and
  485.      * then try the password file entry.  obviously this shouldn't
  486.      * be in the restricted command directory since it could be
  487.      * used to leave the restricted environment.
  488.      */
  489.  
  490.     if (! initflag && (cp = getenv ("SHELL")))
  491.         strncpy (prog, cp, sizeof prog);
  492.     else if (pwd->pw_shell && pwd->pw_shell[0])
  493.         strncpy (prog, pwd->pw_shell, sizeof prog);
  494.     else
  495.         strcpy (prog, "/bin/sh");
  496.  
  497.     /*
  498.      * now i try to find the basename of the login shell.  this
  499.      * will become argv[0] of the spawned command.
  500.      */
  501.  
  502.     if (cp = strrchr (prog, '/'))
  503.         cp++;
  504.     else
  505.         cp = prog;
  506.  
  507.     /*
  508.      * to have the shell perform login processing i will set the
  509.      * first character in the first argument to a "-".
  510.      */
  511.  
  512.     if (initflag)
  513.         strcat (strcpy (base, "-"), cp);
  514.     else
  515.         strcpy (base, cp);
  516.  
  517. #ifdef    SHADOWPWD
  518.     endspent ();
  519. #endif
  520. #ifdef    SHADOWGRP
  521.     endsgent ();
  522. #endif
  523.     endpwent ();
  524.     endgrent ();
  525.  
  526.     /*
  527.      * switch back to her home directory if i am doing login
  528.      * initialization.
  529.      */
  530.  
  531.     if (initflag) {
  532.         chdir (pwd->pw_dir);
  533.         while (*envp) {
  534.             if (strncmp (*envp, "PATH=", 5) == 0 ||
  535.                     strncmp (*envp, "HOME=", 5) == 0 ||
  536.                     strncmp (*envp, "SHELL=", 6) == 0 ||
  537.                     strncmp (*envp, "TERM=", 5) == 0)
  538.                 addenv (*envp);
  539.  
  540.             envp++;
  541.         }
  542.     } else {
  543.         while (*envp)
  544.             addenv (*envp++);
  545.     }
  546.  
  547.     /*
  548.      * exec the login shell and go away.  we are trying to get
  549.      * back to the previous environment which should be the
  550.      * user's login shell.
  551.      */
  552.  
  553.     shell (prog, base);
  554.     /*NOTREACHED*/
  555.  
  556. failure:
  557.     /*
  558.      * this is where all failures land.  the group id will not
  559.      * have been set, so the setgid() below will set me to the
  560.      * original group id i had when i was invoked.
  561.      */
  562.  
  563.     /*
  564.      * only newgrp needs to re-exec the user's shell.  that is
  565.      * because the shell doesn't recognize "sg", so it doesn't
  566.      * "exec" this command.
  567.      */
  568.  
  569.     if (strcmp (Prog, "newgrp") != 0) {
  570. #ifdef    USE_SYSLOG
  571.         closelog ();
  572. #endif
  573.         exit (1);
  574.     }
  575.     
  576.     /*
  577.      * The GID is still set to the old value, so now I can
  578.      * give the user back her shell.
  579.      */
  580.  
  581.     goto okay;
  582. }
  583.