Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: uidswap.c,v 1.35 2006/08/03 03:34:42 deraadt Exp $ */
      2 /*
      3  * Author: Tatu Ylonen <ylo (at) cs.hut.fi>
      4  * Copyright (c) 1995 Tatu Ylonen <ylo (at) cs.hut.fi>, Espoo, Finland
      5  *                    All rights reserved
      6  * Code for uid-swapping.
      7  *
      8  * As far as I am concerned, the code I have written for this software
      9  * can be used freely for any purpose.  Any derived versions of this
     10  * software must be clearly marked as such, and if the derived work is
     11  * incompatible with the protocol description in the RFC file, it must be
     12  * called by a name other than "ssh" or "Secure Shell".
     13  */
     14 
     15 #include "includes.h"
     16 
     17 #include <sys/param.h>
     18 #include <errno.h>
     19 #include <pwd.h>
     20 #include <string.h>
     21 #include <unistd.h>
     22 #include <stdarg.h>
     23 
     24 #include <grp.h>
     25 
     26 #include "log.h"
     27 #include "uidswap.h"
     28 #include "xmalloc.h"
     29 
     30 #ifdef ANDROID
     31 #include <private/android_filesystem_config.h>
     32 #include <linux/capability.h>
     33 #include <linux/prctl.h>
     34 #endif
     35 
     36 /*
     37  * Note: all these functions must work in all of the following cases:
     38  *    1. euid=0, ruid=0
     39  *    2. euid=0, ruid!=0
     40  *    3. euid!=0, ruid!=0
     41  * Additionally, they must work regardless of whether the system has
     42  * POSIX saved uids or not.
     43  */
     44 
     45 #if defined(_POSIX_SAVED_IDS) && !defined(BROKEN_SAVED_UIDS)
     46 /* Lets assume that posix saved ids also work with seteuid, even though that
     47    is not part of the posix specification. */
     48 #define SAVED_IDS_WORK_WITH_SETEUID
     49 /* Saved effective uid. */
     50 static uid_t 	saved_euid = 0;
     51 static gid_t	saved_egid = 0;
     52 #endif
     53 
     54 /* Saved effective uid. */
     55 static int	privileged = 0;
     56 static int	temporarily_use_uid_effective = 0;
     57 static gid_t	*saved_egroups = NULL, *user_groups = NULL;
     58 static int	saved_egroupslen = -1, user_groupslen = -1;
     59 
     60 /*
     61  * Temporarily changes to the given uid.  If the effective user
     62  * id is not root, this does nothing.  This call cannot be nested.
     63  */
     64 void
     65 temporarily_use_uid(struct passwd *pw)
     66 {
     67 	/* Save the current euid, and egroups. */
     68 #ifdef SAVED_IDS_WORK_WITH_SETEUID
     69 	saved_euid = geteuid();
     70 	saved_egid = getegid();
     71 	debug("temporarily_use_uid: %u/%u (e=%u/%u)",
     72 	    (u_int)pw->pw_uid, (u_int)pw->pw_gid,
     73 	    (u_int)saved_euid, (u_int)saved_egid);
     74 #ifndef HAVE_CYGWIN
     75 	if (saved_euid != 0) {
     76 		privileged = 0;
     77 		return;
     78 	}
     79 #endif
     80 #else
     81 	if (geteuid() != 0) {
     82 		privileged = 0;
     83 		return;
     84 	}
     85 #endif /* SAVED_IDS_WORK_WITH_SETEUID */
     86 
     87 	privileged = 1;
     88 	temporarily_use_uid_effective = 1;
     89 
     90 	saved_egroupslen = getgroups(0, NULL);
     91 	if (saved_egroupslen < 0)
     92 		fatal("getgroups: %.100s", strerror(errno));
     93 	if (saved_egroupslen > 0) {
     94 		saved_egroups = xrealloc(saved_egroups,
     95 		    saved_egroupslen, sizeof(gid_t));
     96 		if (getgroups(saved_egroupslen, saved_egroups) < 0)
     97 			fatal("getgroups: %.100s", strerror(errno));
     98 	} else { /* saved_egroupslen == 0 */
     99 		if (saved_egroups != NULL)
    100 			xfree(saved_egroups);
    101 	}
    102 
    103 	/* set and save the user's groups */
    104 	if (user_groupslen == -1) {
    105 		if (initgroups(pw->pw_name, pw->pw_gid) < 0)
    106 			fatal("initgroups: %s: %.100s", pw->pw_name,
    107 			    strerror(errno));
    108 
    109 		user_groupslen = getgroups(0, NULL);
    110 		if (user_groupslen < 0)
    111 			fatal("getgroups: %.100s", strerror(errno));
    112 		if (user_groupslen > 0) {
    113 			user_groups = xrealloc(user_groups,
    114 			    user_groupslen, sizeof(gid_t));
    115 			if (getgroups(user_groupslen, user_groups) < 0)
    116 				fatal("getgroups: %.100s", strerror(errno));
    117 		} else { /* user_groupslen == 0 */
    118 			if (user_groups)
    119 				xfree(user_groups);
    120 		}
    121 	}
    122 	/* Set the effective uid to the given (unprivileged) uid. */
    123 	if (setgroups(user_groupslen, user_groups) < 0)
    124 		fatal("setgroups: %.100s", strerror(errno));
    125 #ifndef SAVED_IDS_WORK_WITH_SETEUID
    126 	/* Propagate the privileged gid to all of our gids. */
    127 	if (setgid(getegid()) < 0)
    128 		debug("setgid %u: %.100s", (u_int) getegid(), strerror(errno));
    129 	/* Propagate the privileged uid to all of our uids. */
    130 	if (setuid(geteuid()) < 0)
    131 		debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno));
    132 #endif /* SAVED_IDS_WORK_WITH_SETEUID */
    133 	if (setegid(pw->pw_gid) < 0)
    134 		fatal("setegid %u: %.100s", (u_int)pw->pw_gid,
    135 		    strerror(errno));
    136 	if (seteuid(pw->pw_uid) == -1)
    137 		fatal("seteuid %u: %.100s", (u_int)pw->pw_uid,
    138 		    strerror(errno));
    139 }
    140 
    141 void
    142 permanently_drop_suid(uid_t uid)
    143 {
    144 	uid_t old_uid = getuid();
    145 
    146 	debug("permanently_drop_suid: %u", (u_int)uid);
    147 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
    148 	if (setresuid(uid, uid, uid) < 0)
    149 		fatal("setresuid %u: %.100s", (u_int)uid, strerror(errno));
    150 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
    151 	if (setreuid(uid, uid) < 0)
    152 		fatal("setreuid %u: %.100s", (u_int)uid, strerror(errno));
    153 #else
    154 # ifndef SETEUID_BREAKS_SETUID
    155 	if (seteuid(uid) < 0)
    156 		fatal("seteuid %u: %.100s", (u_int)uid, strerror(errno));
    157 # endif
    158 	if (setuid(uid) < 0)
    159 		fatal("setuid %u: %.100s", (u_int)uid, strerror(errno));
    160 #endif
    161 
    162 #ifndef HAVE_CYGWIN
    163 	/* Try restoration of UID if changed (test clearing of saved uid) */
    164 	if (old_uid != uid &&
    165 	    (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
    166 		fatal("%s: was able to restore old [e]uid", __func__);
    167 #endif
    168 
    169 	/* Verify UID drop was successful */
    170 	if (getuid() != uid || geteuid() != uid) {
    171 		fatal("%s: euid incorrect uid:%u euid:%u (should be %u)",
    172 		    __func__, (u_int)getuid(), (u_int)geteuid(), (u_int)uid);
    173 	}
    174 }
    175 
    176 /*
    177  * Restores to the original (privileged) uid.
    178  */
    179 void
    180 restore_uid(void)
    181 {
    182 	/* it's a no-op unless privileged */
    183 	if (!privileged) {
    184 		debug("restore_uid: (unprivileged)");
    185 		return;
    186 	}
    187 	if (!temporarily_use_uid_effective)
    188 		fatal("restore_uid: temporarily_use_uid not effective");
    189 
    190 #ifdef SAVED_IDS_WORK_WITH_SETEUID
    191 	debug("restore_uid: %u/%u", (u_int)saved_euid, (u_int)saved_egid);
    192 	/* Set the effective uid back to the saved privileged uid. */
    193 	if (seteuid(saved_euid) < 0)
    194 		fatal("seteuid %u: %.100s", (u_int)saved_euid, strerror(errno));
    195 	if (setegid(saved_egid) < 0)
    196 		fatal("setegid %u: %.100s", (u_int)saved_egid, strerror(errno));
    197 #else /* SAVED_IDS_WORK_WITH_SETEUID */
    198 	/*
    199 	 * We are unable to restore the real uid to its unprivileged value.
    200 	 * Propagate the real uid (usually more privileged) to effective uid
    201 	 * as well.
    202 	 */
    203 	setuid(getuid());
    204 	setgid(getgid());
    205 #endif /* SAVED_IDS_WORK_WITH_SETEUID */
    206 
    207 	if (setgroups(saved_egroupslen, saved_egroups) < 0)
    208 		fatal("setgroups: %.100s", strerror(errno));
    209 	temporarily_use_uid_effective = 0;
    210 }
    211 
    212 /*
    213  * Permanently sets all uids to the given uid.  This cannot be
    214  * called while temporarily_use_uid is effective.
    215  */
    216 void
    217 permanently_set_uid(struct passwd *pw)
    218 {
    219 	uid_t old_uid = getuid();
    220 	gid_t old_gid = getgid();
    221 #ifdef ANDROID
    222 	struct __user_cap_header_struct header;
    223 	struct __user_cap_data_struct cap;
    224 #endif
    225 
    226 	if (pw == NULL)
    227 		fatal("permanently_set_uid: no user given");
    228 	if (temporarily_use_uid_effective)
    229 		fatal("permanently_set_uid: temporarily_use_uid effective");
    230 	debug("permanently_set_uid: %u/%u", (u_int)pw->pw_uid,
    231 	    (u_int)pw->pw_gid);
    232 
    233 #ifdef ANDROID
    234 	if (pw->pw_uid == AID_SHELL) {
    235 		prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
    236 
    237 		/* add extra groups needed for shell user:
    238 		** AID_LOG to read system logs (adb logcat)
    239 		** AID_INPUT to diagnose input issues (getevent)
    240 		** AID_INET to diagnose network issues (netcfg, ping)
    241 		** AID_GRAPHICS to access the frame buffer
    242 		** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
    243 		** AID_SDCARD_RW to allow writing to the SD card
    244 		** AID_MOUNT to allow unmounting the SD card before rebooting
    245 		** AID_NET_BW_STATS to read out qtaguid statistics
    246 		*/
    247 		gid_t groups[] = { AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS,
    248 						   AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_RW,
    249 						   AID_MOUNT, AID_NET_BW_STATS };
    250 		setgroups(sizeof(groups)/sizeof(groups[0]), groups);
    251 	}
    252 #endif
    253 
    254 #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
    255 	if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
    256 		fatal("setresgid %u: %.100s", (u_int)pw->pw_gid, strerror(errno));
    257 #elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
    258 	if (setregid(pw->pw_gid, pw->pw_gid) < 0)
    259 		fatal("setregid %u: %.100s", (u_int)pw->pw_gid, strerror(errno));
    260 #else
    261 	if (setegid(pw->pw_gid) < 0)
    262 		fatal("setegid %u: %.100s", (u_int)pw->pw_gid, strerror(errno));
    263 	if (setgid(pw->pw_gid) < 0)
    264 		fatal("setgid %u: %.100s", (u_int)pw->pw_gid, strerror(errno));
    265 #endif
    266 
    267 #ifdef __APPLE__
    268 	/*
    269 	 * OS X requires initgroups after setgid to opt back into
    270 	 * memberd support for >16 supplemental groups.
    271 	 */
    272 	if (initgroups(pw->pw_name, pw->pw_gid) < 0)
    273 		fatal("initgroups %.100s %u: %.100s",
    274 		    pw->pw_name, (u_int)pw->pw_gid, strerror(errno));
    275 #endif
    276 
    277 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
    278 	if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
    279 		fatal("setresuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno));
    280 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
    281 	if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
    282 		fatal("setreuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno));
    283 #else
    284 # ifndef SETEUID_BREAKS_SETUID
    285 	if (seteuid(pw->pw_uid) < 0)
    286 		fatal("seteuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno));
    287 # endif
    288 	if (setuid(pw->pw_uid) < 0)
    289 		fatal("setuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno));
    290 #endif
    291 
    292 #ifndef HAVE_CYGWIN
    293 	/* Try restoration of GID if changed (test clearing of saved gid) */
    294 	if (old_gid != pw->pw_gid && pw->pw_uid != 0 &&
    295 	    (setgid(old_gid) != -1 || setegid(old_gid) != -1))
    296 		fatal("%s: was able to restore old [e]gid", __func__);
    297 #endif
    298 
    299 	/* Verify GID drop was successful */
    300 	if (getgid() != pw->pw_gid || getegid() != pw->pw_gid) {
    301 		fatal("%s: egid incorrect gid:%u egid:%u (should be %u)",
    302 		    __func__, (u_int)getgid(), (u_int)getegid(),
    303 		    (u_int)pw->pw_gid);
    304 	}
    305 
    306 #ifndef HAVE_CYGWIN
    307 	/* Try restoration of UID if changed (test clearing of saved uid) */
    308 	if (old_uid != pw->pw_uid &&
    309 	    (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
    310 		fatal("%s: was able to restore old [e]uid", __func__);
    311 #endif
    312 
    313 	/* Verify UID drop was successful */
    314 	if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) {
    315 		fatal("%s: euid incorrect uid:%u euid:%u (should be %u)",
    316 		    __func__, (u_int)getuid(), (u_int)geteuid(),
    317 		    (u_int)pw->pw_uid);
    318 	}
    319 
    320 #ifdef ANDROID
    321 	if (pw->pw_uid == AID_SHELL) {
    322 		/* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */
    323 		header.version = _LINUX_CAPABILITY_VERSION;
    324 		header.pid = 0;
    325 		cap.effective = cap.permitted = (1 << CAP_SYS_BOOT);
    326 		cap.inheritable = 0;
    327 		capset(&header, &cap);
    328 	}
    329 #endif
    330 
    331 }
    332