home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3186 / su.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-13  |  13.2 KB  |  470 lines

  1. /* su.c - proper su for horrible value-added SCO Unix. */
  2.  
  3. /* By Eamonn McManus, Datacode Communications Ltd <em@dce.ie>, August 1990.
  4.    This program is not copyrighted.
  5.  
  6.    Usage: su [-s shell] [-] [user [shell-args]].
  7.  
  8.    Sets user id to the named user (default root) and execs the named shell
  9.    (default their login shell).  If the invoking user is not root, the
  10.    specified user's login password must be supplied, and the -s option is
  11.    not allowed.  The shell-args if supplied are usually -c "commandline"
  12.    to do just that one command under control of the shell.
  13.  
  14.    Usage differs from standard su in that you cannot say, e.g., "su -c cmd";
  15.    you must specify a user to su to except if there are no shell-args.
  16.  
  17.    If a group called `wheel' exists in /etc/group, only members of that
  18.    group can su to root.  The name `wheel' dates from TOPS-20 at least,
  19.    but I don't know what it means.  4.3 BSD re-invented it.
  20.  
  21.    Standard su clears the environment to a few standard entries when you
  22.    do `su -'.  We don't bother doing that, but just change a few key
  23.    entries.
  24. */
  25.  
  26. #ifndef __STDC__
  27. #define const /* nothing */
  28. #endif
  29.  
  30. #ifndef lint
  31. static const char rcsid[] =
  32.     "$Id: su.c,v 1.1 90/09/28 16:22:21 em Release $";
  33. #endif
  34.  
  35. #ifndef SecureWare
  36. #define SecureWare 1    /* Appears to be necessary, if regrettable. */
  37. #endif
  38.  
  39. #include <stdio.h>
  40. #ifdef __STDC__
  41. #include <stdlib.h>
  42. #endif
  43. #include <string.h>
  44. #include <ctype.h>
  45. #include <pwd.h>
  46. #include <grp.h>
  47. #include <sys/types.h>
  48. #include <sys/wait.h>
  49. #if SecureWare
  50. #include <sys/security.h>
  51. #include <prot.h>
  52. #endif
  53. #include <errno.h>
  54.  
  55. #define DEFAULTS_FILE "/etc/default/su"
  56.  
  57. #ifdef __STDC__    /* <prototypes.h> is worthless, conflicts with <stdlib.h>. */
  58.  
  59. extern time_t time(long *tloc);
  60. extern const char *ctime(const time_t *clock);
  61. extern const char *ttyname(int fildes);
  62. extern const char *oksetluid(int uid);
  63. extern char *getpasswd(const char *prompt, int max_size);
  64. extern char *crypt(const char *key, const char *salt);
  65. /* Declare everything for gcc -Wall. */
  66. extern int getuid(void), fork(void), setgid(int gid), setuid(int uid);
  67. extern int putenv(const char *envstring);
  68. extern unsigned sleep(unsigned seconds);
  69. extern int execv(const char *path, const char *const* arg);
  70. extern pid_t waitpid(pid_t pid, int *status, int options);
  71. extern int umask(int cmask);
  72. extern int chdir(const char *dir);
  73.  
  74. typedef void *pointer;
  75.  
  76. static void su(const struct passwd *pwd, int islogin, const char *shell,
  77.            const char **shargs);
  78. static void crash(const char *why, const char *what);
  79. static void logsu(const char *sutype, const char *sumsg, const char *suarg);
  80. static int readdefault(const char *defaultname);
  81. static const char *defaultvalue(const char *settingname);
  82. static char *xstrdup(const char *str);
  83. static pointer xmalloc(int size);
  84. static void xputenv(const char *name, const char *value);
  85. static struct passwd *xgetpwnam(const char *name);
  86. static struct passwd *xgetpwuid(int uid);
  87. static struct passwd *whoami(void);
  88.  
  89. #else    /* !STDC */
  90.  
  91. extern char *getpass(), *getpasswd();
  92. extern char *crypt();
  93. extern char *ttyname();
  94. extern char *oksetluid();
  95. extern char *malloc();
  96.  
  97. typedef char *pointer;
  98.  
  99. static void su(), logsu(), crash(), xputenv();
  100. static char *defaultvalue(), *xstrdup();
  101. static pointer xmalloc();
  102. static struct passwd *xgetpwnam(), *xgetpwuid(), *whoami();
  103.  
  104. #endif    /* !STDC */
  105.  
  106.  
  107. const char *progname = "su";
  108.  
  109.  
  110. int main(argc, argv)
  111. int argc;
  112. const char **const argv;
  113. {
  114.     int i;
  115.     int login = 0;
  116.     struct passwd *pwd;
  117.     const char *user = "root", *shell = NULL;
  118.     if (argc > 0)
  119.     progname = argv[0];
  120.     (void) readdefault(DEFAULTS_FILE);
  121.     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
  122.     switch (argv[i][1]) {
  123.     case '\0':
  124.         login = 1; break;
  125.     case 's':    /* Specify different shell from user's own. */
  126.         if (argv[i][2])
  127.         shell = &argv[i][2];
  128.         else {
  129.         if (++i >= argc)
  130.             goto usage;
  131.         shell = argv[i];
  132.         }
  133.         if (getuid() != 0) {
  134.         logsu("BADSU", "tried to say -s ", shell);
  135.         crash("*-s", "only superuser can specify -s");
  136.         }
  137.         break;
  138.     default:
  139. usage:
  140.         fprintf(stderr, "Usage: %s [-s shell] [-] [user [shell-args]]\n",
  141.             progname);
  142.         exit(1);
  143.     }
  144.     }
  145.     if (i < argc) {
  146.     user = argv[i];
  147.     i++;
  148.     }
  149.     if ((pwd = xgetpwnam(user)) == NULL)
  150.     crash("*no such user", user);
  151.     /* Root isn't asked for a password; everyone else is unless su'ing
  152.        to themselves. */
  153.     if (getuid() != 0 && getuid() != pwd->pw_uid) {
  154.     char *pass, *crpass, *realpass;
  155. #if SecureWare
  156.     struct pr_passwd *ugh;
  157.     int origumask;
  158. #endif
  159.     /* If becoming root, make sure we're on the wheel (whatever that
  160.        means). */
  161.     if (pwd->pw_uid == 0) {
  162.         struct group *grp = getgrnam("wheel");
  163.         if (grp != NULL) {
  164.         struct passwd *me = whoami();
  165.         char **mem;
  166.         for (mem = grp->gr_mem; *mem != NULL; mem++)
  167.             if (strcmp(*mem, me->pw_name) == 0)
  168.             break;
  169.         if (*mem == NULL) {
  170.             logsu("BADSU", "to root ", "but not on wheel");
  171.             fprintf(stderr, "Not in wheel group\n");
  172.             exit(2);
  173.         }
  174.         }
  175.     }
  176. #if !SecureWare    /* Standard su, untested. */
  177.     if ((pass = getpass("Password:")) == NULL)
  178.         crash("getpass", user);
  179.     if ((crpass = crypt(pass, pwd->pw_passwd)) == NULL)
  180.         crash("crypt", user);
  181.     realpass = pwd->pw_passwd;
  182. #else /* SecureWare */
  183.     origumask = umask(0); (void) umask(origumask);
  184.     set_auth_parameters(argc, argv);
  185.     /* OBNOXIOUS MISFEATURE: above call sets the umask to 077.  If I want
  186.        the umask to be changed, I'll ASK for it to be changed.  Grrr.  */
  187.     (void) umask(origumask);
  188.     if ((ugh = getprpwnam(user)) == NULL)
  189.         crash("get protected password", user);
  190.     if ((pass = getpasswd("Password:", AUTH_MAX_PASSWD_LENGTH)) == NULL)
  191.         crash("getpasswd", user);
  192.     /* Use the undocumented bigcrypt() routine which crypts a password
  193.        in pieces if it is longer than 8 characters. */
  194.     if ((crpass = bigcrypt(pass, ugh->ufld.fd_encrypt)) == NULL)
  195.         crash("crypt", user);
  196.         /* I don't think crypt can fail, but may as well test. */
  197. #if 0    /* DEBUG */
  198. fprintf(stderr, "user %s entered %s crypted %s really %s\n",
  199.     user, pass, crpass, ugh->ufld.fd_encrypt);
  200. #endif
  201.     realpass = ugh->ufld.fd_encrypt;
  202. #endif /* SecureWare */
  203.     if (strcmp(crpass, realpass) != 0) {
  204.         logsu("BADSU", "wrong password for ", user);
  205.         sleep(1);    /* Make dictionary searches etc harder. */
  206.         fprintf(stderr, "Sorry\n");
  207.         exit(2);
  208.     }
  209.     }
  210.     logsu("SU", "to ", user);
  211.     /* We overwrite argv[i - 1] with the name of the shell so we can use
  212.        the rest of the array as an argument to execv().
  213.        The stupid Microsoft compiler warns about the ?: below.  Ignore it.
  214.        Better still, use GNU C. */
  215.     su(pwd, login, shell ? shell : pwd->pw_shell, argv + i - 1);
  216.     return 0;    /* Not reached. */
  217. }
  218.  
  219.  
  220. /* su to the named user and uid.  If islogin, pretend we are logging in,
  221.    to the extent of setting HOME and LOGNAME environment variables,
  222.    prefixing argv[0] of the shell with "-", and changing to the new
  223.    user's directory.  */
  224. static void su(pwd, islogin, shell, shargs)
  225. const struct passwd *pwd;
  226. int islogin;
  227. const char *shell;
  228. const char **shargs;
  229. {
  230.     if (shell == NULL || *shell == '\0')
  231.     shell = "/bin/sh";
  232.     shargs[0] = strrchr(shell, '/');
  233.     if (shargs[0] != NULL)
  234.     shargs[0]++;
  235.     else shargs[0] = shell;
  236.     if (islogin) {    /* su - user */
  237.     if (strcmp(shargs[0], "sh") == 0)
  238.         shargs[0] = "-su";    /* As per man page. */
  239.     else {
  240.         char *p = xmalloc(strlen(shargs[0]) + sizeof("-"));
  241.         (void) sprintf(p, "-%s", shargs[0]);
  242.         shargs[0] = p;
  243.     }
  244.     /* Change environment; probably not necessary as shell will do
  245.        this because it's invoked with the leading "-".  */
  246.     xputenv("LOGNAME", pwd->pw_name);
  247.     }
  248.     xputenv("HOME", pwd->pw_dir);
  249.     xputenv("SHELL", shell);
  250. #if SecureWare
  251.     {
  252.     const char *err;
  253.     if ((err = oksetluid(pwd->pw_uid)) != NULL)
  254.         crash("setluid", err);
  255.     }
  256. #endif
  257.     if (setgid(pwd->pw_gid) < 0)
  258.     crash("setgid", pwd->pw_name);
  259.     if (setuid(pwd->pw_uid) < 0)
  260.     crash("setuid", pwd->pw_name);
  261.     if (islogin && chdir(pwd->pw_dir) < 0)
  262.     perror(pwd->pw_dir);    /* But su anyway. */
  263.     execv(shell, shargs);
  264.     crash("execl", shell);
  265. }
  266.  
  267.  
  268. /* Log an su attempt.  The three fields are printed as well as the current
  269.    user, their tty, and the time. */
  270. static void logsu(sutype, sumsg, suarg)
  271. const char *sutype, *sumsg, *suarg;
  272. {
  273.     const char *const*logp;
  274.     static const char *const logfiles[] = {"SULOG", "CONSOLE", NULL};
  275.     struct passwd *pwd;
  276.     char *user;
  277.     if ((pwd = whoami()) == NULL)
  278.     user = "(unknown)";
  279.     else user = pwd->pw_name;
  280.     for (logp = logfiles; *logp != NULL; logp++) {
  281.     FILE *f;
  282.     const char *p;
  283.     time_t now;
  284.     if ((p = defaultvalue(*logp)) == NULL)
  285.         continue;
  286.     if ((f = fopen(p, "a")) == NULL) {
  287.         /* This isn't a fatal error, lest we lock ourselves out of su.
  288.            Giving the message to the su'ing user is imperfect but will
  289.            do.  If you want to know why your log isn't getting any su
  290.            messages you can try su. */
  291.         (void) fprintf(stderr, "%s: append to log ", progname);
  292.         perror(p);
  293.         continue;
  294.     }
  295.     (void) fprintf(f, "%s: %s%s: user %s", sutype, sumsg, suarg, user);
  296.     if ((p = ttyname(0)) != NULL)
  297.         (void) fprintf(f, " on %s", p);
  298.     (void) time(&now);
  299.     (void) fprintf(f, ", %s", ctime(&now));    /* ctime() has trailing \n. */
  300.     (void) fclose(f);
  301.     }
  302. }
  303.  
  304.  
  305. /* Print message "why: what" to stderr.  The what string is printed with
  306.    perror unless the why string begins with *.  The * is not printed.
  307.    Exits the program.  */
  308. static void crash(why, what)
  309. const char *why, *what;
  310. {
  311.     fprintf(stderr, "%s: ", progname);
  312.     if (*why == '*') {
  313.     fprintf(stderr, "%s", why + 1);
  314.     if (what)
  315.         fprintf(stderr, ": %s", what);
  316.     putc('\n', stderr);
  317.     } else if (what) {
  318.     fprintf(stderr, "%s: ", why);
  319.     perror(what);
  320.     } else perror(why);
  321.     exit(1);
  322. }
  323.  
  324.  
  325. static const struct setting {
  326.     const char *name, *value;
  327.     const struct setting *next;
  328. } *settingp;
  329.  
  330.  
  331. /* Read the configuration file.  Lines in the file can be blank, comments
  332.    beginning with #, or assigments looking like WORD=VALUE.  Spaces around
  333.    the WORD and the VALUE are stripped.  We store (word, value) pairs
  334.    in a linked list which we can consult later for settings of
  335.    interest.  If we don't succeed in opening the file we don't care; there
  336.    will just be no settings.  Return status is <0 if there is an error,
  337.    though this doesn't include not being able to open the file. */
  338. static int readdefault(configname)
  339. const char *configname;
  340. {
  341.     FILE *f;
  342.     char buf[512];
  343.     int i;
  344.     char *p;
  345.     struct setting *sp;
  346.     if ((f = fopen(configname, "r")) == NULL)
  347.     return 0;
  348.     while (fgets(buf, sizeof buf, f) != NULL) {
  349.     for (i = strlen(buf); i > 0 && isspace(buf[i - 1]); i--) ;
  350.     buf[i] = '\0';    /* i == 0 => just a \n. */
  351.     for (i = 0; isspace(buf[i]); i++) ;
  352.     if (buf[i] == '\0' || buf[i] == '#')
  353.         continue;
  354.     p = xstrdup(buf + i);
  355.     for (i = 0; p[i] != '='; i++) {
  356.         if (isspace(p[i])) {
  357.         p[i] = '\0';
  358.         do i++; while (isspace(p[i]));
  359.         if (p[i] != '=') {
  360.             (void) fclose(f);
  361.             (void) fprintf(stderr,
  362.                    "%s: format error in defaults file:\n%s\n",
  363.                    progname, buf);
  364.             return -1;
  365.         }
  366.         break;
  367.         }
  368.     }
  369.     p[i] = '\0';
  370.     sp = (struct setting *) xmalloc((int) sizeof *sp);
  371.     sp->name = p;
  372.     do i++; while (isspace(p[i]));
  373.     sp->value = p + i;
  374.     sp->next = settingp;
  375.     settingp = sp;
  376.     }
  377.     (void) fclose(f);
  378.     return 0;
  379. }
  380.  
  381.  
  382. /* Return the value of a default setting. */
  383. static const char *defaultvalue(settingname)
  384. const char *settingname;
  385. {
  386.     const struct setting *sp;
  387.     for (sp = settingp; sp != NULL; sp = sp->next)
  388.     if (strcmp(sp->name, settingname) == 0)
  389.         return sp->value;
  390.     return NULL;
  391. }
  392.  
  393.  
  394. /* Safe malloc, 'cos it crashes if it fails.  (That's safe.) */
  395. static pointer xmalloc(size)
  396. int size;
  397. {
  398.     pointer p;
  399.     if ((p = malloc((size_t) size)) == NULL)
  400.     crash("malloc", (char *) NULL);
  401.     return p;
  402. }
  403.  
  404.  
  405. /* Likewise for strdup.  Since strdup isn't standard we write it by hand. */
  406. static char *xstrdup(str)
  407. const char *str;
  408. {
  409.     char *p = xmalloc(strlen(str) + 1);
  410.     return strcpy(p, str);
  411. }
  412.  
  413.  
  414. /* Put name=value into the environment. */
  415. static void xputenv(name, value)
  416. const char *name, *value;
  417. {
  418.     int len = strlen(name) + strlen(value) + sizeof("=");
  419.     char *p = xmalloc(len);
  420.     (void) sprintf(p, "%s=%s", name, value);
  421.     if (putenv(p) < 0)
  422.     crash("putenv", p);
  423. }
  424.  
  425.  
  426. /* Wrappers for getpw* that copy the returned structure into a malloced
  427.    buffer, so it doesn't get overwritten by subsequent calls. */
  428. static struct passwd *copypasswd(pwd)
  429. struct passwd *pwd;
  430. {
  431.     struct passwd *p;
  432.     if (pwd == NULL)
  433.     return NULL;
  434.     p = (struct passwd *) xmalloc(sizeof *p);
  435.     *p = *pwd;
  436.     /* We only dup the fields we're interested in here; the other ones will
  437.        get splatted the next time you call getpwthing() because the stuff
  438.        they point to will be overwritten. */
  439.     p->pw_name = xstrdup(p->pw_name);
  440.     p->pw_dir = xstrdup(p->pw_dir);
  441.     p->pw_shell = xstrdup(p->pw_shell);
  442.     return p;
  443. }
  444.  
  445.  
  446. static struct passwd *xgetpwnam(name)
  447. const char *name;
  448. {
  449.     /* You may get a warning from the next line, if you have an inadequate
  450.        <pwd.h> that declares getpwnam(char *name) rather than
  451.        getpwnam(const char *name). */
  452.     return copypasswd(getpwnam(name));
  453. }
  454.  
  455.  
  456. static struct passwd *xgetpwuid(uid)
  457. int uid;
  458. {
  459.     return copypasswd(getpwuid(uid));
  460. }
  461.  
  462.  
  463. static struct passwd *whoami()
  464. {
  465.     static struct passwd *iam;
  466.     if (iam != NULL)
  467.     return iam;
  468.     else return iam = xgetpwuid(getuid());
  469. }
  470.