1294336Sdes/* $OpenBSD: uidswap.c,v 1.39 2015/06/24 01:49:19 dtucker Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 657429Smarkm * Code for uid-swapping. 765668Skris * 865668Skris * As far as I am concerned, the code I have written for this software 965668Skris * can be used freely for any purpose. Any derived versions of this 1065668Skris * software must be clearly marked as such, and if the derived work is 1165668Skris * incompatible with the protocol description in the RFC file, it must be 1265668Skris * called by a name other than "ssh" or "Secure Shell". 1357429Smarkm */ 1457429Smarkm 1557429Smarkm#include "includes.h" 1657429Smarkm 17162852Sdes#include <errno.h> 18162852Sdes#include <pwd.h> 19162852Sdes#include <string.h> 20162852Sdes#include <unistd.h> 21294332Sdes#include <limits.h> 22162852Sdes#include <stdarg.h> 23261320Sdes#include <stdlib.h> 24162852Sdes 25162852Sdes#include <grp.h> 26162852Sdes 2776259Sgreen#include "log.h" 2857429Smarkm#include "uidswap.h" 29126274Sdes#include "xmalloc.h" 3057429Smarkm 3157429Smarkm/* 3257429Smarkm * Note: all these functions must work in all of the following cases: 3357429Smarkm * 1. euid=0, ruid=0 3457429Smarkm * 2. euid=0, ruid!=0 3557429Smarkm * 3. euid!=0, ruid!=0 3657429Smarkm * Additionally, they must work regardless of whether the system has 3757429Smarkm * POSIX saved uids or not. 3857429Smarkm */ 3957429Smarkm 4098937Sdes#if defined(_POSIX_SAVED_IDS) && !defined(BROKEN_SAVED_UIDS) 4157429Smarkm/* Lets assume that posix saved ids also work with seteuid, even though that 4257429Smarkm is not part of the posix specification. */ 4398937Sdes#define SAVED_IDS_WORK_WITH_SETEUID 4498937Sdes/* Saved effective uid. */ 4598937Sdesstatic uid_t saved_euid = 0; 4698937Sdesstatic gid_t saved_egid = 0; 4798937Sdes#endif 4857429Smarkm 4957429Smarkm/* Saved effective uid. */ 5076259Sgreenstatic int privileged = 0; 5176259Sgreenstatic int temporarily_use_uid_effective = 0; 52126274Sdesstatic gid_t *saved_egroups = NULL, *user_groups = NULL; 5376259Sgreenstatic int saved_egroupslen = -1, user_groupslen = -1; 5457429Smarkm 5557429Smarkm/* 5657429Smarkm * Temporarily changes to the given uid. If the effective user 5757429Smarkm * id is not root, this does nothing. This call cannot be nested. 5857429Smarkm */ 5960573Skrisvoid 6076259Sgreentemporarily_use_uid(struct passwd *pw) 6157429Smarkm{ 6276259Sgreen /* Save the current euid, and egroups. */ 6398937Sdes#ifdef SAVED_IDS_WORK_WITH_SETEUID 6457429Smarkm saved_euid = geteuid(); 6598937Sdes saved_egid = getegid(); 66106121Sdes debug("temporarily_use_uid: %u/%u (e=%u/%u)", 67106121Sdes (u_int)pw->pw_uid, (u_int)pw->pw_gid, 68106121Sdes (u_int)saved_euid, (u_int)saved_egid); 69146998Sdes#ifndef HAVE_CYGWIN 7076259Sgreen if (saved_euid != 0) { 7176259Sgreen privileged = 0; 7276259Sgreen return; 7376259Sgreen } 74146998Sdes#endif 7598937Sdes#else 7698937Sdes if (geteuid() != 0) { 7798937Sdes privileged = 0; 7898937Sdes return; 7998937Sdes } 8098937Sdes#endif /* SAVED_IDS_WORK_WITH_SETEUID */ 8198937Sdes 8276259Sgreen privileged = 1; 8376259Sgreen temporarily_use_uid_effective = 1; 84126274Sdes 85126274Sdes saved_egroupslen = getgroups(0, NULL); 8676259Sgreen if (saved_egroupslen < 0) 8776259Sgreen fatal("getgroups: %.100s", strerror(errno)); 88126274Sdes if (saved_egroupslen > 0) { 89294336Sdes saved_egroups = xreallocarray(saved_egroups, 90162852Sdes saved_egroupslen, sizeof(gid_t)); 91126274Sdes if (getgroups(saved_egroupslen, saved_egroups) < 0) 92126274Sdes fatal("getgroups: %.100s", strerror(errno)); 93126274Sdes } else { /* saved_egroupslen == 0 */ 94255767Sdes free(saved_egroups); 95126274Sdes } 9657429Smarkm 9776259Sgreen /* set and save the user's groups */ 9876259Sgreen if (user_groupslen == -1) { 9976259Sgreen if (initgroups(pw->pw_name, pw->pw_gid) < 0) 10076259Sgreen fatal("initgroups: %s: %.100s", pw->pw_name, 10176259Sgreen strerror(errno)); 102126274Sdes 103126274Sdes user_groupslen = getgroups(0, NULL); 10476259Sgreen if (user_groupslen < 0) 10576259Sgreen fatal("getgroups: %.100s", strerror(errno)); 106126274Sdes if (user_groupslen > 0) { 107294336Sdes user_groups = xreallocarray(user_groups, 108162852Sdes user_groupslen, sizeof(gid_t)); 109126274Sdes if (getgroups(user_groupslen, user_groups) < 0) 110126274Sdes fatal("getgroups: %.100s", strerror(errno)); 111126274Sdes } else { /* user_groupslen == 0 */ 112255767Sdes free(user_groups); 113126274Sdes } 11476259Sgreen } 11557429Smarkm /* Set the effective uid to the given (unprivileged) uid. */ 11676259Sgreen if (setgroups(user_groupslen, user_groups) < 0) 11776259Sgreen fatal("setgroups: %.100s", strerror(errno)); 11898937Sdes#ifndef SAVED_IDS_WORK_WITH_SETEUID 11998937Sdes /* Propagate the privileged gid to all of our gids. */ 12098937Sdes if (setgid(getegid()) < 0) 12198937Sdes debug("setgid %u: %.100s", (u_int) getegid(), strerror(errno)); 12298937Sdes /* Propagate the privileged uid to all of our uids. */ 12398937Sdes if (setuid(geteuid()) < 0) 12498937Sdes debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno)); 12598937Sdes#endif /* SAVED_IDS_WORK_WITH_SETEUID */ 12676259Sgreen if (setegid(pw->pw_gid) < 0) 12798675Sdes fatal("setegid %u: %.100s", (u_int)pw->pw_gid, 12876259Sgreen strerror(errno)); 12976259Sgreen if (seteuid(pw->pw_uid) == -1) 13098675Sdes fatal("seteuid %u: %.100s", (u_int)pw->pw_uid, 13176259Sgreen strerror(errno)); 13257429Smarkm} 13357429Smarkm 134162852Sdesvoid 135162852Sdespermanently_drop_suid(uid_t uid) 136162852Sdes{ 137296633Sdes#ifndef NO_UID_RESTORATION_TEST 138162852Sdes uid_t old_uid = getuid(); 139261320Sdes#endif 140162852Sdes 141162852Sdes debug("permanently_drop_suid: %u", (u_int)uid); 142162852Sdes if (setresuid(uid, uid, uid) < 0) 143162852Sdes fatal("setresuid %u: %.100s", (u_int)uid, strerror(errno)); 144162852Sdes 145296633Sdes#ifndef NO_UID_RESTORATION_TEST 146296633Sdes /* 147296633Sdes * Try restoration of UID if changed (test clearing of saved uid). 148296633Sdes * 149296633Sdes * Note that we don't do this on Cygwin, or on Solaris-based platforms 150296633Sdes * where fine-grained privileges are available (the user might be 151296633Sdes * deliberately allowed the right to setuid back to root). 152296633Sdes */ 153162852Sdes if (old_uid != uid && 154162852Sdes (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) 155162852Sdes fatal("%s: was able to restore old [e]uid", __func__); 156162852Sdes#endif 157162852Sdes 158162852Sdes /* Verify UID drop was successful */ 159162852Sdes if (getuid() != uid || geteuid() != uid) { 160162852Sdes fatal("%s: euid incorrect uid:%u euid:%u (should be %u)", 161162852Sdes __func__, (u_int)getuid(), (u_int)geteuid(), (u_int)uid); 162162852Sdes } 163162852Sdes} 164162852Sdes 16557429Smarkm/* 16676259Sgreen * Restores to the original (privileged) uid. 16757429Smarkm */ 16860573Skrisvoid 16976259Sgreenrestore_uid(void) 17057429Smarkm{ 17176259Sgreen /* it's a no-op unless privileged */ 172106121Sdes if (!privileged) { 173106121Sdes debug("restore_uid: (unprivileged)"); 17476259Sgreen return; 175106121Sdes } 17676259Sgreen if (!temporarily_use_uid_effective) 17776259Sgreen fatal("restore_uid: temporarily_use_uid not effective"); 17898937Sdes 17998937Sdes#ifdef SAVED_IDS_WORK_WITH_SETEUID 180106121Sdes debug("restore_uid: %u/%u", (u_int)saved_euid, (u_int)saved_egid); 18176259Sgreen /* Set the effective uid back to the saved privileged uid. */ 18257429Smarkm if (seteuid(saved_euid) < 0) 18398675Sdes fatal("seteuid %u: %.100s", (u_int)saved_euid, strerror(errno)); 18498937Sdes if (setegid(saved_egid) < 0) 18598937Sdes fatal("setegid %u: %.100s", (u_int)saved_egid, strerror(errno)); 18698937Sdes#else /* SAVED_IDS_WORK_WITH_SETEUID */ 18798937Sdes /* 18898937Sdes * We are unable to restore the real uid to its unprivileged value. 18998937Sdes * Propagate the real uid (usually more privileged) to effective uid 19098937Sdes * as well. 19198937Sdes */ 19298937Sdes setuid(getuid()); 19398937Sdes setgid(getgid()); 19498937Sdes#endif /* SAVED_IDS_WORK_WITH_SETEUID */ 19598937Sdes 19676259Sgreen if (setgroups(saved_egroupslen, saved_egroups) < 0) 19776259Sgreen fatal("setgroups: %.100s", strerror(errno)); 19876259Sgreen temporarily_use_uid_effective = 0; 19957429Smarkm} 20057429Smarkm 20157429Smarkm/* 20257429Smarkm * Permanently sets all uids to the given uid. This cannot be 20357429Smarkm * called while temporarily_use_uid is effective. 20457429Smarkm */ 20560573Skrisvoid 20676259Sgreenpermanently_set_uid(struct passwd *pw) 20757429Smarkm{ 208296633Sdes#ifndef NO_UID_RESTORATION_TEST 209124208Sdes uid_t old_uid = getuid(); 210124208Sdes gid_t old_gid = getgid(); 211261320Sdes#endif 212124208Sdes 213162852Sdes if (pw == NULL) 214162852Sdes fatal("permanently_set_uid: no user given"); 21576259Sgreen if (temporarily_use_uid_effective) 21698675Sdes fatal("permanently_set_uid: temporarily_use_uid effective"); 217106121Sdes debug("permanently_set_uid: %u/%u", (u_int)pw->pw_uid, 218106121Sdes (u_int)pw->pw_gid); 219124208Sdes 220124208Sdes if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0) 221124208Sdes fatal("setresgid %u: %.100s", (u_int)pw->pw_gid, strerror(errno)); 222124208Sdes 223192595Sdes#ifdef __APPLE__ 224192595Sdes /* 225192595Sdes * OS X requires initgroups after setgid to opt back into 226192595Sdes * memberd support for >16 supplemental groups. 227192595Sdes */ 228192595Sdes if (initgroups(pw->pw_name, pw->pw_gid) < 0) 229192595Sdes fatal("initgroups %.100s %u: %.100s", 230192595Sdes pw->pw_name, (u_int)pw->pw_gid, strerror(errno)); 231192595Sdes#endif 232192595Sdes 233124208Sdes if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) 234124208Sdes fatal("setresuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno)); 235124208Sdes 236296633Sdes#ifndef NO_UID_RESTORATION_TEST 237124208Sdes /* Try restoration of GID if changed (test clearing of saved gid) */ 238146998Sdes if (old_gid != pw->pw_gid && pw->pw_uid != 0 && 239124208Sdes (setgid(old_gid) != -1 || setegid(old_gid) != -1)) 240124208Sdes fatal("%s: was able to restore old [e]gid", __func__); 241146998Sdes#endif 242124208Sdes 243124208Sdes /* Verify GID drop was successful */ 244124208Sdes if (getgid() != pw->pw_gid || getegid() != pw->pw_gid) { 245126274Sdes fatal("%s: egid incorrect gid:%u egid:%u (should be %u)", 246126274Sdes __func__, (u_int)getgid(), (u_int)getegid(), 247124208Sdes (u_int)pw->pw_gid); 248124208Sdes } 249124208Sdes 250296633Sdes#ifndef NO_UID_RESTORATION_TEST 251124208Sdes /* Try restoration of UID if changed (test clearing of saved uid) */ 252126274Sdes if (old_uid != pw->pw_uid && 253124208Sdes (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) 254124208Sdes fatal("%s: was able to restore old [e]uid", __func__); 255124208Sdes#endif 256124208Sdes 257124208Sdes /* Verify UID drop was successful */ 258124208Sdes if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) { 259126274Sdes fatal("%s: euid incorrect uid:%u euid:%u (should be %u)", 260126274Sdes __func__, (u_int)getuid(), (u_int)geteuid(), 261124208Sdes (u_int)pw->pw_uid); 262124208Sdes } 26357429Smarkm} 264