xref: /onnv-gate/usr/src/cmd/ssh/libssh/common/uidswap.c (revision 11134:8aa0c4ca6639)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * Author: Tatu Ylonen <ylo@cs.hut.fi>
30Sstevel@tonic-gate  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
40Sstevel@tonic-gate  *                    All rights reserved
50Sstevel@tonic-gate  * Code for uid-swapping.
60Sstevel@tonic-gate  *
70Sstevel@tonic-gate  * As far as I am concerned, the code I have written for this software
80Sstevel@tonic-gate  * can be used freely for any purpose.  Any derived versions of this
90Sstevel@tonic-gate  * software must be clearly marked as such, and if the derived work is
100Sstevel@tonic-gate  * incompatible with the protocol description in the RFC file, it must be
110Sstevel@tonic-gate  * called by a name other than "ssh" or "Secure Shell".
120Sstevel@tonic-gate  */
135600Sjp161948 /*
149139SJan.Pechanec@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
155600Sjp161948  * Use is subject to license terms.
165600Sjp161948  */
170Sstevel@tonic-gate 
180Sstevel@tonic-gate #include "includes.h"
190Sstevel@tonic-gate RCSID("$OpenBSD: uidswap.c,v 1.23 2002/07/15 17:15:31 stevesk Exp $");
200Sstevel@tonic-gate 
219139SJan.Pechanec@Sun.COM #include <priv.h>
220Sstevel@tonic-gate 
230Sstevel@tonic-gate #include "log.h"
240Sstevel@tonic-gate #include "uidswap.h"
2511044SHuie-Ying.Lee@Sun.COM #include "buffer.h"
269139SJan.Pechanec@Sun.COM #include "servconf.h"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Note: all these functions must work in all of the following cases:
300Sstevel@tonic-gate  *    1. euid=0, ruid=0
310Sstevel@tonic-gate  *    2. euid=0, ruid!=0
320Sstevel@tonic-gate  *    3. euid!=0, ruid!=0
330Sstevel@tonic-gate  * Additionally, they must work regardless of whether the system has
340Sstevel@tonic-gate  * POSIX saved uids or not.
350Sstevel@tonic-gate  */
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #if defined(_POSIX_SAVED_IDS) && !defined(BROKEN_SAVED_UIDS)
380Sstevel@tonic-gate /* Lets assume that posix saved ids also work with seteuid, even though that
390Sstevel@tonic-gate    is not part of the posix specification. */
400Sstevel@tonic-gate #define SAVED_IDS_WORK
410Sstevel@tonic-gate /* Saved effective uid. */
420Sstevel@tonic-gate static uid_t 	saved_euid = 0;
430Sstevel@tonic-gate static gid_t	saved_egid = 0;
440Sstevel@tonic-gate #endif
450Sstevel@tonic-gate 
460Sstevel@tonic-gate /* Saved effective uid. */
470Sstevel@tonic-gate static int	privileged = 0;
480Sstevel@tonic-gate static int	temporarily_use_uid_effective = 0;
49*11134SCasper.Dik@Sun.COM static int	ngroups_max = -1;
50*11134SCasper.Dik@Sun.COM static gid_t	*saved_egroups, *user_groups;
510Sstevel@tonic-gate static int	saved_egroupslen = -1, user_groupslen = -1;
520Sstevel@tonic-gate 
530Sstevel@tonic-gate /*
540Sstevel@tonic-gate  * Temporarily changes to the given uid.  If the effective user
550Sstevel@tonic-gate  * id is not root, this does nothing.  This call cannot be nested.
560Sstevel@tonic-gate  */
570Sstevel@tonic-gate void
temporarily_use_uid(struct passwd * pw)580Sstevel@tonic-gate temporarily_use_uid(struct passwd *pw)
590Sstevel@tonic-gate {
600Sstevel@tonic-gate 	/* Save the current euid, and egroups. */
610Sstevel@tonic-gate #ifdef SAVED_IDS_WORK
620Sstevel@tonic-gate 	saved_euid = geteuid();
630Sstevel@tonic-gate 	saved_egid = getegid();
640Sstevel@tonic-gate 	debug("temporarily_use_uid: %u/%u (e=%u/%u)",
650Sstevel@tonic-gate 	    (u_int)pw->pw_uid, (u_int)pw->pw_gid,
660Sstevel@tonic-gate 	    (u_int)saved_euid, (u_int)saved_egid);
670Sstevel@tonic-gate 	if (saved_euid != 0) {
680Sstevel@tonic-gate 		privileged = 0;
690Sstevel@tonic-gate 		return;
700Sstevel@tonic-gate 	}
710Sstevel@tonic-gate #else
720Sstevel@tonic-gate 	if (geteuid() != 0) {
730Sstevel@tonic-gate 		privileged = 0;
740Sstevel@tonic-gate 		return;
750Sstevel@tonic-gate 	}
760Sstevel@tonic-gate #endif /* SAVED_IDS_WORK */
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 	privileged = 1;
790Sstevel@tonic-gate 	temporarily_use_uid_effective = 1;
80*11134SCasper.Dik@Sun.COM 
81*11134SCasper.Dik@Sun.COM 	if (ngroups_max < 0) {
82*11134SCasper.Dik@Sun.COM 		ngroups_max = sysconf(_SC_NGROUPS_MAX);
83*11134SCasper.Dik@Sun.COM 		saved_egroups = malloc(ngroups_max * sizeof (gid_t));
84*11134SCasper.Dik@Sun.COM 		user_groups = malloc(ngroups_max * sizeof (gid_t));
85*11134SCasper.Dik@Sun.COM 		if (saved_egroups == NULL || user_groups == NULL)
86*11134SCasper.Dik@Sun.COM 			fatal("malloc(gid array): %.100s", strerror(errno));
87*11134SCasper.Dik@Sun.COM 	}
88*11134SCasper.Dik@Sun.COM 
89*11134SCasper.Dik@Sun.COM 	saved_egroupslen = getgroups(ngroups_max, saved_egroups);
900Sstevel@tonic-gate 	if (saved_egroupslen < 0)
910Sstevel@tonic-gate 		fatal("getgroups: %.100s", strerror(errno));
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 	/* set and save the user's groups */
940Sstevel@tonic-gate 	if (user_groupslen == -1) {
950Sstevel@tonic-gate 		if (initgroups(pw->pw_name, pw->pw_gid) < 0)
960Sstevel@tonic-gate 			fatal("initgroups: %s: %.100s", pw->pw_name,
970Sstevel@tonic-gate 			    strerror(errno));
98*11134SCasper.Dik@Sun.COM 		user_groupslen = getgroups(ngroups_max, user_groups);
990Sstevel@tonic-gate 		if (user_groupslen < 0)
1000Sstevel@tonic-gate 			fatal("getgroups: %.100s", strerror(errno));
1010Sstevel@tonic-gate 	}
1020Sstevel@tonic-gate 	/* Set the effective uid to the given (unprivileged) uid. */
1030Sstevel@tonic-gate 	if (setgroups(user_groupslen, user_groups) < 0)
1040Sstevel@tonic-gate 		fatal("setgroups: %.100s", strerror(errno));
1050Sstevel@tonic-gate #ifdef SAVED_IDS_WORK
1060Sstevel@tonic-gate 	/* Set saved gid and set real gid */
1070Sstevel@tonic-gate 	if (setregid(pw->pw_gid, -1) == -1)
1080Sstevel@tonic-gate 		debug("setregid(%u, -1): %.100s", (uint_t)pw->pw_gid, strerror(errno));
1090Sstevel@tonic-gate 	/* Set saved uid and set real uid */
1100Sstevel@tonic-gate 	if (setreuid(pw->pw_uid, -1) == -1)
1110Sstevel@tonic-gate 		debug("setreuid(%u, -1): %.100s", (uint_t)pw->pw_uid, strerror(errno));
1120Sstevel@tonic-gate #else
1130Sstevel@tonic-gate 	/* Propagate the privileged gid to all of our gids. */
1140Sstevel@tonic-gate 	if (setgid(getegid()) < 0)
1150Sstevel@tonic-gate 		debug("setgid %u: %.100s", (u_int) getegid(), strerror(errno));
1160Sstevel@tonic-gate 	/* Propagate the privileged uid to all of our uids. */
1170Sstevel@tonic-gate 	if (setuid(geteuid()) < 0)
1180Sstevel@tonic-gate 		debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno));
1190Sstevel@tonic-gate #endif /* SAVED_IDS_WORK */
1200Sstevel@tonic-gate 	/* Set effective gid */
1210Sstevel@tonic-gate 	if (setegid(pw->pw_gid) == -1)
1220Sstevel@tonic-gate 		fatal("setegid %u: %.100s", (u_int)pw->pw_uid,
1230Sstevel@tonic-gate 		    strerror(errno));
1240Sstevel@tonic-gate 	/* Set effective uid */
1250Sstevel@tonic-gate 	if (seteuid(pw->pw_uid) == -1)
1260Sstevel@tonic-gate 		fatal("seteuid %u: %.100s", (u_int)pw->pw_uid,
1270Sstevel@tonic-gate 		    strerror(errno));
1280Sstevel@tonic-gate 	/*
1290Sstevel@tonic-gate 	 * If saved set ids work then
1300Sstevel@tonic-gate 	 *
1310Sstevel@tonic-gate 	 *	ruid == euid == pw->pw_uid
1320Sstevel@tonic-gate 	 *	saved uid = previous euid
1330Sstevel@tonic-gate 	 *	rgid == egid == pw->pw_gid
1340Sstevel@tonic-gate 	 *	saved gid = previous egid
1350Sstevel@tonic-gate 	 */
1360Sstevel@tonic-gate }
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate /*
1390Sstevel@tonic-gate  * Restores to the original (privileged) uid.
1400Sstevel@tonic-gate  */
1410Sstevel@tonic-gate void
restore_uid(void)1420Sstevel@tonic-gate restore_uid(void)
1430Sstevel@tonic-gate {
1440Sstevel@tonic-gate 	/* it's a no-op unless privileged */
1450Sstevel@tonic-gate 	if (!privileged) {
1460Sstevel@tonic-gate 		debug("restore_uid: (unprivileged)");
1470Sstevel@tonic-gate 		return;
1480Sstevel@tonic-gate 	}
1490Sstevel@tonic-gate 	if (!temporarily_use_uid_effective)
1500Sstevel@tonic-gate 		fatal("restore_uid: temporarily_use_uid not effective");
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate #ifdef SAVED_IDS_WORK
1530Sstevel@tonic-gate 	debug("restore_uid: %u/%u", (u_int)saved_euid, (u_int)saved_egid);
1540Sstevel@tonic-gate 	/* Set the effective uid back to the saved privileged uid. */
1550Sstevel@tonic-gate 	if (seteuid(saved_euid) < 0)
1560Sstevel@tonic-gate 		fatal("seteuid %u: %.100s", (u_int)saved_euid, strerror(errno));
1570Sstevel@tonic-gate 	if (setuid(saved_euid) < 0)
1580Sstevel@tonic-gate 		fatal("setuid %u: %.100s", (u_int)saved_euid, strerror(errno));
1590Sstevel@tonic-gate 	if (setegid(saved_egid) < 0)
1600Sstevel@tonic-gate 		fatal("setegid %u: %.100s", (u_int)saved_egid, strerror(errno));
1610Sstevel@tonic-gate 	if (setgid(saved_egid) < 0)
1620Sstevel@tonic-gate 		fatal("setgid %u: %.100s", (u_int)saved_egid, strerror(errno));
1630Sstevel@tonic-gate #else /* SAVED_IDS_WORK */
1640Sstevel@tonic-gate 	/*
1650Sstevel@tonic-gate 	 * We are unable to restore the real uid to its unprivileged value.
1660Sstevel@tonic-gate 	 * Propagate the real uid (usually more privileged) to effective uid
1670Sstevel@tonic-gate 	 * as well.
1680Sstevel@tonic-gate 	 */
1690Sstevel@tonic-gate 	setuid(getuid());
1700Sstevel@tonic-gate 	setgid(getgid());
1710Sstevel@tonic-gate #endif /* SAVED_IDS_WORK */
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	if (setgroups(saved_egroupslen, saved_egroups) < 0)
1740Sstevel@tonic-gate 		fatal("setgroups: %.100s", strerror(errno));
1750Sstevel@tonic-gate 	temporarily_use_uid_effective = 0;
1760Sstevel@tonic-gate }
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate /*
1799139SJan.Pechanec@Sun.COM  * Permanently sets all uids to the given uid. This cannot be called while
1809139SJan.Pechanec@Sun.COM  * temporarily_use_uid is effective. Note that when the ChrootDirectory option
1819139SJan.Pechanec@Sun.COM  * is in use we keep a few privileges so that we can call chroot(2) later while
1829139SJan.Pechanec@Sun.COM  * already running under UIDs of a connecting user.
1830Sstevel@tonic-gate  */
1840Sstevel@tonic-gate void
permanently_set_uid(struct passwd * pw,char * chroot_directory)1859139SJan.Pechanec@Sun.COM permanently_set_uid(struct passwd *pw, char *chroot_directory)
1860Sstevel@tonic-gate {
1879139SJan.Pechanec@Sun.COM 	priv_set_t *pset;
1889139SJan.Pechanec@Sun.COM 
1890Sstevel@tonic-gate 	if (temporarily_use_uid_effective)
1909139SJan.Pechanec@Sun.COM 		fatal("%s: temporarily_use_uid effective", __func__);
1919139SJan.Pechanec@Sun.COM 
1929139SJan.Pechanec@Sun.COM 	debug("%s: %u/%u", __func__, (u_int)pw->pw_uid, (u_int)pw->pw_gid);
1939139SJan.Pechanec@Sun.COM 
1940Sstevel@tonic-gate 	if (initgroups(pw->pw_name, pw->pw_gid) < 0)
1950Sstevel@tonic-gate 		fatal("initgroups: %s: %.100s", pw->pw_name,
1960Sstevel@tonic-gate 		    strerror(errno));
1979139SJan.Pechanec@Sun.COM 
1980Sstevel@tonic-gate 	if (setgid(pw->pw_gid) < 0)
1990Sstevel@tonic-gate 		fatal("setgid %u: %.100s", (u_int)pw->pw_gid, strerror(errno));
2009139SJan.Pechanec@Sun.COM 
2019139SJan.Pechanec@Sun.COM 	/*
2029139SJan.Pechanec@Sun.COM 	 * If root is connecting we are done now. Note that we must have called
2039139SJan.Pechanec@Sun.COM 	 * setgid() in case that the SSH server was run under a group other than
2049139SJan.Pechanec@Sun.COM 	 * root.
2059139SJan.Pechanec@Sun.COM 	 */
2069139SJan.Pechanec@Sun.COM 	if (pw->pw_uid == 0)
2079139SJan.Pechanec@Sun.COM 		return;
2089139SJan.Pechanec@Sun.COM 
2099139SJan.Pechanec@Sun.COM 	/*
2109139SJan.Pechanec@Sun.COM 	 * This means we will keep all privileges after the UID change.
2119139SJan.Pechanec@Sun.COM 	 */
2129139SJan.Pechanec@Sun.COM 	if (setpflags(PRIV_AWARE, 1) != 0)
2139139SJan.Pechanec@Sun.COM 		fatal("setpflags: %s", strerror(errno));
2149139SJan.Pechanec@Sun.COM 
2159139SJan.Pechanec@Sun.COM 	/* Now we are running under UID of the user. */
2160Sstevel@tonic-gate 	if (setuid(pw->pw_uid) < 0)
2170Sstevel@tonic-gate 		fatal("setuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno));
2189139SJan.Pechanec@Sun.COM 
2199139SJan.Pechanec@Sun.COM 	/*
2209139SJan.Pechanec@Sun.COM 	 * We will run with the privileges from the Inheritable set as
2219139SJan.Pechanec@Sun.COM 	 * we would have after exec(2) if we had stayed in NPA mode
2229139SJan.Pechanec@Sun.COM 	 * before setuid(2) call (see privileges(5), user_attr(4), and
2239139SJan.Pechanec@Sun.COM 	 * pam_unix_cred(5)). We want to run with P = E = I, with I as
2249139SJan.Pechanec@Sun.COM 	 * set by pam_unix_cred(5). We also add PRIV_PROC_CHROOT,
2259139SJan.Pechanec@Sun.COM 	 * obviously, and then PRIV_PROC_FORK and PRIV_PROC_EXEC, since
2269139SJan.Pechanec@Sun.COM 	 * those two might have been removed from the I set. Note that
2279139SJan.Pechanec@Sun.COM 	 * we are expected to finish the login process without them in
2289139SJan.Pechanec@Sun.COM 	 * the I set, the important thing is that those not be passed on
2299139SJan.Pechanec@Sun.COM 	 * to a shell or a subsystem later if they were not set in
2309139SJan.Pechanec@Sun.COM 	 * pam_unix_cred(5).
2319139SJan.Pechanec@Sun.COM 	 */
2329139SJan.Pechanec@Sun.COM 	if ((pset = priv_allocset()) == NULL)
2339139SJan.Pechanec@Sun.COM 		fatal("priv_allocset: %s", strerror(errno));
2349139SJan.Pechanec@Sun.COM 	if (getppriv(PRIV_INHERITABLE, pset) != 0)
2359139SJan.Pechanec@Sun.COM 		fatal("getppriv: %s", strerror(errno));
2369139SJan.Pechanec@Sun.COM 
2379139SJan.Pechanec@Sun.COM 	/* We do not need PRIV_PROC_CHROOT unless chroot()ing. */
2389139SJan.Pechanec@Sun.COM 	if (chroot_requested(chroot_directory) &&
2399139SJan.Pechanec@Sun.COM 	    priv_addset(pset, PRIV_PROC_CHROOT) == -1) {
2409139SJan.Pechanec@Sun.COM 		fatal("%s: priv_addset failed", __func__);
2419139SJan.Pechanec@Sun.COM 	}
2429139SJan.Pechanec@Sun.COM 
2439139SJan.Pechanec@Sun.COM 	if (priv_addset(pset, PRIV_PROC_FORK) == -1 ||
2449139SJan.Pechanec@Sun.COM 	    priv_addset(pset, PRIV_PROC_EXEC) == -1) {
2459139SJan.Pechanec@Sun.COM 		fatal("%s: priv_addset failed", __func__);
2469139SJan.Pechanec@Sun.COM 	}
2479139SJan.Pechanec@Sun.COM 
2489139SJan.Pechanec@Sun.COM 	/* Set only P; this will also set E. */
2499139SJan.Pechanec@Sun.COM 	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) == -1)
2509139SJan.Pechanec@Sun.COM 		fatal("setppriv: %s", strerror(errno));
2519139SJan.Pechanec@Sun.COM 
2529139SJan.Pechanec@Sun.COM 	/* We don't need the PA flag anymore. */
2539139SJan.Pechanec@Sun.COM 	if (setpflags(PRIV_AWARE, 0) == -1)
2549139SJan.Pechanec@Sun.COM 		fatal("setpflags: %s", strerror(errno));
2559139SJan.Pechanec@Sun.COM 
2569139SJan.Pechanec@Sun.COM 	priv_freeset(pset);
2570Sstevel@tonic-gate }
258