su.c revision 3702
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1988, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
351590Srgrimesstatic char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381590Srgrimes#endif /* not lint */
391590Srgrimes
401590Srgrimes#ifndef lint
411590Srgrimesstatic char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
421590Srgrimes#endif /* not lint */
431590Srgrimes
441590Srgrimes#include <sys/param.h>
451590Srgrimes#include <sys/time.h>
461590Srgrimes#include <sys/resource.h>
471590Srgrimes
481590Srgrimes#include <err.h>
491590Srgrimes#include <errno.h>
501590Srgrimes#include <grp.h>
511590Srgrimes#include <paths.h>
521590Srgrimes#include <pwd.h>
531590Srgrimes#include <stdio.h>
541590Srgrimes#include <stdlib.h>
551590Srgrimes#include <string.h>
561590Srgrimes#include <syslog.h>
571590Srgrimes#include <unistd.h>
581590Srgrimes
593702Spst#ifdef	SKEY
603702Spst#include <skey.h>
613702Spst#endif
623702Spst
631590Srgrimes#ifdef KERBEROS
641590Srgrimes#include <kerberosIV/des.h>
651590Srgrimes#include <kerberosIV/krb.h>
661590Srgrimes#include <netdb.h>
671590Srgrimes
681590Srgrimes#define	ARGSTR	"-Kflm"
691590Srgrimes
701590Srgrimesint use_kerberos = 1;
711590Srgrimes#else
721590Srgrimes#define	ARGSTR	"-flm"
731590Srgrimes#endif
741590Srgrimes
751590Srgrimeschar   *ontty __P((void));
761590Srgrimesint	chshell __P((char *));
771590Srgrimes
781590Srgrimesint
791590Srgrimesmain(argc, argv)
801590Srgrimes	int argc;
811590Srgrimes	char **argv;
821590Srgrimes{
831590Srgrimes	extern char **environ;
841590Srgrimes	struct passwd *pwd;
851590Srgrimes	char *p, **g, *user, *shell, *username, *cleanenv[2], *nargv[4], **np;
861590Srgrimes	struct group *gr;
871590Srgrimes	uid_t ruid;
881590Srgrimes	int asme, ch, asthem, fastlogin, prio;
891590Srgrimes	enum { UNSET, YES, NO } iscsh = UNSET;
901590Srgrimes	char shellbuf[MAXPATHLEN];
911590Srgrimes
921590Srgrimes	np = &nargv[3];
931590Srgrimes	*np-- = NULL;
941590Srgrimes	asme = asthem = fastlogin = 0;
951590Srgrimes	while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
961590Srgrimes		switch((char)ch) {
971590Srgrimes#ifdef KERBEROS
981590Srgrimes		case 'K':
991590Srgrimes			use_kerberos = 0;
1001590Srgrimes			break;
1011590Srgrimes#endif
1021590Srgrimes		case 'f':
1031590Srgrimes			fastlogin = 1;
1041590Srgrimes			break;
1051590Srgrimes		case '-':
1061590Srgrimes		case 'l':
1071590Srgrimes			asme = 0;
1081590Srgrimes			asthem = 1;
1091590Srgrimes			break;
1101590Srgrimes		case 'm':
1111590Srgrimes			asme = 1;
1121590Srgrimes			asthem = 0;
1131590Srgrimes			break;
1141590Srgrimes		case '?':
1151590Srgrimes		default:
1161590Srgrimes			(void)fprintf(stderr, "usage: su [%s] [login]\n",
1171590Srgrimes			    ARGSTR);
1181590Srgrimes			exit(1);
1191590Srgrimes		}
1201590Srgrimes	argv += optind;
1211590Srgrimes
1221590Srgrimes	errno = 0;
1231590Srgrimes	prio = getpriority(PRIO_PROCESS, 0);
1241590Srgrimes	if (errno)
1251590Srgrimes		prio = 0;
1261590Srgrimes	(void)setpriority(PRIO_PROCESS, 0, -2);
1271590Srgrimes	openlog("su", LOG_CONS, 0);
1281590Srgrimes
1291590Srgrimes	/* get current login name and shell */
1301590Srgrimes	ruid = getuid();
1311590Srgrimes	username = getlogin();
1321590Srgrimes	if (username == NULL || (pwd = getpwnam(username)) == NULL ||
1331590Srgrimes	    pwd->pw_uid != ruid)
1341590Srgrimes		pwd = getpwuid(ruid);
1351590Srgrimes	if (pwd == NULL)
1361590Srgrimes		errx(1, "who are you?");
1371590Srgrimes	username = strdup(pwd->pw_name);
1381590Srgrimes	if (username == NULL)
1391590Srgrimes		err(1, NULL);
1401590Srgrimes	if (asme)
1411590Srgrimes		if (pwd->pw_shell && *pwd->pw_shell)
1421590Srgrimes			shell = strcpy(shellbuf,  pwd->pw_shell);
1431590Srgrimes		else {
1441590Srgrimes			shell = _PATH_BSHELL;
1451590Srgrimes			iscsh = NO;
1461590Srgrimes		}
1471590Srgrimes
1481590Srgrimes	/* get target login information, default to root */
1491590Srgrimes	user = *argv ? *argv : "root";
1501590Srgrimes	if ((pwd = getpwnam(user)) == NULL) {
1511590Srgrimes		fprintf(stderr, "su: unknown login %s\n", user);
1521590Srgrimes		exit(1);
1531590Srgrimes	}
1541590Srgrimes
1551590Srgrimes	if (ruid) {
1561590Srgrimes#ifdef KERBEROS
1571590Srgrimes	    if (!use_kerberos || kerberos(username, user, pwd->pw_uid))
1581590Srgrimes#endif
1591590Srgrimes	    {
1601590Srgrimes		/* only allow those in group zero to su to root. */
1611590Srgrimes		if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)))
1621590Srgrimes			for (g = gr->gr_mem;; ++g) {
1631590Srgrimes				if (!*g)
1641590Srgrimes					errx(1,
1651590Srgrimes			    "you are not in the correct group to su %s.",
1661590Srgrimes					    user);
1671590Srgrimes				if (strcmp(username, *g) == 0)
1681590Srgrimes					break;
1691590Srgrimes		}
1701590Srgrimes		/* if target requires a password, verify it */
1711590Srgrimes		if (*pwd->pw_passwd) {
1723208Spst#ifdef	SKEY
1733208Spst			p = skey_getpass("Password:", pwd, 1);
1743208Spst			if (strcmp(pwd->pw_passwd,
1753208Spst				   skey_crypt(p, pwd->pw_passwd, pwd, 1))) {
1763208Spst#else
1771590Srgrimes			p = getpass("Password:");
1781590Srgrimes			if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
1793208Spst#endif
1801590Srgrimes				fprintf(stderr, "Sorry\n");
1811590Srgrimes				syslog(LOG_AUTH|LOG_WARNING,
1821590Srgrimes					"BAD SU %s to %s%s", username,
1831590Srgrimes					user, ontty());
1841590Srgrimes				exit(1);
1851590Srgrimes			}
1861590Srgrimes		}
1871590Srgrimes	    }
1881590Srgrimes	}
1891590Srgrimes
1901590Srgrimes	if (asme) {
1911590Srgrimes		/* if asme and non-standard target shell, must be root */
1921590Srgrimes		if (!chshell(pwd->pw_shell) && ruid)
1931590Srgrimes			errx(1, "permission denied (shell).");
1941590Srgrimes	} else if (pwd->pw_shell && *pwd->pw_shell) {
1951590Srgrimes		shell = pwd->pw_shell;
1961590Srgrimes		iscsh = UNSET;
1971590Srgrimes	} else {
1981590Srgrimes		shell = _PATH_BSHELL;
1991590Srgrimes		iscsh = NO;
2001590Srgrimes	}
2011590Srgrimes
2021590Srgrimes	/* if we're forking a csh, we want to slightly muck the args */
2031590Srgrimes	if (iscsh == UNSET) {
2041590Srgrimes		if (p = strrchr(shell, '/'))
2051590Srgrimes			++p;
2061590Srgrimes		else
2071590Srgrimes			p = shell;
2081590Srgrimes		iscsh = strcmp(p, "csh") ? NO : YES;
2091590Srgrimes	}
2101590Srgrimes
2111590Srgrimes	/* set permissions */
2121590Srgrimes	if (setgid(pwd->pw_gid) < 0)
2131590Srgrimes		err(1, "setgid");
2141590Srgrimes	if (initgroups(user, pwd->pw_gid))
2151590Srgrimes		errx(1, "initgroups failed");
2161590Srgrimes	if (setuid(pwd->pw_uid) < 0)
2171590Srgrimes		err(1, "setuid");
2181590Srgrimes
2191590Srgrimes	if (!asme) {
2201590Srgrimes		if (asthem) {
2211590Srgrimes			p = getenv("TERM");
2221590Srgrimes			cleanenv[0] = _PATH_DEFPATH;
2231590Srgrimes			cleanenv[1] = NULL;
2241590Srgrimes			environ = cleanenv;
2251590Srgrimes			(void)setenv("TERM", p, 1);
2261590Srgrimes			if (chdir(pwd->pw_dir) < 0)
2271590Srgrimes				errx(1, "no directory");
2281590Srgrimes		}
2291590Srgrimes		if (asthem || pwd->pw_uid)
2301590Srgrimes			(void)setenv("USER", pwd->pw_name, 1);
2311590Srgrimes		(void)setenv("HOME", pwd->pw_dir, 1);
2321590Srgrimes		(void)setenv("SHELL", shell, 1);
2331590Srgrimes	}
2341590Srgrimes
2351590Srgrimes	if (iscsh == YES) {
2361590Srgrimes		if (fastlogin)
2371590Srgrimes			*np-- = "-f";
2381590Srgrimes		if (asme)
2391590Srgrimes			*np-- = "-m";
2401590Srgrimes	}
2411590Srgrimes
2421590Srgrimes	/* csh strips the first character... */
2431590Srgrimes	*np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
2441590Srgrimes
2451590Srgrimes	if (ruid != 0)
2461590Srgrimes		syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
2471590Srgrimes		    username, user, ontty());
2481590Srgrimes
2491590Srgrimes	(void)setpriority(PRIO_PROCESS, 0, prio);
2501590Srgrimes
2511590Srgrimes	execv(shell, np);
2521590Srgrimes	err(1, "%s", shell);
2531590Srgrimes}
2541590Srgrimes
2551590Srgrimesint
2561590Srgrimeschshell(sh)
2571590Srgrimes	char *sh;
2581590Srgrimes{
2591590Srgrimes	char *cp;
2601590Srgrimes
2611590Srgrimes	while ((cp = getusershell()) != NULL)
2621590Srgrimes		if (strcmp(cp, sh) == 0)
2631590Srgrimes			return (1);
2641590Srgrimes	return (0);
2651590Srgrimes}
2661590Srgrimes
2671590Srgrimeschar *
2681590Srgrimesontty()
2691590Srgrimes{
2701590Srgrimes	char *p;
2711590Srgrimes	static char buf[MAXPATHLEN + 4];
2721590Srgrimes
2731590Srgrimes	buf[0] = 0;
2741590Srgrimes	if (p = ttyname(STDERR_FILENO))
2751590Srgrimes		snprintf(buf, sizeof(buf), " on %s", p);
2761590Srgrimes	return (buf);
2771590Srgrimes}
2781590Srgrimes
2791590Srgrimes#ifdef KERBEROS
2801590Srgrimeskerberos(username, user, uid)
2811590Srgrimes	char *username, *user;
2821590Srgrimes	int uid;
2831590Srgrimes{
2841590Srgrimes	extern char *krb_err_txt[];
2851590Srgrimes	KTEXT_ST ticket;
2861590Srgrimes	AUTH_DAT authdata;
2871590Srgrimes	struct hostent *hp;
2881590Srgrimes	char *p;
2891590Srgrimes	int kerno;
2901590Srgrimes	u_long faddr;
2911590Srgrimes	char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
2921590Srgrimes	char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
2931590Srgrimes	char *krb_get_phost();
2941590Srgrimes
2951590Srgrimes	if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
2961590Srgrimes		return (1);
2971590Srgrimes	if (koktologin(username, lrealm, user) && !uid) {
2981590Srgrimes		warnx("kerberos: not in %s's ACL.", user);
2991590Srgrimes		return (1);
3001590Srgrimes	}
3011590Srgrimes	(void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid());
3021590Srgrimes
3031590Srgrimes	(void)setenv("KRBTKFILE", krbtkfile, 1);
3041590Srgrimes	(void)krb_set_tkt_string(krbtkfile);
3051590Srgrimes	/*
3061590Srgrimes	 * Set real as well as effective ID to 0 for the moment,
3071590Srgrimes	 * to make the kerberos library do the right thing.
3081590Srgrimes	 */
3091590Srgrimes	if (setuid(0) < 0) {
3101590Srgrimes		warn("setuid");
3111590Srgrimes		return (1);
3121590Srgrimes	}
3131590Srgrimes
3141590Srgrimes	/*
3151590Srgrimes	 * Little trick here -- if we are su'ing to root,
3161590Srgrimes	 * we need to get a ticket for "xxx.root", where xxx represents
3171590Srgrimes	 * the name of the person su'ing.  Otherwise (non-root case),
3181590Srgrimes	 * we need to get a ticket for "yyy.", where yyy represents
3191590Srgrimes	 * the name of the person being su'd to, and the instance is null
3201590Srgrimes	 *
3211590Srgrimes	 * We should have a way to set the ticket lifetime,
3221590Srgrimes	 * with a system default for root.
3231590Srgrimes	 */
3241590Srgrimes	kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
3251590Srgrimes		(uid == 0 ? "root" : ""), lrealm,
3261590Srgrimes	    	"krbtgt", lrealm, DEFAULT_TKT_LIFE, 0);
3271590Srgrimes
3281590Srgrimes	if (kerno != KSUCCESS) {
3291590Srgrimes		if (kerno == KDC_PR_UNKNOWN) {
3301590Srgrimes			warnx("kerberos: principal unknown: %s.%s@%s",
3311590Srgrimes				(uid == 0 ? username : user),
3321590Srgrimes				(uid == 0 ? "root" : ""), lrealm);
3331590Srgrimes			return (1);
3341590Srgrimes		}
3351590Srgrimes		warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
3361590Srgrimes		syslog(LOG_NOTICE|LOG_AUTH,
3371590Srgrimes		    "BAD Kerberos SU: %s to %s%s: %s",
3381590Srgrimes		    username, user, ontty(), krb_err_txt[kerno]);
3391590Srgrimes		return (1);
3401590Srgrimes	}
3411590Srgrimes
3421590Srgrimes	if (chown(krbtkfile, uid, -1) < 0) {
3431590Srgrimes		warn("chown");
3441590Srgrimes		(void)unlink(krbtkfile);
3451590Srgrimes		return (1);
3461590Srgrimes	}
3471590Srgrimes
3481590Srgrimes	(void)setpriority(PRIO_PROCESS, 0, -2);
3491590Srgrimes
3501590Srgrimes	if (gethostname(hostname, sizeof(hostname)) == -1) {
3511590Srgrimes		warn("gethostname");
3521590Srgrimes		dest_tkt();
3531590Srgrimes		return (1);
3541590Srgrimes	}
3551590Srgrimes
3561590Srgrimes	(void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
3571590Srgrimes	savehost[sizeof(savehost) - 1] = '\0';
3581590Srgrimes
3591590Srgrimes	kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
3601590Srgrimes
3611590Srgrimes	if (kerno == KDC_PR_UNKNOWN) {
3621590Srgrimes		warnx("Warning: TGT not verified.");
3631590Srgrimes		syslog(LOG_NOTICE|LOG_AUTH,
3641590Srgrimes		    "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
3651590Srgrimes		    username, user, ontty(), krb_err_txt[kerno],
3661590Srgrimes		    "rcmd", savehost);
3671590Srgrimes	} else if (kerno != KSUCCESS) {
3681590Srgrimes		warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
3691590Srgrimes		syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
3701590Srgrimes		    username, user, ontty(), krb_err_txt[kerno]);
3711590Srgrimes		dest_tkt();
3721590Srgrimes		return (1);
3731590Srgrimes	} else {
3741590Srgrimes		if (!(hp = gethostbyname(hostname))) {
3751590Srgrimes			warnx("can't get addr of %s", hostname);
3761590Srgrimes			dest_tkt();
3771590Srgrimes			return (1);
3781590Srgrimes		}
3791590Srgrimes		memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
3801590Srgrimes
3811590Srgrimes		if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
3821590Srgrimes		    &authdata, "")) != KSUCCESS) {
3831590Srgrimes			warnx("kerberos: unable to verify rcmd ticket: %s\n",
3841590Srgrimes			    krb_err_txt[kerno]);
3851590Srgrimes			syslog(LOG_NOTICE|LOG_AUTH,
3861590Srgrimes			    "failed su: %s to %s%s: %s", username,
3871590Srgrimes			     user, ontty(), krb_err_txt[kerno]);
3881590Srgrimes			dest_tkt();
3891590Srgrimes			return (1);
3901590Srgrimes		}
3911590Srgrimes	}
3921590Srgrimes	return (0);
3931590Srgrimes}
3941590Srgrimes
3951590Srgrimeskoktologin(name, realm, toname)
3961590Srgrimes	char *name, *realm, *toname;
3971590Srgrimes{
3981590Srgrimes	AUTH_DAT *kdata;
3991590Srgrimes	AUTH_DAT kdata_st;
4001590Srgrimes
4011590Srgrimes	kdata = &kdata_st;
4021590Srgrimes	memset((char *)kdata, 0, sizeof(*kdata));
4031590Srgrimes	(void)strcpy(kdata->pname, name);
4041590Srgrimes	(void)strcpy(kdata->pinst,
4051590Srgrimes	    ((strcmp(toname, "root") == 0) ? "root" : ""));
4061590Srgrimes	(void)strcpy(kdata->prealm, realm);
4071590Srgrimes	return (kuserok(kdata, toname));
4081590Srgrimes}
4091590Srgrimes#endif
410