add argument/usage message, correct exit status
[unix-history] / usr / src / usr.bin / su / su.c
CommitLineData
31e00b32 1/*
7d5e1706
KB
2 * Copyright (c) 1988 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
12d409ff 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31e00b32
DF
16 */
17
18#ifndef lint
19char copyright[] =
7d5e1706 20"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
31e00b32 21 All rights reserved.\n";
7d5e1706 22#endif /* not lint */
31e00b32 23
13dd8b1d 24#ifndef lint
302dd6d2 25static char sccsid[] = "@(#)su.c 5.18 (Berkeley) %G%";
7d5e1706 26#endif /* not lint */
13dd8b1d 27
a8d8c335 28#include <sys/param.h>
4f4c2c68
SL
29#include <sys/time.h>
30#include <sys/resource.h>
7d5e1706
KB
31#include <syslog.h>
32#include <stdio.h>
33#include <pwd.h>
34#include <grp.h>
d9e13dc0 35#include <string.h>
dbf8133b 36#include <unistd.h>
3a7a366d 37#include "pathnames.h"
3a265c7a 38
d9e13dc0 39#ifdef KERBEROS
302dd6d2 40#include <kerberosIV/des.h>
ba1b9d1d 41#include <kerberosIV/krb.h>
094e5ff2 42#include <netdb.h>
d9e13dc0 43
094e5ff2 44#define ARGSTR "-flmn"
d9e13dc0
KB
45
46int use_kerberos = 1;
094e5ff2
KF
47#else
48#define ARGSTR "-flm"
49#endif
50
7d5e1706 51main(argc, argv)
13dd8b1d 52 int argc;
7d5e1706 53 char **argv;
3a265c7a 54{
7d5e1706
KB
55 extern char **environ;
56 extern int errno, optind;
7d5e1706
KB
57 register struct passwd *pwd;
58 register char *p, **g;
59 struct group *gr;
a8d8c335 60 uid_t ruid, getuid();
6964f298 61 int asme, ch, fulllogin, fastlogin, prio;
a8d8c335 62 enum { UNSET, YES, NO } iscsh = UNSET;
6964f298 63 char *user, *shell, *username, *cleanenv[2], *nargv[4], **np;
d9e13dc0 64 char shellbuf[MAXPATHLEN];
dbf8133b 65 char *crypt(), *getpass(), *getenv(), *getlogin(), *mytty();
3a265c7a 66
6964f298
KB
67 np = &nargv[3];
68 *np-- = NULL;
69 asme = fulllogin = fastlogin = 0;
094e5ff2 70 while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
7d5e1706 71 switch((char)ch) {
a8d8c335
KB
72 case 'f':
73 fastlogin = 1;
74 break;
7d5e1706
KB
75 case '-':
76 case 'l':
77 fulllogin = 1;
78 break;
6964f298
KB
79 case 'm':
80 asme = 1;
81 break;
d9e13dc0 82#ifdef KERBEROS
094e5ff2
KF
83 case 'n':
84 use_kerberos = 0;
85 break;
86#endif
7d5e1706
KB
87 case '?':
88 default:
d9e13dc0
KB
89 (void)fprintf(stderr, "usage: su [%s] [login]\n",
90 ARGSTR);
7d5e1706
KB
91 exit(1);
92 }
93 argv += optind;
da31c6da 94
6964f298
KB
95 errno = 0;
96 prio = getpriority(PRIO_PROCESS, 0);
97 if (errno)
98 prio = 0;
99 (void)setpriority(PRIO_PROCESS, 0, -2);
100
101 /* get current login name and shell */
a8d8c335 102 if ((pwd = getpwuid(ruid = getuid())) == NULL) {
7d5e1706 103 fprintf(stderr, "su: who are you?\n");
96f93d9e
RC
104 exit(1);
105 }
d9e13dc0 106 username = strdup(pwd->pw_name);
6964f298 107 if (asme)
a8d8c335
KB
108 if (pwd->pw_shell && *pwd->pw_shell)
109 shell = strcpy(shellbuf, pwd->pw_shell);
110 else {
3a7a366d 111 shell = _PATH_BSHELL;
a8d8c335
KB
112 iscsh = NO;
113 }
7d5e1706 114
094e5ff2 115 /* get target login information, default to root */
6964f298 116 user = *argv ? *argv : "root";
96f93d9e 117 if ((pwd = getpwnam(user)) == NULL) {
7d5e1706 118 fprintf(stderr, "su: unknown login %s\n", user);
96f93d9e
RC
119 exit(1);
120 }
7d5e1706
KB
121
122 /* only allow those in group zero to su to root. */
a8d8c335 123 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)))
7d5e1706
KB
124 for (g = gr->gr_mem;; ++g) {
125 if (!*g) {
12d409ff
KB
126 (void)fprintf(stderr,
127 "su: you are not in the correct group to su %s.\n", user);
7d5e1706
KB
128 exit(1);
129 }
130 if (!strcmp(username, *g))
131 break;
a8a168db 132 }
12d409ff 133 openlog("su", LOG_CONS, 0);
a8a168db 134
d9e13dc0
KB
135 if (ruid) {
136#ifdef KERBEROS
137 if (!use_kerberos || kerberos(username, user, pwd->pw_uid))
094e5ff2 138#endif
d9e13dc0
KB
139 /* if target requires a password, verify it */
140 if (*pwd->pw_passwd) {
141 p = getpass("Password:");
142 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
143 fprintf(stderr, "Sorry\n");
144 if (pwd->pw_uid == 0)
145 syslog(LOG_AUTH|LOG_CRIT,
146 "BAD SU %s on %s", username,
dbf8133b 147 mytty());
d9e13dc0
KB
148 exit(1);
149 }
3a265c7a 150 }
3a265c7a 151 }
a8d8c335 152
6fad2abb
KB
153 if (asme) {
154 /* if asme and non-standard target shell, must be root */
155 if (!chshell(pwd->pw_shell) && ruid) {
d9e13dc0 156 (void)fprintf(stderr, "su: permission denied.\n");
6fad2abb 157 exit(1);
a8d8c335 158 }
d9e13dc0 159 } else if (pwd->pw_shell && *pwd->pw_shell) {
6fad2abb
KB
160 shell = pwd->pw_shell;
161 iscsh = UNSET;
162 } else {
3a7a366d 163 shell = _PATH_BSHELL;
6fad2abb
KB
164 iscsh = NO;
165 }
a8d8c335 166
6964f298 167 /* if we're forking a csh, we want to slightly muck the args */
a8d8c335
KB
168 if (iscsh == UNSET) {
169 if (p = rindex(shell, '/'))
170 ++p;
171 else
172 p = shell;
173 iscsh = strcmp(p, "csh") ? NO : YES;
174 }
175
6964f298 176 /* set permissions */
13dd8b1d
SL
177 if (setgid(pwd->pw_gid) < 0) {
178 perror("su: setgid");
7d5e1706 179 exit(1);
13dd8b1d 180 }
4f4c2c68 181 if (initgroups(user, pwd->pw_gid)) {
d9e13dc0 182 (void)fprintf(stderr, "su: initgroups failed.\n");
7d5e1706 183 exit(1);
13dd8b1d
SL
184 }
185 if (setuid(pwd->pw_uid) < 0) {
186 perror("su: setuid");
7d5e1706 187 exit(1);
13dd8b1d 188 }
6964f298
KB
189
190 if (!asme) {
191 if (fulllogin) {
192 p = getenv("TERM");
3a7a366d 193 cleanenv[0] = _PATH_SEARCHPATH;
6964f298
KB
194 cleanenv[1] = NULL;
195 environ = cleanenv;
196 (void)setenv("TERM", p, 1);
197 if (chdir(pwd->pw_dir) < 0) {
198 fprintf(stderr, "su: no directory\n");
199 exit(1);
200 }
201 }
202 if (fulllogin || pwd->pw_uid)
203 (void)setenv("USER", pwd->pw_name, 1);
a8d8c335
KB
204 (void)setenv("HOME", pwd->pw_dir, 1);
205 (void)setenv("SHELL", shell, 1);
3a265c7a 206 }
3a265c7a 207
6964f298
KB
208 if (iscsh == YES) {
209 if (fastlogin)
210 *np-- = "-f";
211 if (asme)
212 *np-- = "-m";
213 }
a8d8c335
KB
214
215 /* csh strips the first character... */
6964f298 216 *np = fulllogin ? "-su" : iscsh == YES ? "_su" : "su";
7d5e1706
KB
217
218 if (pwd->pw_uid == 0)
dbf8133b 219 syslog(LOG_NOTICE|LOG_AUTH, "%s on %s", username, mytty());
7d5e1706
KB
220
221 (void)setpriority(PRIO_PROCESS, 0, prio);
222
6964f298 223 execv(shell, np);
d9e13dc0 224 (void)fprintf(stderr, "su: %s not found.\n", shell);
7d5e1706 225 exit(1);
3a265c7a 226}
7b34c4f8
KB
227
228chshell(sh)
229 char *sh;
230{
d9e13dc0
KB
231 register char *cp;
232 char *getusershell();
7b34c4f8
KB
233
234 while ((cp = getusershell()) != NULL)
235 if (!strcmp(cp, sh))
236 return(1);
237 return(0);
238}
094e5ff2 239
dbf8133b
KB
240char *
241mytty()
242{
243 char *p, *ttyname();
244
245 return((p = ttyname(STDERR_FILENO)) ? p : "UNKNOWN TTY");
246}
247
d9e13dc0
KB
248#ifdef KERBEROS
249kerberos(username, user, uid)
250 char *username, *user;
251 int uid;
252{
253 extern char *krb_err_txt[];
254 KTEXT_ST ticket;
255 AUTH_DAT authdata;
256 struct hostent *hp;
257 register char *p;
258 int kerno;
259 u_long faddr;
260 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN], pw_buf[_PASSWORD_LEN];
261 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
dbf8133b 262 char *mytty();
d9e13dc0
KB
263
264 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) {
265 (void)fprintf(stderr, "su: couldn't get local realm.\n");
266 return(1);
267 }
268 if (koktologin(username, lrealm, user) && !uid) {
269 (void)fprintf(stderr, "kerberos su: not in %s's ACL.\n", user);
270 return(1);
271 }
272 (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid());
273 /* setuid(uid); */
274
275 if (read_pw_string(pw_buf, sizeof(pw_buf) - 1,
276 "Kerberos password: ", 0)) {
277 (void)fprintf(stderr, "su: error reading password.\n");
278 return(1);
279 }
280
281 (void)setenv("KRBTKFILE", krbtkfile, 1);
282 /* short lifetime for root tickets */
283 if (setuid(0) < 0) {
284 perror("su: setuid");
285 return(1);
286 }
287 (void)unlink(krbtkfile);
288 /* POLICY: short ticket lifetime for root */
289 kerno = krb_get_pw_in_tkt(username, (uid == 0 ? "root" : ""), lrealm,
290 "krbtgt", lrealm, (uid == 0 ? 2 : DEFAULT_TKT_LIFE), pw_buf);
291
292 bzero(pw_buf, sizeof(pw_buf));
293
294 if (kerno != KSUCCESS) {
295 if (kerno == KDC_PR_UNKNOWN)
296 return(1);
297 (void)printf("su: unable to su: %s\n", krb_err_txt[kerno]);
298 syslog(LOG_NOTICE|LOG_AUTH,
dbf8133b 299 "su: BAD Kerberos SU: %s on %s: %s", username, mytty(),
d9e13dc0
KB
300 krb_err_txt[kerno]);
301 return(1);
302 }
303
304 if (chown(krbtkfile, uid, -1) < 0) {
305 perror("su: chown:");
306 (void)unlink(krbtkfile);
307 return(1);
308 }
309
310 (void)setpriority(PRIO_PROCESS, 0, -2);
311
312 if (gethostname(hostname, sizeof(hostname)) == -1) {
313 perror("su: hostname");
314 dest_tkt();
315 return(1);
316 }
317
318 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
319 savehost[sizeof(savehost) - 1] = '\0';
320
321 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
322
323 if (kerno == KDC_PR_UNKNOWN) {
324 (void)printf("Warning: tgt not verified.\n");
325 syslog(LOG_NOTICE|LOG_AUTH, "su: %s on %s, tgt not verified",
dbf8133b 326 username, mytty());
d9e13dc0
KB
327 } else if (kerno != KSUCCESS) {
328 (void)printf("Unable to use tgt: %s\n", krb_err_txt[kerno]);
329 syslog(LOG_NOTICE|LOG_AUTH, "su: failed su: %s on %s: %s",
dbf8133b 330 username, mytty(), krb_err_txt[kerno]);
d9e13dc0
KB
331 dest_tkt();
332 return(1);
333 } else {
334 if (!(hp = gethostbyname(hostname))) {
335 (void)printf("su: can't get addr of %s\n", hostname);
336 dest_tkt();
337 return(1);
338 }
339 (void)bcopy((char *)hp->h_addr, (char *)&faddr, sizeof(faddr));
340
341 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
342 &authdata, "")) != KSUCCESS) {
343 (void)printf("su: unable to verify rcmd ticket: %s\n",
344 krb_err_txt[kerno]);
345 syslog(LOG_NOTICE|LOG_AUTH,
346 "su: failed su: %s on %s: %s", username,
dbf8133b 347 mytty(), krb_err_txt[kerno]);
d9e13dc0
KB
348 dest_tkt();
349 return(1);
350 }
351 }
352 return(0);
353}
354
094e5ff2 355koktologin(name, realm, toname)
d9e13dc0 356 char *name, *realm, *toname;
094e5ff2 357{
d9e13dc0
KB
358 register AUTH_DAT *kdata;
359 AUTH_DAT kdata_st;
094e5ff2 360
d9e13dc0 361 kdata = &kdata_st;
094e5ff2 362 bzero((caddr_t) kdata, sizeof(*kdata));
d9e13dc0
KB
363 (void)strcpy(kdata->pname, name);
364 (void)strcpy(kdata->pinst,
365 ((strcmp(toname, "root") == 0) ? "root" : ""));
366 (void)strcpy(kdata->prealm, realm);
094e5ff2
KF
367 return(kuserok(kdata, toname));
368}
369#endif