su.c revision 9502
121308Sache/*
221308Sache * Copyright (c) 1988, 1993, 1994
321308Sache *	The Regents of the University of California.  All rights reserved.
4165670Sache *
521308Sache * Redistribution and use in source and binary forms, with or without
621308Sache * modification, are permitted provided that the following conditions
721308Sache * are met:
821308Sache * 1. Redistributions of source code must retain the above copyright
921308Sache *    notice, this list of conditions and the following disclaimer.
1021308Sache * 2. Redistributions in binary form must reproduce the above copyright
1158310Sache *    notice, this list of conditions and the following disclaimer in the
1221308Sache *    documentation and/or other materials provided with the distribution.
1321308Sache * 3. All advertising materials mentioning features or use of this software
1421308Sache *    must display the following acknowledgement:
1521308Sache *	This product includes software developed by the University of
1621308Sache *	California, Berkeley and its contributors.
1721308Sache * 4. Neither the name of the University nor the names of its contributors
1821308Sache *    may be used to endorse or promote products derived from this software
1921308Sache *    without specific prior written permission.
2021308Sache *
2121308Sache * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2258310Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2321308Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2421308Sache * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2521308Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2621308Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2721308Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2821308Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2921308Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3021308Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3121308Sache * SUCH DAMAGE.
3221308Sache */
3321308Sache
3421308Sache#ifndef lint
3521308Sachestatic char copyright[] =
3621308Sache"@(#) Copyright (c) 1988, 1993, 1994\n\
3721308Sache	The Regents of the University of California.  All rights reserved.\n";
3821308Sache#endif /* not lint */
3921308Sache
4021308Sache#ifndef lint
4121308Sachestatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
4221308Sache#endif /* not lint */
4321308Sache
4421308Sache#include <sys/param.h>
4521308Sache#include <sys/time.h>
4621308Sache#include <sys/resource.h>
4721308Sache
4821308Sache#include <err.h>
4921308Sache#include <errno.h>
5058310Sache#include <grp.h>
51119610Sache#include <paths.h>
5258310Sache#include <pwd.h>
53165670Sache#include <stdio.h>
54165670Sache#include <stdlib.h>
5521308Sache#include <string.h>
5621308Sache#include <syslog.h>
5721308Sache#include <unistd.h>
5821308Sache
5921308Sache#ifdef	SKEY
6021308Sache#include <skey.h>
6121308Sache#endif
6221308Sache
6321308Sache#ifdef KERBEROS
6421308Sache#include <kerberosIV/des.h>
6521308Sache#include <kerberosIV/krb.h>
6621308Sache#include <netdb.h>
6721308Sache
6821308Sache#define	ARGSTR	"-Kflm"
6921308Sache
7021308Sacheint use_kerberos = 1;
71165670Sache#else
72165670Sache#define	ARGSTR	"-flm"
73165670Sache#endif
74165670Sache
75165670Sachechar   *ontty __P((void));
76165670Sacheint	chshell __P((char *));
77165670Sache
78165670Sacheint
79165670Sachemain(argc, argv)
80165670Sache	int argc;
81165670Sache	char **argv;
82165670Sache{
83165670Sache	extern char **environ;
84165670Sache	struct passwd *pwd;
85165670Sache#ifdef WHEELSU
86165670Sache	char *targetpass;
87165670Sache	int iswheelsu;
88165670Sache#endif /* WHEELSU */
8921308Sache	char *p, **g, *user, *shell, *username, *cleanenv[20], *nargv[4], **np;
9021308Sache	struct group *gr;
9121308Sache	uid_t ruid;
9221308Sache	int asme, ch, asthem, fastlogin, prio;
9321308Sache	enum { UNSET, YES, NO } iscsh = UNSET;
9421308Sache	char shellbuf[MAXPATHLEN];
9521308Sache
9621308Sache	np = &nargv[3];
97165670Sache	*np-- = NULL;
98165670Sache#ifdef WHEELSU
99165670Sache	iswheelsu =
10021308Sache#endif /* WHEELSU */
10121308Sache	asme = asthem = fastlogin = 0;
10221308Sache	while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
10321308Sache		switch((char)ch) {
10421308Sache#ifdef KERBEROS
10521308Sache		case 'K':
10675406Sache			use_kerberos = 0;
10721308Sache			break;
108165670Sache#endif
109165670Sache		case 'f':
110165670Sache			fastlogin = 1;
11121308Sache			break;
11221308Sache		case '-':
113165670Sache		case 'l':
11421308Sache			asme = 0;
11521308Sache			asthem = 1;
11621308Sache			break;
11721308Sache		case 'm':
11821308Sache			asme = 1;
11921308Sache			asthem = 0;
12021308Sache			break;
12121308Sache		case '?':
122165670Sache		default:
12321308Sache			(void)fprintf(stderr, "usage: su [%s] [login]\n",
12421308Sache			    ARGSTR);
125165670Sache			exit(1);
126165670Sache		}
127165670Sache	argv += optind;
128165670Sache
129165670Sache	errno = 0;
130165670Sache	prio = getpriority(PRIO_PROCESS, 0);
131165670Sache	if (errno)
132165670Sache		prio = 0;
133165670Sache	(void)setpriority(PRIO_PROCESS, 0, -2);
134165670Sache	openlog("su", LOG_CONS, 0);
135165670Sache
136165670Sache	/* get current login name and shell */
137165670Sache	ruid = getuid();
138165670Sache	username = getlogin();
139165670Sache	if (username == NULL || (pwd = getpwnam(username)) == NULL ||
140165670Sache	    pwd->pw_uid != ruid)
141165670Sache		pwd = getpwuid(ruid);
142165670Sache	if (pwd == NULL)
143165670Sache		errx(1, "who are you?");
144165670Sache	username = strdup(pwd->pw_name);
145165670Sache	if (username == NULL)
146165670Sache		err(1, NULL);
147165670Sache	if (asme)
148165670Sache		if (pwd->pw_shell && *pwd->pw_shell)
149165670Sache			shell = strcpy(shellbuf,  pwd->pw_shell);
150165670Sache		else {
151165670Sache			shell = _PATH_BSHELL;
152165670Sache			iscsh = NO;
153165670Sache		}
154165670Sache
155165670Sache	/* get target login information, default to root */
156165670Sache	user = *argv ? *argv : "root";
157165670Sache	if ((pwd = getpwnam(user)) == NULL) {
158165670Sache		errx(1, "unknown login: %s", user);
159165670Sache	}
160165670Sache
16121308Sache#ifdef WHEELSU
16221308Sache	targetpass = strdup(pwd->pw_passwd);
16321308Sache#endif /* WHEELSU */
16421308Sache
16521308Sache	if (ruid) {
16621308Sache#ifdef KERBEROS
16775406Sache	    if (!use_kerberos || kerberos(username, user, pwd->pw_uid))
16821308Sache#endif
16921308Sache	    {
17021308Sache		/* only allow those in group zero to su to root. */
17175406Sache		if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)))
17221308Sache			for (g = gr->gr_mem;; ++g) {
17321308Sache				if (!*g)
17421308Sache					errx(1,
17521308Sache			    "you are not in the correct group to su %s.",
17621308Sache					    user);
17721308Sache				if (strcmp(username, *g) == 0) {
17875406Sache#ifdef WHEELSU
17921308Sache					iswheelsu = 1;
18021308Sache#endif /* WHEELSU */
18121308Sache					break;
18221308Sache				}
18321308Sache			}
18421308Sache		/* if target requires a password, verify it */
18521308Sache		if (*pwd->pw_passwd) {
18621308Sache#ifdef	SKEY
18721308Sache#ifdef WHEELSU
18821308Sache			if (iswheelsu) {
18921308Sache				pwd = getpwnam(username);
19021308Sache			}
19121308Sache#endif /* WHEELSU */
19221308Sache			p = skey_getpass("Password:", pwd, 1);
19321308Sache			if (!(!strcmp(pwd->pw_passwd,
19421308Sache				      skey_crypt(p, pwd->pw_passwd, pwd, 1))
19521308Sache#ifdef WHEELSU
19621308Sache			      || (iswheelsu && !strcmp(targetpass,
19721308Sache						       crypt(p,
19821308Sache							     targetpass)))
19921308Sache#endif /* WHEELSU */
20021308Sache			      )) {
20121308Sache#else
20221308Sache			p = getpass("Password:");
20321308Sache			if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
20421308Sache#endif
20521308Sache				fprintf(stderr, "Sorry\n");
20621308Sache				syslog(LOG_AUTH|LOG_WARNING,
20721308Sache					"BAD SU %s to %s%s", username,
20821308Sache					user, ontty());
20921308Sache				exit(1);
21021308Sache			}
21121308Sache#ifdef WHEELSU
21221308Sache			if (iswheelsu) {
21375406Sache				pwd = getpwnam(user);
21421308Sache			}
21521308Sache#endif /* WHEELSU */
21621308Sache		}
21721308Sache	    }
21875406Sache	}
21921308Sache
22021308Sache	if (asme) {
22121308Sache		/* if asme and non-standard target shell, must be root */
222165670Sache		if (!chshell(pwd->pw_shell) && ruid)
223165670Sache			errx(1, "permission denied (shell).");
22421308Sache	} else if (pwd->pw_shell && *pwd->pw_shell) {
22521308Sache		shell = pwd->pw_shell;
22621308Sache		iscsh = UNSET;
22721308Sache	} else {
22821308Sache		shell = _PATH_BSHELL;
22921308Sache		iscsh = NO;
23021308Sache	}
23121308Sache
23221308Sache	/* if we're forking a csh, we want to slightly muck the args */
23321308Sache	if (iscsh == UNSET) {
23421308Sache		if (p = strrchr(shell, '/'))
23521308Sache			++p;
23621308Sache		else
23721308Sache			p = shell;
23821308Sache		if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
23921308Sache		    iscsh = strcmp(p, "tcsh") ? NO : YES;
24021308Sache	}
24121308Sache
24221308Sache	/* set permissions */
24321308Sache	if (setgid(pwd->pw_gid) < 0)
24421308Sache		err(1, "setgid");
24521308Sache	if (initgroups(user, pwd->pw_gid))
24621308Sache		errx(1, "initgroups failed");
24721308Sache	if (setuid(pwd->pw_uid) < 0)
24821308Sache		err(1, "setuid");
24921308Sache
25021308Sache	if (!asme) {
25121308Sache		if (asthem) {
25221308Sache			p = getenv("TERM");
25321308Sache			cleanenv[0] = NULL;
25421308Sache			environ = cleanenv;
25521308Sache			(void)setenv("PATH", _PATH_DEFPATH, 1);
25621308Sache			(void)setenv("TERM", p, 1);
25721308Sache			if (chdir(pwd->pw_dir) < 0)
25821308Sache				errx(1, "no directory");
25921308Sache		}
26021308Sache		if (asthem || pwd->pw_uid)
26121308Sache			(void)setenv("USER", pwd->pw_name, 1);
26221308Sache		(void)setenv("HOME", pwd->pw_dir, 1);
26321308Sache		(void)setenv("SHELL", shell, 1);
26421308Sache	}
26521308Sache
26621308Sache	if (iscsh == YES) {
26721308Sache		if (fastlogin)
26821308Sache			*np-- = "-f";
26921308Sache		if (asme)
27021308Sache			*np-- = "-m";
27121308Sache	}
27221308Sache
27321308Sache	/* csh strips the first character... */
27421308Sache	*np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
27521308Sache
27621308Sache	if (ruid != 0)
27721308Sache		syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
27821308Sache		    username, user, ontty());
27921308Sache
28021308Sache	(void)setpriority(PRIO_PROCESS, 0, prio);
28121308Sache
28221308Sache	execv(shell, np);
28321308Sache	err(1, "%s", shell);
28421308Sache}
28521308Sache
28621308Sacheint
28721308Sachechshell(sh)
28821308Sache	char *sh;
28921308Sache{
29021308Sache	char *cp;
29121308Sache
29221308Sache	while ((cp = getusershell()) != NULL)
29321308Sache		if (strcmp(cp, sh) == 0)
29421308Sache			return (1);
29575406Sache	return (0);
29621308Sache}
29721308Sache
29821308Sachechar *
29921308Sacheontty()
300157184Sache{
301157184Sache	char *p;
302157184Sache	static char buf[MAXPATHLEN + 4];
303157184Sache
30421308Sache	buf[0] = 0;
305157184Sache	if (p = ttyname(STDERR_FILENO))
30621308Sache		snprintf(buf, sizeof(buf), " on %s", p);
30721308Sache	return (buf);
30821308Sache}
30921308Sache
31021308Sache#ifdef KERBEROS
31121308Sachekerberos(username, user, uid)
31221308Sache	char *username, *user;
31321308Sache	int uid;
31421308Sache{
31521308Sache	extern char *krb_err_txt[];
31621308Sache	KTEXT_ST ticket;
31721308Sache	AUTH_DAT authdata;
31821308Sache	struct hostent *hp;
31921308Sache	char *p;
32021308Sache	int kerno;
32121308Sache	u_long faddr;
32221308Sache	char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
32375406Sache	char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
32421308Sache	char *krb_get_phost();
32521308Sache
32621308Sache	if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
32721308Sache		return (1);
32821308Sache	if (koktologin(username, lrealm, user) && !uid) {
329		warnx("kerberos: not in %s's ACL.", user);
330		return (1);
331	}
332	(void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid());
333
334	(void)setenv("KRBTKFILE", krbtkfile, 1);
335	(void)krb_set_tkt_string(krbtkfile);
336	/*
337	 * Set real as well as effective ID to 0 for the moment,
338	 * to make the kerberos library do the right thing.
339	 */
340	if (setuid(0) < 0) {
341		warn("setuid");
342		return (1);
343	}
344
345	/*
346	 * Little trick here -- if we are su'ing to root,
347	 * we need to get a ticket for "xxx.root", where xxx represents
348	 * the name of the person su'ing.  Otherwise (non-root case),
349	 * we need to get a ticket for "yyy.", where yyy represents
350	 * the name of the person being su'd to, and the instance is null
351	 *
352	 * We should have a way to set the ticket lifetime,
353	 * with a system default for root.
354	 */
355	kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
356		(uid == 0 ? "root" : ""), lrealm,
357	    	"krbtgt", lrealm, DEFAULT_TKT_LIFE, 0);
358
359	if (kerno != KSUCCESS) {
360		if (kerno == KDC_PR_UNKNOWN) {
361			warnx("kerberos: principal unknown: %s.%s@%s",
362				(uid == 0 ? username : user),
363				(uid == 0 ? "root" : ""), lrealm);
364			return (1);
365		}
366		warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
367		syslog(LOG_NOTICE|LOG_AUTH,
368		    "BAD Kerberos SU: %s to %s%s: %s",
369		    username, user, ontty(), krb_err_txt[kerno]);
370		return (1);
371	}
372
373	if (chown(krbtkfile, uid, -1) < 0) {
374		warn("chown");
375		(void)unlink(krbtkfile);
376		return (1);
377	}
378
379	(void)setpriority(PRIO_PROCESS, 0, -2);
380
381	if (gethostname(hostname, sizeof(hostname)) == -1) {
382		warn("gethostname");
383		dest_tkt();
384		return (1);
385	}
386
387	(void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
388	savehost[sizeof(savehost) - 1] = '\0';
389
390	kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
391
392	if (kerno == KDC_PR_UNKNOWN) {
393		warnx("Warning: TGT not verified.");
394		syslog(LOG_NOTICE|LOG_AUTH,
395		    "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
396		    username, user, ontty(), krb_err_txt[kerno],
397		    "rcmd", savehost);
398	} else if (kerno != KSUCCESS) {
399		warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
400		syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
401		    username, user, ontty(), krb_err_txt[kerno]);
402		dest_tkt();
403		return (1);
404	} else {
405		if (!(hp = gethostbyname(hostname))) {
406			warnx("can't get addr of %s", hostname);
407			dest_tkt();
408			return (1);
409		}
410		memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
411
412		if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
413		    &authdata, "")) != KSUCCESS) {
414			warnx("kerberos: unable to verify rcmd ticket: %s\n",
415			    krb_err_txt[kerno]);
416			syslog(LOG_NOTICE|LOG_AUTH,
417			    "failed su: %s to %s%s: %s", username,
418			     user, ontty(), krb_err_txt[kerno]);
419			dest_tkt();
420			return (1);
421		}
422	}
423	return (0);
424}
425
426koktologin(name, realm, toname)
427	char *name, *realm, *toname;
428{
429	AUTH_DAT *kdata;
430	AUTH_DAT kdata_st;
431
432	kdata = &kdata_st;
433	memset((char *)kdata, 0, sizeof(*kdata));
434	(void)strcpy(kdata->pname, name);
435	(void)strcpy(kdata->pinst,
436	    ((strcmp(toname, "root") == 0) ? "root" : ""));
437	(void)strcpy(kdata->prealm, realm);
438	return (kuserok(kdata, toname));
439}
440#endif
441