Home | History | Annotate | Line # | Download | only in su
su.c revision 1.1.1.2
      1      1.1  cgd /*
      2  1.1.1.2  jtc  * Copyright (c) 1988, 1993, 1994
      3  1.1.1.2  jtc  *	The Regents of the University of California.  All rights reserved.
      4      1.1  cgd  *
      5      1.1  cgd  * Redistribution and use in source and binary forms, with or without
      6      1.1  cgd  * modification, are permitted provided that the following conditions
      7      1.1  cgd  * are met:
      8      1.1  cgd  * 1. Redistributions of source code must retain the above copyright
      9      1.1  cgd  *    notice, this list of conditions and the following disclaimer.
     10      1.1  cgd  * 2. Redistributions in binary form must reproduce the above copyright
     11      1.1  cgd  *    notice, this list of conditions and the following disclaimer in the
     12      1.1  cgd  *    documentation and/or other materials provided with the distribution.
     13      1.1  cgd  * 3. All advertising materials mentioning features or use of this software
     14      1.1  cgd  *    must display the following acknowledgement:
     15      1.1  cgd  *	This product includes software developed by the University of
     16      1.1  cgd  *	California, Berkeley and its contributors.
     17      1.1  cgd  * 4. Neither the name of the University nor the names of its contributors
     18      1.1  cgd  *    may be used to endorse or promote products derived from this software
     19      1.1  cgd  *    without specific prior written permission.
     20      1.1  cgd  *
     21      1.1  cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22      1.1  cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23      1.1  cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24      1.1  cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25      1.1  cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26      1.1  cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27      1.1  cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28      1.1  cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29      1.1  cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30      1.1  cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31      1.1  cgd  * SUCH DAMAGE.
     32      1.1  cgd  */
     33      1.1  cgd 
     34      1.1  cgd #ifndef lint
     35  1.1.1.2  jtc static char copyright[] =
     36  1.1.1.2  jtc "@(#) Copyright (c) 1988, 1993, 1994\n\
     37  1.1.1.2  jtc 	The Regents of the University of California.  All rights reserved.\n";
     38      1.1  cgd #endif /* not lint */
     39      1.1  cgd 
     40      1.1  cgd #ifndef lint
     41  1.1.1.2  jtc static char sccsid[] = "@(#)su.c	8.3 (Berkeley) 4/2/94";
     42      1.1  cgd #endif /* not lint */
     43      1.1  cgd 
     44      1.1  cgd #include <sys/param.h>
     45      1.1  cgd #include <sys/time.h>
     46      1.1  cgd #include <sys/resource.h>
     47  1.1.1.2  jtc 
     48  1.1.1.2  jtc #include <err.h>
     49  1.1.1.2  jtc #include <errno.h>
     50      1.1  cgd #include <grp.h>
     51  1.1.1.2  jtc #include <paths.h>
     52  1.1.1.2  jtc #include <pwd.h>
     53  1.1.1.2  jtc #include <stdio.h>
     54  1.1.1.2  jtc #include <stdlib.h>
     55      1.1  cgd #include <string.h>
     56  1.1.1.2  jtc #include <syslog.h>
     57      1.1  cgd #include <unistd.h>
     58      1.1  cgd 
     59      1.1  cgd #ifdef KERBEROS
     60      1.1  cgd #include <kerberosIV/des.h>
     61      1.1  cgd #include <kerberosIV/krb.h>
     62      1.1  cgd #include <netdb.h>
     63      1.1  cgd 
     64      1.1  cgd #define	ARGSTR	"-Kflm"
     65      1.1  cgd 
     66      1.1  cgd int use_kerberos = 1;
     67      1.1  cgd #else
     68      1.1  cgd #define	ARGSTR	"-flm"
     69      1.1  cgd #endif
     70      1.1  cgd 
     71  1.1.1.2  jtc char   *ontty __P((void));
     72  1.1.1.2  jtc int	chshell __P((char *));
     73  1.1.1.2  jtc 
     74  1.1.1.2  jtc int
     75      1.1  cgd main(argc, argv)
     76      1.1  cgd 	int argc;
     77      1.1  cgd 	char **argv;
     78      1.1  cgd {
     79      1.1  cgd 	extern char **environ;
     80  1.1.1.2  jtc 	struct passwd *pwd;
     81  1.1.1.2  jtc 	char *p, **g, *user, *shell, *username, *cleanenv[2], *nargv[4], **np;
     82      1.1  cgd 	struct group *gr;
     83  1.1.1.2  jtc 	uid_t ruid;
     84      1.1  cgd 	int asme, ch, asthem, fastlogin, prio;
     85      1.1  cgd 	enum { UNSET, YES, NO } iscsh = UNSET;
     86      1.1  cgd 	char shellbuf[MAXPATHLEN];
     87      1.1  cgd 
     88      1.1  cgd 	np = &nargv[3];
     89      1.1  cgd 	*np-- = NULL;
     90      1.1  cgd 	asme = asthem = fastlogin = 0;
     91      1.1  cgd 	while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
     92      1.1  cgd 		switch((char)ch) {
     93      1.1  cgd #ifdef KERBEROS
     94      1.1  cgd 		case 'K':
     95      1.1  cgd 			use_kerberos = 0;
     96      1.1  cgd 			break;
     97      1.1  cgd #endif
     98      1.1  cgd 		case 'f':
     99      1.1  cgd 			fastlogin = 1;
    100      1.1  cgd 			break;
    101      1.1  cgd 		case '-':
    102      1.1  cgd 		case 'l':
    103      1.1  cgd 			asme = 0;
    104      1.1  cgd 			asthem = 1;
    105      1.1  cgd 			break;
    106      1.1  cgd 		case 'm':
    107      1.1  cgd 			asme = 1;
    108      1.1  cgd 			asthem = 0;
    109      1.1  cgd 			break;
    110      1.1  cgd 		case '?':
    111      1.1  cgd 		default:
    112      1.1  cgd 			(void)fprintf(stderr, "usage: su [%s] [login]\n",
    113      1.1  cgd 			    ARGSTR);
    114      1.1  cgd 			exit(1);
    115      1.1  cgd 		}
    116      1.1  cgd 	argv += optind;
    117      1.1  cgd 
    118      1.1  cgd 	errno = 0;
    119      1.1  cgd 	prio = getpriority(PRIO_PROCESS, 0);
    120      1.1  cgd 	if (errno)
    121      1.1  cgd 		prio = 0;
    122      1.1  cgd 	(void)setpriority(PRIO_PROCESS, 0, -2);
    123      1.1  cgd 	openlog("su", LOG_CONS, 0);
    124      1.1  cgd 
    125      1.1  cgd 	/* get current login name and shell */
    126      1.1  cgd 	ruid = getuid();
    127      1.1  cgd 	username = getlogin();
    128      1.1  cgd 	if (username == NULL || (pwd = getpwnam(username)) == NULL ||
    129      1.1  cgd 	    pwd->pw_uid != ruid)
    130      1.1  cgd 		pwd = getpwuid(ruid);
    131  1.1.1.2  jtc 	if (pwd == NULL)
    132  1.1.1.2  jtc 		errx(1, "who are you?");
    133      1.1  cgd 	username = strdup(pwd->pw_name);
    134  1.1.1.2  jtc 	if (username == NULL)
    135  1.1.1.2  jtc 		err(1, NULL);
    136      1.1  cgd 	if (asme)
    137      1.1  cgd 		if (pwd->pw_shell && *pwd->pw_shell)
    138      1.1  cgd 			shell = strcpy(shellbuf,  pwd->pw_shell);
    139      1.1  cgd 		else {
    140      1.1  cgd 			shell = _PATH_BSHELL;
    141      1.1  cgd 			iscsh = NO;
    142      1.1  cgd 		}
    143      1.1  cgd 
    144      1.1  cgd 	/* get target login information, default to root */
    145      1.1  cgd 	user = *argv ? *argv : "root";
    146      1.1  cgd 	if ((pwd = getpwnam(user)) == NULL) {
    147      1.1  cgd 		fprintf(stderr, "su: unknown login %s\n", user);
    148      1.1  cgd 		exit(1);
    149      1.1  cgd 	}
    150      1.1  cgd 
    151      1.1  cgd 	if (ruid) {
    152      1.1  cgd #ifdef KERBEROS
    153      1.1  cgd 	    if (!use_kerberos || kerberos(username, user, pwd->pw_uid))
    154      1.1  cgd #endif
    155      1.1  cgd 	    {
    156      1.1  cgd 		/* only allow those in group zero to su to root. */
    157      1.1  cgd 		if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)))
    158      1.1  cgd 			for (g = gr->gr_mem;; ++g) {
    159  1.1.1.2  jtc 				if (!*g)
    160  1.1.1.2  jtc 					errx(1,
    161  1.1.1.2  jtc 			    "you are not in the correct group to su %s.",
    162      1.1  cgd 					    user);
    163  1.1.1.2  jtc 				if (strcmp(username, *g) == 0)
    164      1.1  cgd 					break;
    165      1.1  cgd 		}
    166      1.1  cgd 		/* if target requires a password, verify it */
    167      1.1  cgd 		if (*pwd->pw_passwd) {
    168      1.1  cgd 			p = getpass("Password:");
    169      1.1  cgd 			if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
    170      1.1  cgd 				fprintf(stderr, "Sorry\n");
    171      1.1  cgd 				syslog(LOG_AUTH|LOG_WARNING,
    172      1.1  cgd 					"BAD SU %s to %s%s", username,
    173      1.1  cgd 					user, ontty());
    174      1.1  cgd 				exit(1);
    175      1.1  cgd 			}
    176      1.1  cgd 		}
    177      1.1  cgd 	    }
    178      1.1  cgd 	}
    179      1.1  cgd 
    180      1.1  cgd 	if (asme) {
    181      1.1  cgd 		/* if asme and non-standard target shell, must be root */
    182  1.1.1.2  jtc 		if (!chshell(pwd->pw_shell) && ruid)
    183  1.1.1.2  jtc 			errx(1, "permission denied (shell).");
    184      1.1  cgd 	} else if (pwd->pw_shell && *pwd->pw_shell) {
    185      1.1  cgd 		shell = pwd->pw_shell;
    186      1.1  cgd 		iscsh = UNSET;
    187      1.1  cgd 	} else {
    188      1.1  cgd 		shell = _PATH_BSHELL;
    189      1.1  cgd 		iscsh = NO;
    190      1.1  cgd 	}
    191      1.1  cgd 
    192      1.1  cgd 	/* if we're forking a csh, we want to slightly muck the args */
    193      1.1  cgd 	if (iscsh == UNSET) {
    194  1.1.1.2  jtc 		if (p = strrchr(shell, '/'))
    195      1.1  cgd 			++p;
    196      1.1  cgd 		else
    197      1.1  cgd 			p = shell;
    198      1.1  cgd 		iscsh = strcmp(p, "csh") ? NO : YES;
    199      1.1  cgd 	}
    200      1.1  cgd 
    201      1.1  cgd 	/* set permissions */
    202  1.1.1.2  jtc 	if (setgid(pwd->pw_gid) < 0)
    203  1.1.1.2  jtc 		err(1, "setgid");
    204  1.1.1.2  jtc 	if (initgroups(user, pwd->pw_gid))
    205  1.1.1.2  jtc 		errx(1, "initgroups failed");
    206  1.1.1.2  jtc 	if (setuid(pwd->pw_uid) < 0)
    207  1.1.1.2  jtc 		err(1, "setuid");
    208      1.1  cgd 
    209      1.1  cgd 	if (!asme) {
    210      1.1  cgd 		if (asthem) {
    211      1.1  cgd 			p = getenv("TERM");
    212      1.1  cgd 			cleanenv[0] = _PATH_DEFPATH;
    213      1.1  cgd 			cleanenv[1] = NULL;
    214      1.1  cgd 			environ = cleanenv;
    215      1.1  cgd 			(void)setenv("TERM", p, 1);
    216  1.1.1.2  jtc 			if (chdir(pwd->pw_dir) < 0)
    217  1.1.1.2  jtc 				errx(1, "no directory");
    218      1.1  cgd 		}
    219      1.1  cgd 		if (asthem || pwd->pw_uid)
    220      1.1  cgd 			(void)setenv("USER", pwd->pw_name, 1);
    221      1.1  cgd 		(void)setenv("HOME", pwd->pw_dir, 1);
    222      1.1  cgd 		(void)setenv("SHELL", shell, 1);
    223      1.1  cgd 	}
    224      1.1  cgd 
    225      1.1  cgd 	if (iscsh == YES) {
    226      1.1  cgd 		if (fastlogin)
    227      1.1  cgd 			*np-- = "-f";
    228      1.1  cgd 		if (asme)
    229      1.1  cgd 			*np-- = "-m";
    230      1.1  cgd 	}
    231      1.1  cgd 
    232      1.1  cgd 	/* csh strips the first character... */
    233      1.1  cgd 	*np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
    234      1.1  cgd 
    235      1.1  cgd 	if (ruid != 0)
    236      1.1  cgd 		syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
    237      1.1  cgd 		    username, user, ontty());
    238      1.1  cgd 
    239      1.1  cgd 	(void)setpriority(PRIO_PROCESS, 0, prio);
    240      1.1  cgd 
    241      1.1  cgd 	execv(shell, np);
    242  1.1.1.2  jtc 	err(1, "%s", shell);
    243      1.1  cgd }
    244      1.1  cgd 
    245  1.1.1.2  jtc int
    246      1.1  cgd chshell(sh)
    247      1.1  cgd 	char *sh;
    248      1.1  cgd {
    249  1.1.1.2  jtc 	char *cp;
    250      1.1  cgd 
    251      1.1  cgd 	while ((cp = getusershell()) != NULL)
    252  1.1.1.2  jtc 		if (strcmp(cp, sh) == 0)
    253      1.1  cgd 			return (1);
    254      1.1  cgd 	return (0);
    255      1.1  cgd }
    256      1.1  cgd 
    257      1.1  cgd char *
    258      1.1  cgd ontty()
    259      1.1  cgd {
    260  1.1.1.2  jtc 	char *p;
    261      1.1  cgd 	static char buf[MAXPATHLEN + 4];
    262      1.1  cgd 
    263      1.1  cgd 	buf[0] = 0;
    264      1.1  cgd 	if (p = ttyname(STDERR_FILENO))
    265  1.1.1.2  jtc 		snprintf(buf, sizeof(buf), " on %s", p);
    266      1.1  cgd 	return (buf);
    267      1.1  cgd }
    268      1.1  cgd 
    269      1.1  cgd #ifdef KERBEROS
    270      1.1  cgd kerberos(username, user, uid)
    271      1.1  cgd 	char *username, *user;
    272      1.1  cgd 	int uid;
    273      1.1  cgd {
    274      1.1  cgd 	extern char *krb_err_txt[];
    275      1.1  cgd 	KTEXT_ST ticket;
    276      1.1  cgd 	AUTH_DAT authdata;
    277      1.1  cgd 	struct hostent *hp;
    278  1.1.1.2  jtc 	char *p;
    279      1.1  cgd 	int kerno;
    280      1.1  cgd 	u_long faddr;
    281      1.1  cgd 	char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
    282      1.1  cgd 	char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
    283  1.1.1.2  jtc 	char *krb_get_phost();
    284      1.1  cgd 
    285      1.1  cgd 	if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
    286      1.1  cgd 		return (1);
    287      1.1  cgd 	if (koktologin(username, lrealm, user) && !uid) {
    288  1.1.1.2  jtc 		warnx("kerberos: not in %s's ACL.", user);
    289      1.1  cgd 		return (1);
    290      1.1  cgd 	}
    291      1.1  cgd 	(void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid());
    292      1.1  cgd 
    293      1.1  cgd 	(void)setenv("KRBTKFILE", krbtkfile, 1);
    294      1.1  cgd 	(void)krb_set_tkt_string(krbtkfile);
    295      1.1  cgd 	/*
    296      1.1  cgd 	 * Set real as well as effective ID to 0 for the moment,
    297      1.1  cgd 	 * to make the kerberos library do the right thing.
    298      1.1  cgd 	 */
    299      1.1  cgd 	if (setuid(0) < 0) {
    300  1.1.1.2  jtc 		warn("setuid");
    301      1.1  cgd 		return (1);
    302      1.1  cgd 	}
    303      1.1  cgd 
    304      1.1  cgd 	/*
    305      1.1  cgd 	 * Little trick here -- if we are su'ing to root,
    306      1.1  cgd 	 * we need to get a ticket for "xxx.root", where xxx represents
    307      1.1  cgd 	 * the name of the person su'ing.  Otherwise (non-root case),
    308      1.1  cgd 	 * we need to get a ticket for "yyy.", where yyy represents
    309      1.1  cgd 	 * the name of the person being su'd to, and the instance is null
    310      1.1  cgd 	 *
    311      1.1  cgd 	 * We should have a way to set the ticket lifetime,
    312      1.1  cgd 	 * with a system default for root.
    313      1.1  cgd 	 */
    314      1.1  cgd 	kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
    315      1.1  cgd 		(uid == 0 ? "root" : ""), lrealm,
    316      1.1  cgd 	    	"krbtgt", lrealm, DEFAULT_TKT_LIFE, 0);
    317      1.1  cgd 
    318      1.1  cgd 	if (kerno != KSUCCESS) {
    319      1.1  cgd 		if (kerno == KDC_PR_UNKNOWN) {
    320  1.1.1.2  jtc 			warnx("kerberos: principal unknown: %s.%s@%s",
    321      1.1  cgd 				(uid == 0 ? username : user),
    322      1.1  cgd 				(uid == 0 ? "root" : ""), lrealm);
    323      1.1  cgd 			return (1);
    324      1.1  cgd 		}
    325  1.1.1.2  jtc 		warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
    326      1.1  cgd 		syslog(LOG_NOTICE|LOG_AUTH,
    327      1.1  cgd 		    "BAD Kerberos SU: %s to %s%s: %s",
    328      1.1  cgd 		    username, user, ontty(), krb_err_txt[kerno]);
    329      1.1  cgd 		return (1);
    330      1.1  cgd 	}
    331      1.1  cgd 
    332      1.1  cgd 	if (chown(krbtkfile, uid, -1) < 0) {
    333  1.1.1.2  jtc 		warn("chown");
    334      1.1  cgd 		(void)unlink(krbtkfile);
    335      1.1  cgd 		return (1);
    336      1.1  cgd 	}
    337      1.1  cgd 
    338      1.1  cgd 	(void)setpriority(PRIO_PROCESS, 0, -2);
    339      1.1  cgd 
    340      1.1  cgd 	if (gethostname(hostname, sizeof(hostname)) == -1) {
    341  1.1.1.2  jtc 		warn("gethostname");
    342      1.1  cgd 		dest_tkt();
    343      1.1  cgd 		return (1);
    344      1.1  cgd 	}
    345      1.1  cgd 
    346      1.1  cgd 	(void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
    347      1.1  cgd 	savehost[sizeof(savehost) - 1] = '\0';
    348      1.1  cgd 
    349      1.1  cgd 	kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
    350      1.1  cgd 
    351      1.1  cgd 	if (kerno == KDC_PR_UNKNOWN) {
    352  1.1.1.2  jtc 		warnx("Warning: TGT not verified.");
    353      1.1  cgd 		syslog(LOG_NOTICE|LOG_AUTH,
    354      1.1  cgd 		    "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
    355      1.1  cgd 		    username, user, ontty(), krb_err_txt[kerno],
    356      1.1  cgd 		    "rcmd", savehost);
    357      1.1  cgd 	} else if (kerno != KSUCCESS) {
    358  1.1.1.2  jtc 		warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
    359      1.1  cgd 		syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
    360      1.1  cgd 		    username, user, ontty(), krb_err_txt[kerno]);
    361      1.1  cgd 		dest_tkt();
    362      1.1  cgd 		return (1);
    363      1.1  cgd 	} else {
    364      1.1  cgd 		if (!(hp = gethostbyname(hostname))) {
    365  1.1.1.2  jtc 			warnx("can't get addr of %s", hostname);
    366      1.1  cgd 			dest_tkt();
    367      1.1  cgd 			return (1);
    368      1.1  cgd 		}
    369  1.1.1.2  jtc 		memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr));
    370      1.1  cgd 
    371      1.1  cgd 		if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
    372      1.1  cgd 		    &authdata, "")) != KSUCCESS) {
    373  1.1.1.2  jtc 			warnx("kerberos: unable to verify rcmd ticket: %s\n",
    374      1.1  cgd 			    krb_err_txt[kerno]);
    375      1.1  cgd 			syslog(LOG_NOTICE|LOG_AUTH,
    376      1.1  cgd 			    "failed su: %s to %s%s: %s", username,
    377      1.1  cgd 			     user, ontty(), krb_err_txt[kerno]);
    378      1.1  cgd 			dest_tkt();
    379      1.1  cgd 			return (1);
    380      1.1  cgd 		}
    381      1.1  cgd 	}
    382      1.1  cgd 	return (0);
    383      1.1  cgd }
    384      1.1  cgd 
    385      1.1  cgd koktologin(name, realm, toname)
    386      1.1  cgd 	char *name, *realm, *toname;
    387      1.1  cgd {
    388  1.1.1.2  jtc 	AUTH_DAT *kdata;
    389      1.1  cgd 	AUTH_DAT kdata_st;
    390      1.1  cgd 
    391      1.1  cgd 	kdata = &kdata_st;
    392  1.1.1.2  jtc 	memset((char *)kdata, 0, sizeof(*kdata));
    393      1.1  cgd 	(void)strcpy(kdata->pname, name);
    394      1.1  cgd 	(void)strcpy(kdata->pinst,
    395      1.1  cgd 	    ((strcmp(toname, "root") == 0) ? "root" : ""));
    396      1.1  cgd 	(void)strcpy(kdata->prealm, realm);
    397      1.1  cgd 	return (kuserok(kdata, toname));
    398      1.1  cgd }
    399      1.1  cgd #endif
    400