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}