master xplshn/aruu / cmd / xsi / su.c
  1/* see LICENSE file for copyright and license details */
  2#include "passwd.h"
  3#include "util.h"
  4#include "wexec.h"
  5
  6#include <grp.h>
  7#include <limits.h>
  8#include <pwd.h>
  9#include <stdio.h>
 10#include <stdlib.h>
 11#include <string.h>
 12#include <termios.h>
 13#include <unistd.h>
 14
 15static void
 16usage(void)
 17{
 18  eprintf("usage: %s [-l] [name]\n", argv0);
 19}
 20
 21/* reads one line with echo off into a reusable static buffer */
 22static char *
 23readpass(const char *prompt)
 24{
 25  static char    buf[128];
 26  struct termios old, raw;
 27  int            have_old;
 28  size_t         n;
 29
 30  fputs(prompt, stdout);
 31  fflush(stdout);
 32
 33  have_old = tcgetattr(STDIN_FILENO, &old) == 0;
 34  if (have_old) {
 35    raw = old;
 36    raw.c_lflag &= ~ECHO;
 37    tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
 38  }
 39
 40  if (!fgets(buf, sizeof(buf), stdin)) {
 41    if (have_old)
 42      tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
 43    return NULL;
 44  }
 45
 46  if (have_old) {
 47    tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
 48    fputc('\n', stdout);
 49  }
 50
 51  n = strlen(buf);
 52  if (n && buf[n - 1] == '\n')
 53    buf[--n] = '\0';
 54
 55  return buf;
 56}
 57
 58// ?man su: become another user
 59// ?man arguments: [-l] [name]
 60// ?man authenticate as name (default: root) and start a shell as them
 61// ?man with -l, also chdir to their home directory and run a login shell,
 62// ?man same as login would
 63int
 64main(int argc, char *argv[])
 65{
 66  struct passwd *pw;
 67  char           shellbuf[PATH_MAX];
 68  char          *name, *pass, *base;
 69  char          *shargv[2];
 70  int            lflag = 0;
 71  uid_t          uid;
 72
 73  ARGBEGIN
 74  {
 75    // ?man -l: start a login shell, chdir home like login does
 76    case 'l':
 77      lflag = 1;
 78      break;
 79    default:
 80      usage();
 81  }
 82  ARGEND
 83
 84  name = argc > 0 ? argv[0] : "root";
 85  pw   = getpwnam(name);
 86  if (!pw)
 87    eprintf("su: unknown user: %s\n", name);
 88
 89  uid = getuid();
 90  if (uid != 0) {
 91    pass = readpass("Password: ");
 92    if (!pass || pw_check(pw, pass) != 1)
 93      eprintf("su: incorrect password\n");
 94  }
 95
 96  if (initgroups(pw->pw_name, pw->pw_gid) < 0)
 97    weprintf("initgroups:");
 98  if (setgid(pw->pw_gid) < 0)
 99    eprintf("setgid:");
100  if (setuid(pw->pw_uid) < 0)
101    eprintf("setuid:");
102
103  strlcpy(shellbuf, pw->pw_shell[0] ? pw->pw_shell : "/bin/sh", sizeof(shellbuf));
104
105  if (lflag) {
106    if (chdir(pw->pw_dir) < 0)
107      weprintf("chdir %s:", pw->pw_dir);
108    setenv("HOME", pw->pw_dir, 1);
109  }
110  setenv("USER", pw->pw_name, 1);
111  setenv("LOGNAME", pw->pw_name, 1);
112  setenv("SHELL", shellbuf, 1);
113
114  base = strrchr(shellbuf, '/');
115  base = base ? base + 1 : shellbuf;
116
117  if (lflag) {
118    shargv[0]    = emalloc(strlen(base) + 2);
119    shargv[0][0] = '-';
120    strcpy(shargv[0] + 1, base);
121  } else {
122    shargv[0] = estrdup(base);
123  }
124  shargv[1] = NULL;
125
126  wexecv_self(shellbuf, shargv);
127  return 1;
128}