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