From b09b8dcdda058d6f237baf50810f13e5b6aa47bb Mon Sep 17 00:00:00 2001
From: Damien Miller <djm@mindrot.org>
Date: Fri, 8 Jan 2016 14:24:56 +1100
Subject: [PATCH 02/36] Support Illumos/Solaris fine-grained privileges

Includes a pre-auth privsep sandbox and several pledge()
emulations. bz#2511, patch by Alex Wilson.

ok dtucker@
---
 Makefile.in                   |   5 +-
 configure.ac                  |  38 +++++++++++++-
 mux.c                         |   4 ++
 openbsd-compat/port-solaris.c | 114 ++++++++++++++++++++++++++++++++++++++++++
 openbsd-compat/port-solaris.h |   3 ++
 platform-pledge.c             |  71 ++++++++++++++++++++++++++
 platform.h                    |   5 ++
 sandbox-solaris.c             | 107 +++++++++++++++++++++++++++++++++++++++
 sftp-server.c                 |   3 ++
 ssh-agent.c                   |   2 +
 uidswap.c                     |  18 ++++---
 11 files changed, 360 insertions(+), 10 deletions(-)
 create mode 100644 platform-pledge.c
 create mode 100644 sandbox-solaris.c

diff --git a/Makefile.in b/Makefile.in
index 40cc7aa..8494713 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -91,7 +91,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
 	sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \
 	kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
 	kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \
-	kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o
+	kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \
+	platform-pledge.o
 
 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
 	sshconnect.o sshconnect1.o sshconnect2.o mux.o \
@@ -110,7 +111,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
 	sftp-server.o sftp-common.o \
 	roaming_common.o roaming_serv.o \
 	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
-	sandbox-seccomp-filter.o sandbox-capsicum.o
+	sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-solaris.o
 
 MANPAGES	= moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out
 MANPAGES_IN	= moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5
diff --git a/configure.ac b/configure.ac
index 9b05c30..b06cede 100644
--- a/configure.ac
+++ b/configure.ac
@@ -469,6 +469,11 @@ AC_CHECK_HEADERS([sys/un.h], [], [], [
 SIA_MSG="no"
 SPC_MSG="no"
 SP_MSG="no"
+SPP_MSG="no"
+
+# Support for Solaris/Illumos privileges (this test is used by both
+# the --with-solaris-privs option and --with-sandbox=solaris).
+SOLARIS_PRIVS="no"
 
 # Check for some target-specific stuff
 case "$host" in
@@ -575,6 +580,8 @@ case "$host" in
 	LIBS="$LIBS /usr/lib/textreadmode.o"
 	AC_DEFINE([HAVE_CYGWIN], [1], [Define if you are on Cygwin])
 	AC_DEFINE([USE_PIPES], [1], [Use PIPES instead of a socketpair()])
+	AC_DEFINE([NO_UID_RESTORATION_TEST], [1],
+		[Define to disable UID restoration test])
 	AC_DEFINE([DISABLE_SHADOW], [1],
 		[Define if you want to disable shadow passwords])
 	AC_DEFINE([NO_X11_UNIX_SOCKETS], [1],
@@ -889,13 +896,18 @@ mips-sony-bsd|mips-sony-newsos4)
 	else
 		AC_MSG_RESULT([no])
 	fi
+	AC_CHECK_FUNC([setppriv],
+		[ AC_CHECK_HEADERS([priv.h], [
+			SOLARIS_PRIVS="yes"
+		])
+	])
 	AC_ARG_WITH([solaris-contracts],
 		[  --with-solaris-contracts Enable Solaris process contracts (experimental)],
 		[
 		AC_CHECK_LIB([contract], [ct_tmpl_activate],
 			[ AC_DEFINE([USE_SOLARIS_PROCESS_CONTRACTS], [1],
 				[Define if you have Solaris process contracts])
-			  SSHDLIBS="$SSHDLIBS -lcontract"
+			  LIBS="$LIBS -lcontract"
 			  SPC_MSG="yes" ], )
 		],
 	)
@@ -905,10 +917,27 @@ mips-sony-bsd|mips-sony-newsos4)
 		AC_CHECK_LIB([project], [setproject],
 			[ AC_DEFINE([USE_SOLARIS_PROJECTS], [1],
 				[Define if you have Solaris projects])
-			SSHDLIBS="$SSHDLIBS -lproject"
+			LIBS="$LIBS -lproject"
 			SP_MSG="yes" ], )
 		],
 	)
+	AC_ARG_WITH([solaris-privs],
+		[  --with-solaris-privs    Enable Solaris/Illumos privileges (experimental)],
+		[
+		AC_MSG_CHECKING([for Solaris/Illumos privilege support])
+		if test "x$SOLARIS_PRIVS" = "xyes" ; then
+			AC_MSG_RESULT([found])
+			AC_DEFINE([NO_UID_RESTORATION_TEST], [1],
+				[Define to disable UID restoration test])
+			AC_DEFINE([USE_SOLARIS_PRIVS], [1],
+				[Define if you have Solaris privileges])
+			SPP_MSG="yes"
+		else
+			AC_MSG_RESULT([not found])
+			AC_MSG_ERROR([*** must have support for Solaris privileges to use --with-solaris-privs])
+		fi
+		],
+	)
 	TEST_SHELL=$SHELL	# let configure find us a capable shell
 	;;
 *-*-sunos4*)
@@ -3147,6 +3176,10 @@ elif test "x$sandbox_arg" = "xrlimit" || \
 		AC_MSG_ERROR([rlimit sandbox requires select to work with rlimit])
 	SANDBOX_STYLE="rlimit"
 	AC_DEFINE([SANDBOX_RLIMIT], [1], [Sandbox using setrlimit(2)])
+elif test "x$sandbox_arg" = "xsolaris" || \
+   ( test -z "$sandbox_arg" && test "x$SOLARIS_PRIVS" = "xyes" ) ; then
+	SANDBOX_STYLE="solaris"
+	AC_DEFINE([SANDBOX_SOLARIS], [1], [Sandbox using Solaris/Illumos privileges])
 elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \
      test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then
 	SANDBOX_STYLE="none"
@@ -4933,6 +4966,7 @@ echo "              MD5 password support: $MD5_MSG"
 echo "                   libedit support: $LIBEDIT_MSG"
 echo "  Solaris process contract support: $SPC_MSG"
 echo "           Solaris project support: $SP_MSG"
+echo "         Solaris privilege support: $SPP_MSG"
 echo "       IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG"
 echo "           Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG"
 echo "                  BSD Auth support: $BSD_AUTH_MSG"
diff --git a/mux.c b/mux.c
index e6136fd..8c2a53a 100644
--- a/mux.c
+++ b/mux.c
@@ -1889,6 +1889,8 @@ mux_client_request_session(int fd)
 	}
 	muxclient_request_id++;
 
+	platform_pledge_mux();
+
 	signal(SIGHUP, control_client_sighandler);
 	signal(SIGINT, control_client_sighandler);
 	signal(SIGTERM, control_client_sighandler);
@@ -1996,6 +1998,8 @@ mux_client_request_stdio_fwd(int fd)
 	    mm_send_fd(fd, STDOUT_FILENO) == -1)
 		fatal("%s: send fds failed", __func__);
 
+	platform_pledge_mux();
+
 	debug3("%s: stdio forward request sent", __func__);
 
 	/* Read their reply */
diff --git a/openbsd-compat/port-solaris.c b/openbsd-compat/port-solaris.c
index 25382f1..962cd16 100644
--- a/openbsd-compat/port-solaris.c
+++ b/openbsd-compat/port-solaris.c
@@ -227,3 +227,117 @@ solaris_set_default_project(struct passwd *pw)
 	}
 }
 #endif /* USE_SOLARIS_PROJECTS */
+
+#ifdef USE_SOLARIS_PRIVS
+# ifdef HAVE_PRIV_H
+#  include <priv.h>
+# endif
+
+void
+solaris_drop_privs_pinfo_net_fork_exec(void)
+{
+	priv_set_t *pset = NULL, *npset = NULL;
+
+	/*
+	 * Note: this variant avoids dropping DAC filesystem rights, in case
+	 * the process calling it is running as root and should have the
+	 * ability to read/write/chown any file on the system.
+	 *
+	 * We start with the basic set, then *add* the DAC rights to it while
+	 * taking away other parts of BASIC we don't need. Then we intersect
+	 * this with our existing PERMITTED set. In this way we keep any
+	 * DAC rights we had before, while otherwise reducing ourselves to
+	 * the minimum set of privileges we need to proceed.
+	 *
+	 * This also means we drop any other parts of "root" that we don't
+	 * need (e.g. the ability to kill any process, create new device nodes
+	 * etc etc).
+	 */
+
+	if ((pset = priv_allocset()) == NULL ||
+	    (npset = priv_allocset()) == NULL)
+		fatal("priv_allocset: %s", strerror(errno));
+
+	priv_basicset(npset);
+
+	if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 ||
+	    priv_addset(npset, PRIV_FILE_DAC_READ) != 0 ||
+	    priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 ||
+	    priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 ||
+	    priv_addset(npset, PRIV_FILE_OWNER) != 0)
+		fatal("priv_addset: %s", strerror(errno));
+
+	if (priv_delset(npset, PRIV_FILE_LINK_ANY) != 0 ||
+	    priv_delset(npset, PRIV_NET_ACCESS) != 0 ||
+	    priv_delset(npset, PRIV_PROC_EXEC) != 0 ||
+	    priv_delset(npset, PRIV_PROC_FORK) != 0 ||
+	    priv_delset(npset, PRIV_PROC_INFO) != 0 ||
+	    priv_delset(npset, PRIV_PROC_SESSION) != 0)
+		fatal("priv_delset: %s", strerror(errno));
+
+	if (getppriv(PRIV_PERMITTED, pset) != 0)
+		fatal("getppriv: %s", strerror(errno));
+
+	priv_intersect(pset, npset);
+
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0)
+		fatal("setppriv: %s", strerror(errno));
+
+	priv_freeset(pset);
+	priv_freeset(npset);
+}
+
+void
+solaris_drop_privs_root_pinfo_net(void)
+{
+	priv_set_t *pset = NULL;
+
+	if ((pset = priv_allocset()) == NULL)
+		fatal("priv_allocset: %s", strerror(errno));
+
+	/* Start with "basic" and drop everything we don't need. */
+	priv_basicset(pset);
+
+	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
+	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
+	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
+	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
+		fatal("priv_delset: %s", strerror(errno));
+
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
+		fatal("setppriv: %s", strerror(errno));
+
+	priv_freeset(pset);
+}
+
+void
+solaris_drop_privs_root_pinfo_net_exec(void)
+{
+	priv_set_t *pset = NULL;
+
+	if ((pset = priv_allocset()) == NULL)
+		fatal("priv_allocset: %s", strerror(errno));
+
+	/* Start with "basic" and drop everything we don't need. */
+	priv_basicset(pset);
+
+	if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 ||
+	    priv_delset(pset, PRIV_NET_ACCESS) != 0 ||
+	    priv_delset(pset, PRIV_PROC_EXEC) != 0 ||
+	    priv_delset(pset, PRIV_PROC_INFO) != 0 ||
+	    priv_delset(pset, PRIV_PROC_SESSION) != 0)
+		fatal("priv_delset: %s", strerror(errno));
+
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0)
+		fatal("setppriv: %s", strerror(errno));
+
+	priv_freeset(pset);
+}
+
+#endif
diff --git a/openbsd-compat/port-solaris.h b/openbsd-compat/port-solaris.h
index cd442e7..b077e18 100644
--- a/openbsd-compat/port-solaris.h
+++ b/openbsd-compat/port-solaris.h
@@ -26,5 +26,8 @@ void solaris_contract_pre_fork(void);
 void solaris_contract_post_fork_child(void);
 void solaris_contract_post_fork_parent(pid_t pid);
 void solaris_set_default_project(struct passwd *);
+void solaris_drop_privs_pinfo_net_fork_exec(void);
+void solaris_drop_privs_root_pinfo_net(void);
+void solaris_drop_privs_root_pinfo_net_exec(void);
 
 #endif
diff --git a/platform-pledge.c b/platform-pledge.c
new file mode 100644
index 0000000..4a6ec15
--- /dev/null
+++ b/platform-pledge.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015 Joyent, Inc
+ * Author: Alex Wilson <alex.wilson@joyent.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "platform.h"
+
+#include "openbsd-compat/openbsd-compat.h"
+
+/*
+ * Drop any fine-grained privileges that are not needed for post-startup
+ * operation of ssh-agent
+ *
+ * Should be as close as possible to pledge("stdio cpath unix id proc exec", ...)
+ */
+void
+platform_pledge_agent(void)
+{
+#ifdef USE_SOLARIS_PRIVS
+	/*
+	 * Note: Solaris priv dropping is closer to tame() than pledge(), but
+	 * we will use what we have.
+	 */
+	solaris_drop_privs_root_pinfo_net();
+#endif
+}
+
+/*
+ * Drop any fine-grained privileges that are not needed for post-startup
+ * operation of sftp-server
+ */
+void
+platform_pledge_sftp_server(void)
+{
+#ifdef USE_SOLARIS_PRIVS
+	solaris_drop_privs_pinfo_net_fork_exec();
+#endif
+}
+
+/*
+ * Drop any fine-grained privileges that are not needed for the post-startup
+ * operation of the SSH client mux
+ *
+ * Should be as close as possible to pledge("stdio proc tty", ...)
+ */
+void
+platform_pledge_mux(void)
+{
+#ifdef USE_SOLARIS_PRIVS
+	solaris_drop_privs_root_pinfo_net_exec();
+#endif
+}
diff --git a/platform.h b/platform.h
index 1c7a45d..e687c99 100644
--- a/platform.h
+++ b/platform.h
@@ -31,3 +31,8 @@ void platform_setusercontext_post_groups(struct passwd *);
 char *platform_get_krb5_client(const char *);
 char *platform_krb5_get_principal_name(const char *);
 int platform_sys_dir_uid(uid_t);
+
+/* in platform-pledge.c */
+void platform_pledge_agent(void);
+void platform_pledge_sftp_server(void);
+void platform_pledge_mux(void);
diff --git a/sandbox-solaris.c b/sandbox-solaris.c
new file mode 100644
index 0000000..98714e1
--- /dev/null
+++ b/sandbox-solaris.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 Joyent, Inc
+ * Author: Alex Wilson <alex.wilson@joyent.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#ifdef SANDBOX_SOLARIS
+#ifndef USE_SOLARIS_PRIVS
+# error "--with-solaris-privs must be used with the Solaris sandbox"
+#endif
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_PRIV_H
+# include <priv.h>
+#endif
+
+#include "log.h"
+#include "ssh-sandbox.h"
+#include "xmalloc.h"
+
+struct ssh_sandbox {
+	priv_set_t *pset;
+};
+
+struct ssh_sandbox *
+ssh_sandbox_init(struct monitor *monitor)
+{
+	struct ssh_sandbox *box = NULL;
+
+	box = xcalloc(1, sizeof(*box));
+	box->pset = priv_allocset();
+
+	if (box->pset == NULL) {
+		free(box);
+		return NULL;
+	}
+
+	/* Start with "basic" and drop everything we don't need. */
+	priv_basicset(box->pset);
+
+	/* Drop everything except the ability to use already-opened files */
+	if (priv_delset(box->pset, PRIV_FILE_LINK_ANY) != 0 ||
+	    priv_delset(box->pset, PRIV_NET_ACCESS) != 0 ||
+	    priv_delset(box->pset, PRIV_PROC_EXEC) != 0 ||
+	    priv_delset(box->pset, PRIV_PROC_FORK) != 0 ||
+	    priv_delset(box->pset, PRIV_PROC_INFO) != 0 ||
+	    priv_delset(box->pset, PRIV_PROC_SESSION) != 0) {
+		free(box);
+		return NULL;
+	}
+
+	/* These may not be available on older Solaris-es */
+# if defined(PRIV_FILE_READ) && defined(PRIV_FILE_WRITE)
+	if (priv_delset(box->pset, PRIV_FILE_READ) != 0 ||
+	    priv_delset(box->pset, PRIV_FILE_WRITE) != 0) {
+		free(box);
+		return NULL;
+	}
+# endif
+
+	return box;
+}
+
+void
+ssh_sandbox_child(struct ssh_sandbox *box)
+{
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, box->pset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_LIMIT, box->pset) != 0 ||
+	    setppriv(PRIV_SET, PRIV_INHERITABLE, box->pset) != 0)
+		fatal("setppriv: %s", strerror(errno));
+}
+
+void
+ssh_sandbox_parent_finish(struct ssh_sandbox *box)
+{
+	priv_freeset(box->pset);
+	box->pset = NULL;
+	free(box);
+}
+
+void
+ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid)
+{
+	/* Nothing to do here */
+}
+
+#endif /* SANDBOX_SOLARIS */
diff --git a/sftp-server.c b/sftp-server.c
index eac11d7..3877c03 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1598,6 +1598,9 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
 		fatal("unable to make the process undumpable");
 #endif /* defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) */
 
+	/* Drop any fine-grained privileges we don't need */
+	platform_pledge_sftp_server();
+
 	if ((cp = getenv("SSH_CONNECTION")) != NULL) {
 		client_addr = xstrdup(cp);
 		if ((cp = strchr(client_addr, ' ')) == NULL) {
diff --git a/ssh-agent.c b/ssh-agent.c
index a335ea3..6c2f6d5 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1402,6 +1402,8 @@ skip:
 	signal(SIGTERM, cleanup_handler);
 	nalloc = 0;
 
+	platform_pledge_agent();
+
 	while (1) {
 		prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp);
 		result = select(max_fd + 1, readsetp, writesetp, NULL, tvp);
diff --git a/uidswap.c b/uidswap.c
index 0702e1d..8bf6b24 100644
--- a/uidswap.c
+++ b/uidswap.c
@@ -134,7 +134,7 @@ temporarily_use_uid(struct passwd *pw)
 void
 permanently_drop_suid(uid_t uid)
 {
-#ifndef HAVE_CYGWIN
+#ifndef NO_UID_RESTORATION_TEST
 	uid_t old_uid = getuid();
 #endif
 
@@ -142,8 +142,14 @@ permanently_drop_suid(uid_t uid)
 	if (setresuid(uid, uid, uid) < 0)
 		fatal("setresuid %u: %.100s", (u_int)uid, strerror(errno));
 
-#ifndef HAVE_CYGWIN
-	/* Try restoration of UID if changed (test clearing of saved uid) */
+#ifndef NO_UID_RESTORATION_TEST
+	/*
+	 * Try restoration of UID if changed (test clearing of saved uid).
+	 *
+	 * Note that we don't do this on Cygwin, or on Solaris-based platforms
+	 * where fine-grained privileges are available (the user might be
+	 * deliberately allowed the right to setuid back to root).
+	 */
 	if (old_uid != uid &&
 	    (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
 		fatal("%s: was able to restore old [e]uid", __func__);
@@ -199,7 +205,7 @@ restore_uid(void)
 void
 permanently_set_uid(struct passwd *pw)
 {
-#ifndef HAVE_CYGWIN
+#ifndef NO_UID_RESTORATION_TEST
 	uid_t old_uid = getuid();
 	gid_t old_gid = getgid();
 #endif
@@ -227,7 +233,7 @@ permanently_set_uid(struct passwd *pw)
 	if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
 		fatal("setresuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno));
 
-#ifndef HAVE_CYGWIN
+#ifndef NO_UID_RESTORATION_TEST
 	/* Try restoration of GID if changed (test clearing of saved gid) */
 	if (old_gid != pw->pw_gid && pw->pw_uid != 0 &&
 	    (setgid(old_gid) != -1 || setegid(old_gid) != -1))
@@ -241,7 +247,7 @@ permanently_set_uid(struct passwd *pw)
 		    (u_int)pw->pw_gid);
 	}
 
-#ifndef HAVE_CYGWIN
+#ifndef NO_UID_RESTORATION_TEST
 	/* Try restoration of UID if changed (test clearing of saved uid) */
 	if (old_uid != pw->pw_uid &&
 	    (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
-- 
2.5.4 (Apple Git-61)

