[PATCH 33/67] policycoreutils: sandbox: FIXME rewrite /tmp handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


This patch looks good to me. acked.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk5yVRIACgkQrlYvE4MpobNTogCaA/XBPxA2GRq8Mux9nab3urM8
ivwAoKLZXJs4tVfbRBNTpQMXnlEgnDYs
=3GvE
-----END PGP SIGNATURE-----
>From 54ed5929b8f8ffac7bdc48d589c5cb38f6798530 Mon Sep 17 00:00:00 2001
From: Eric Paris <eparis@xxxxxxxxxx>
Date: Mon, 15 Aug 2011 19:58:08 -0400
Subject: [PATCH 33/67] policycoreutils: sandbox: FIXME rewrite /tmp handling

seunshare now creates a runtime temporary directory owned by root and
with the sticky bit set properly.  Files from the user-specified directory
are copied to the runtime directory and the changes synced back (using rsync)
at the end of the seunshare run.

review needed to changelog correctness/completeness

Signed-off-by: Eric Paris <eparis@xxxxxxxxxx>
Acked-by: Dan Walsh <dwalsh@xxxxxxxxxx>
---
 policycoreutils/sandbox/sandbox     |    8 +-
 policycoreutils/sandbox/seunshare.8 |    2 +-
 policycoreutils/sandbox/seunshare.c |  488 +++++++++++++++++++++++++++--------
 3 files changed, 386 insertions(+), 112 deletions(-)

diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
index 031fdc7..486cd4e 100644
--- a/policycoreutils/sandbox/sandbox
+++ b/policycoreutils/sandbox/sandbox
@@ -29,7 +29,6 @@ import commands
 import setools
 
 PROGNAME = "policycoreutils"
-HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
 SEUNSHARE = "/usr/sbin/seunshare"
 SANDBOXSH = "/usr/share/sandbox/sandboxX.sh"
 import gettext
@@ -374,23 +373,20 @@ sandbox [-h] [-c] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile
     def __setup_dir(self):
            if self.__options.level or self.__options.session:
                   return
-           sandboxdir = HOMEDIR + "/.sandbox"
-           if not os.path.exists(sandboxdir):
-                  os.mkdir(sandboxdir)
 
            if self.__options.homedir:
                   selinux.chcon(self.__options.homedir, self.__filecon, recursive=True)
                   self.__homedir = self.__options.homedir
            else:
                   selinux.setfscreatecon(self.__filecon)
-                  self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")
+                  self.__homedir = mkdtemp(dir="/tmp", prefix=".sandbox_home_")
 
            if self.__options.tmpdir:
                   selinux.chcon(self.__options.tmpdir, self.__filecon, recursive=True)
                   self.__tmpdir = self.__options.tmpdir
            else:
                   selinux.setfscreatecon(self.__filecon)
-                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox")
+                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox_tmp_")
            selinux.setfscreatecon(None)
            self.__copyfiles()
 
diff --git a/policycoreutils/sandbox/seunshare.8 b/policycoreutils/sandbox/seunshare.8
index a9b846b..a1bf3fa 100644
--- a/policycoreutils/sandbox/seunshare.8
+++ b/policycoreutils/sandbox/seunshare.8
@@ -16,7 +16,7 @@ within the specified context, using the alternate home directory and /tmp direct
 Alternate homedir to be used by the application.  Homedir must be owned by the user.
 .TP
 \fB\-t\ tmpdir
-Use alternate temporary directory to mount on /tmp.  tmpdir must be owned by the user.
+Use alternate tempory directory to mount on /tmp.  tmpdir must be owned by the user.
 .TP
 \fB\-c --cgroups\fR
 Use cgroups to control this copy of seunshare.  Specify parameters in /etc/sysconfig/sandbox.  Max memory usage and cpu usage are to be specified in percent.  You can specify which CPUs to use by numbering them 0,1,2... etc.
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
index 9645b23..a4d6cdc 100644
--- a/policycoreutils/sandbox/seunshare.c
+++ b/policycoreutils/sandbox/seunshare.c
@@ -10,6 +10,7 @@
 #include <sys/wait.h>
 #include <syslog.h>
 #include <sys/mount.h>
+#include <glob.h>
 #include <pwd.h>
 #include <sched.h>
 #include <libcgroup.h>
@@ -99,7 +100,7 @@ static int set_signal_handles(void)
 
 	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
 
-	/* Terminate on SIGHUP. */
+	/* Terminate on SIGHUP */
 	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
 		perror("Unable to set SIGHUP handler");
 		return -1;
@@ -150,26 +151,6 @@ static int spawn_command(const char *cmd, uid_t uid){
 }
 
 /**
- * This function makes sure the mounted directory is owned by the user executing
- * seunshare.
- * If so, it returns 0. If it can not figure this out or they are different, it returns -1.
- */
-static int verify_mount(const char *mntdir, struct passwd *pwd) {
-	struct stat sb;
-	if (stat(mntdir, &sb) == -1) {
-		fprintf(stderr, _("Invalid mount point %s: %s\n"), mntdir, strerror(errno));
-		return -1;
-	}
-	if (sb.st_uid != pwd->pw_uid) {
-		errno = EPERM;
-		syslog(LOG_AUTHPRIV | LOG_ALERT, "%s attempted to mount an invalid directory, %s", pwd->pw_name, mntdir);
-		perror(_("Invalid mount point, reporting to administrator"));
-		return -1;
-	}
-	return 0;
-}
-
-/**
  * Check file/directory ownership, struct stat * must be passed to the
  * functions.
  */
@@ -247,7 +228,7 @@ static int verify_shell(const char *shell_name)
 
 		/* check the shell skipping newline char */
 		if (!strcmp(shell_name, buf)) {
-			rc = 1;
+			rc = 0;
 			break;
 		}
 	}
@@ -255,26 +236,60 @@ static int verify_shell(const char *shell_name)
 	return rc;
 }
 
-static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) {
+/**
+ * Mount directory and check that we mounted the right directory.
+ */
+static int seunshare_mount(const char *src, const char *dst, struct stat *src_st)
+{
+	int flags = MS_REC;
+	int is_tmp = 0;
+
 	if (verbose)
-		printf("Mount %s on %s\n", src, dst);
-	if (mount(dst, dst,  NULL, MS_BIND | MS_REC, NULL) < 0) {
+		printf(_("Mounting %s on %s\n"), src, dst);
+
+	if (strcmp("/tmp", dst) == 0) {
+		flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC;
+		is_tmp = 1;
+	}
+
+	/* mount directory */
+	if (mount(dst, dst,  NULL, MS_BIND | flags, NULL) < 0) {
 		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), dst, dst, strerror(errno));
 		return -1;
 	}
-
-	if (mount(dst, dst, NULL, MS_PRIVATE | MS_REC, NULL) < 0) {
+	if (mount(dst, dst, NULL, MS_PRIVATE | flags, NULL) < 0) {
 		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
 		return -1;
 	}
-
-	if (mount(src, dst, NULL, MS_BIND | MS_REC, NULL) < 0) {
+	if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
 		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
 		return -1;
 	}
 
-	if (verify_mount(dst, pwd) < 0) 
-		return -1;
+	/* verify whether we mounted what we expected to mount */
+	if (verify_directory(dst, src_st, NULL) < 0) return -1;
+
+	/* bind mount /tmp on /var/tmp too */
+	if (is_tmp) {
+		if (verbose)
+			printf(_("Mounting /tmp on /var/tmp\n"));
+
+		if (mount("/var/tmp", "/var/tmp",  NULL, MS_BIND | flags, NULL) < 0) {
+			fprintf(stderr, _("Failed to mount /var/tmp on /var/tmp: %s\n"), strerror(errno));
+			return -1;
+		}
+		if (mount("/var/tmp", "/var/tmp", NULL, MS_PRIVATE | flags, NULL) < 0) {
+			fprintf(stderr, _("Failed to make /var/tmp private: %s\n"), strerror(errno));
+			return -1;
+		}
+		if (mount("/tmp", "/var/tmp",  NULL, MS_BIND | flags, NULL) < 0) {
+			fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno));
+			return -1;
+		}
+	}
+
+	return 0;
+
 }
 
 /**
@@ -285,7 +300,6 @@ static int sandbox_error(const char *string)
 	fprintf(stderr, string);
 	syslog(LOG_AUTHPRIV | LOG_ALERT, string);
 	exit(-1);
-
 }
 
 /**
@@ -485,18 +499,266 @@ err:
 	return rc;
 }
 
+/*
+   If path is empy or ends with  "/." or "/.. return -1 else return 0;
+ */
+static int bad_path(const char *path) {
+	const char *ptr;
+	ptr = path;
+	while (*ptr) ptr++;
+	if (ptr == path) return -1; // ptr null
+	ptr--;
+	if (ptr != path && *ptr  == '.') {
+		ptr--;
+		if (*ptr  == '/') return -1; // path ends in /.
+		if (*ptr  == '.') {
+			if (ptr != path) {
+				ptr--;
+				if (*ptr  == '/') return -1; // path ends in /..
+			}
+		}
+	}
+	return 0;
+}
+
+static int rsynccmd(const char * src, const char *dst, char **cmdbuf)
+{
+	char *buf = NULL;
+	char *newbuf = NULL;
+	glob_t fglob;
+	fglob.gl_offs = 0;
+	int flags = GLOB_PERIOD;
+	unsigned int i = 0;
+	int rc = -1;
+
+	/* match glob for all files in src dir */
+	if (asprintf(&buf, "%s/*", src) == -1) {
+		fprintf(stderr, "Out of memory\n");
+		return -1;
+	}
+
+	if (glob(buf, flags, NULL, &fglob) != 0) {
+		free(buf); buf = NULL;
+		return -1;
+	}
+
+	free(buf); buf = NULL;
+
+	for ( i=0; i < fglob.gl_pathc; i++) {
+		const char *path = fglob.gl_pathv[i];
+
+		if (bad_path(path)) continue;
+
+		if (!buf) {
+			if (asprintf(&newbuf, "\'%s\'", path) == -1) {
+				fprintf(stderr, "Out of memory\n");
+				goto err;
+			}
+		} else {
+			if (asprintf(&newbuf, "%s  \'%s\'", buf, path) == -1) {
+				fprintf(stderr, "Out of memory\n");
+				goto err;
+			}
+		}
+
+		free(buf); buf = newbuf;
+		newbuf = NULL;
+	}
+
+	if (buf) {
+		if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) {
+			fprintf(stderr, "Out of memory\n");
+			goto err;
+		}
+		*cmdbuf=newbuf;
+	}
+	else {
+		*cmdbuf=NULL;
+	}
+	rc = 0;
+
+err:
+	free(buf); buf = NULL;
+	globfree(&fglob);
+	return rc;
+}
+
+/**
+ * Clean up runtime temporary directory.  Returns 0 if no problem was detected,
+ * >0 if some error was detected, but errors here are treated as non-fatal and
+ * left to tmpwatch to finish incomplete cleanup.
+ */
+static int cleanup_tmpdir(const char *tmpdir, const char *src,
+	struct passwd *pwd, int copy_content)
+{
+	char *cmdbuf = NULL;
+	int rc = 0;
+
+	/* rsync files back */
+	if (copy_content) {
+		if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) {
+			fprintf(stderr, _("Out of memory\n"));
+			cmdbuf = NULL;
+			rc++;
+		}
+		if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
+			fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n"));
+			rc++;
+		}
+		free(cmdbuf); cmdbuf = NULL;
+	}
+
+	/* remove files from the runtime temporary directory */
+	if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
+		fprintf(stderr, _("Out of memory\n"));
+		cmdbuf = NULL;
+		rc++;
+	}
+	/* this may fail if there's root-owned file left in the runtime tmpdir */
+	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
+	free(cmdbuf); cmdbuf = NULL;
+
+	/* remove runtime temporary directory */
+	setfsuid(0);
+	if (rmdir(tmpdir) == -1)
+		fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
+	setfsuid(pwd->pw_uid);
+
+	return 0;
+}
+
+/**
+ * seunshare will create a tmpdir in /tmp, with root ownership.  The parent
+ * process waits for it child to exit to attempt to remove the directory.  If
+ * it fails to remove the directory, we will need to rely on tmpreaper/tmpwatch
+ * to clean it up.
+ */
+static char *create_tmpdir(const char *src, struct stat *src_st,
+	struct stat *out_st, struct passwd *pwd, security_context_t execcon)
+{
+	char *tmpdir = NULL;
+	char *cmdbuf = NULL;
+	int fd_t = -1, fd_s = -1;
+	struct stat tmp_st;
+	security_context_t con = NULL;
+
+	/* get selinux context */
+	if (execcon) {
+		setfsuid(pwd->pw_uid);
+		if ((fd_s = open(src, O_RDONLY)) < 0) {
+			fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno));
+			goto err;
+		}
+		if (fstat(fd_s, &tmp_st) == -1) {
+			fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno));
+			goto err;
+		}
+		if (!equal_stats(src_st, &tmp_st)) {
+			fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src);
+			goto err;
+		}
+		if (fgetfilecon(fd_s, &con) == -1) {
+			fprintf(stderr, _("Failed to get context of the directory %s: %s\n"), src, strerror(errno));
+			goto err;
+		}
+
+		/* ok to not reach this if there is an error */
+		setfsuid(0);
+	}
+
+	if (asprintf(&tmpdir, "/tmp/.sandbox-%s-XXXXXX", pwd->pw_name) == -1) {
+		fprintf(stderr, _("Out of memory\n"));
+		tmpdir = NULL;
+		goto err;
+	}
+	if (mkdtemp(tmpdir) == NULL) {
+		fprintf(stderr, _("Failed to create temporary directory: %s\n"), strerror(errno));
+		goto err;
+	}
+
+	/* temporary directory must be owned by root:user */
+	if (verify_directory(tmpdir, NULL, out_st) < 0) {
+		goto err;
+	}
+
+	if (check_owner_uid(0, tmpdir, out_st) < 0)
+		goto err;
+
+	if (check_owner_gid(getgid(), tmpdir, out_st) < 0)
+		goto err;
+
+	/* change permissions of the temporary directory */
+	if ((fd_t = open(tmpdir, O_RDONLY)) < 0) {
+		fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+	if (fstat(fd_t, &tmp_st) == -1) {
+		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+	if (!equal_stats(out_st, &tmp_st)) {
+		fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir);
+		goto err;
+	}
+	if (fchmod(fd_t, 01770) == -1) {
+		fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+	/* re-stat again to pick change mode */
+	if (fstat(fd_t, out_st) == -1) {
+		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+
+	/* copy selinux context */
+	if (execcon) {
+		if (fsetfilecon(fd_t, con) == -1) {
+			fprintf(stderr, _("Failed to set context of the directory %s: %s\n"), tmpdir, strerror(errno));
+			goto err;
+		}
+	}
+
+	setfsuid(pwd->pw_uid);
+
+	if (rsynccmd(src, tmpdir, &cmdbuf) < 0) {
+		goto err;
+	}
+
+	/* ok to not reach this if there is an error */
+	setfsuid(0);
+
+	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
+		fprintf(stderr, _("Failed to populate runtime temporary directory\n"));
+		cleanup_tmpdir(tmpdir, src, pwd, 0);
+		goto err;
+	}
+
+	goto good;
+err:
+	free(tmpdir); tmpdir = NULL;
+good:
+	free(cmdbuf); cmdbuf = NULL;
+	freecon(con); con = NULL;
+	if (fd_t >= 0) close(fd_t);
+	if (fd_s >= 0) close(fd_s);
+	return tmpdir;
+}
+
 int main(int argc, char **argv) {
-	int rc;
 	int status = -1;
+	security_context_t execcon = NULL;
 
-	security_context_t scontext = NULL;
-
-	int flag_index;		/* flag index in argv[] */
 	int clflag;		/* holds codes for command line flags */
-	char *tmpdir_s = NULL;	/* tmpdir spec'd by user in argv[] */
-	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
 	int usecgroups = 0;
 
+	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
+	char *tmpdir_s = NULL;	/* tmpdir spec'd by user in argv[] */
+	char *tmpdir_r = NULL;	/* tmpdir created by seunshare */
+
+	struct stat st_homedir;
+	struct stat st_tmpdir_s;
+	struct stat st_tmpdir_r;
+
 	const struct option long_options[] = {
 		{"homedir", 1, 0, 'h'},
 		{"tmpdir", 1, 0, 't'},
@@ -528,7 +790,7 @@ int main(int argc, char **argv) {
 	}
 
 	if (verify_shell(pwd->pw_shell) < 0) {
-		fprintf(stderr, _("Error!  Shell is not valid.\n"));
+		fprintf(stderr, _("Error: User shell is not valid\n"));
 		return -1;
 	}
 
@@ -539,22 +801,13 @@ int main(int argc, char **argv) {
 
 		switch (clflag) {
 		case 't':
-			if (!(tmpdir_s = realpath(optarg, NULL))) {
-				fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno));
-				return -1;
-			}
-			if (verify_mount(tmpdir_s, pwd) < 0) return -1;
+			tmpdir_s = optarg;
 			break;
 		case 'h':
-			if (!(homedir_s = realpath(optarg, NULL))) {
-				fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno));
-				return -1;
-			}
-			if (verify_mount(homedir_s, pwd) < 0) return -1;
-			if (verify_mount(pwd->pw_dir, pwd) < 0) return -1;
+			homedir_s = optarg;
 			break;
 		case 'v':
-			verbose = 1;
+			verbose++;
 			break;
 		case 'c':
 			usecgroups = 1;
@@ -563,7 +816,7 @@ int main(int argc, char **argv) {
 			cap_set = CAPNG_SELECT_CAPS;
 			break;
 		case 'Z':
-			scontext = strdup(optarg);
+			execcon = optarg;
 			break;
 		default:
 			fprintf(stderr, "%s\n", USAGE_STRING);
@@ -572,13 +825,17 @@ int main(int argc, char **argv) {
 	}
 
 	if (! homedir_s && ! tmpdir_s) {
-		fprintf(stderr, _("Error: tmpdir and/or homedir required \n"),
-			"%s\n", USAGE_STRING);
+		fprintf(stderr, _("Error: tmpdir and/or homedir required\n %s\n"), USAGE_STRING);
 		return -1;
 	}
 
 	if (argc - optind < 1) {
-		fprintf(stderr, _("Error: executable required \n %s \n"), USAGE_STRING);
+		fprintf(stderr, _("Error: executable required\n %s\n"), USAGE_STRING);
+		return -1;
+	}
+
+	if (execcon && is_selinux_enabled() != 1) {
+		fprintf(stderr, _("Error: execution context specified, but SELinux is not enabled\n"));
 		return -1;
 	}
 
@@ -588,84 +845,105 @@ int main(int argc, char **argv) {
 	if (usecgroups && setup_cgroups() < 0)
 		return  -1;
 
-        if (unshare(CLONE_NEWNS) < 0) {
-		perror(_("Failed to unshare"));
+	/* set fsuid to ruid */
+	/* Changing fsuid is usually required when user-specified directory is
+	 * on an NFS mount.  It's also desired to avoid leaking info about
+	 * existence of the files not accessible to the user. */
+	setfsuid(uid);
+
+	/* verify homedir and tmpdir */
+	if (homedir_s && (
+		verify_directory(homedir_s, NULL, &st_homedir) < 0 ||
+		check_owner_uid(uid, homedir_s, &st_homedir))) return -1;
+	if (tmpdir_s && (
+		verify_directory(tmpdir_s, NULL, &st_tmpdir_s) < 0 ||
+		check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))) return -1;
+	setfsuid(0);
+
+	/* create runtime tmpdir */
+	if (tmpdir_s && (tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s,
+						  &st_tmpdir_r, pwd, execcon)) == NULL) {
+		fprintf(stderr, _("Failed to create runtime temporary directory\n"));
 		return -1;
 	}
 
-	if (homedir_s && tmpdir_s && (strncmp(pwd->pw_dir, tmpdir_s, strlen(pwd->pw_dir)) == 0)) {
-	    if (seunshare_mount(tmpdir_s, "/tmp", pwd) < 0)
-		    return -1;
-	    if (seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0)
-		    return -1;
-	} else {			
-		if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0)
-				return -1;
-				
-		if (tmpdir_s && seunshare_mount(tmpdir_s, "/tmp", pwd) < 0)
-				return -1;
-	}
-
-	if (drop_privs(uid))
-		return -1;
-
+	/* spawn child process */
 	int child = fork();
 	if (child == -1) {
 		perror(_("Unable to fork"));
-		return -1;
+		goto err;
 	}
 
-	if (!child) {
-		char *display=NULL;
-		/* Construct a new environment */
-		char *d = getenv("DISPLAY");
-		if (d) {
-			display =  strdup(d);
-			if (!display) {
-				perror(_("Out of memory"));
-				exit(-1);
-			}
-		}
+	if (child == 0) {
+		char *display = NULL;
+		int rc = -1;
 
-		if ((rc = clearenv())) {
-			perror(_("Unable to clear environment"));
-			free(display);
-			exit(-1);
+		if (unshare(CLONE_NEWNS) < 0) {
+			perror(_("Failed to unshare"));
+			goto childerr;
 		}
 
-		if (scontext) {
-			if (setexeccon(scontext)) {
-				fprintf(stderr, _("Could not set exec context to %s.\n"),
-					scontext);
-				free(display);
-				exit(-1);
+		/* assume fsuid==ruid after this point */
+		setfsuid(uid);
+
+		/* mount homedir and tmpdir, in this order */
+		if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir,
+			&st_homedir) != 0) goto childerr;
+		if (tmpdir_s &&	seunshare_mount(tmpdir_r, "/tmp",
+			&st_tmpdir_r) != 0) goto childerr;
+
+		if (drop_privs(uid) != 0) goto childerr;
+
+		/* construct a new environment */
+		if ((display = getenv("DISPLAY")) != NULL) {
+			if ((display = strdup(display)) == NULL) {
+				perror(_("Out of memory"));
+				goto childerr;
 			}
 		}
-
-		if (display) 
+		if ((rc = clearenv()) != 0) {
+			perror(_("Failed to clear environment"));
+			goto childerr;
+		}
+		if (display)
 			rc |= setenv("DISPLAY", display, 1);
 		rc |= setenv("HOME", pwd->pw_dir, 1);
 		rc |= setenv("SHELL", pwd->pw_shell, 1);
 		rc |= setenv("USER", pwd->pw_name, 1);
 		rc |= setenv("LOGNAME", pwd->pw_name, 1);
 		rc |= setenv("PATH", DEFAULT_PATH, 1);
-		
+		if (rc != 0) {
+			fprintf(stderr, _("Failed to construct environment\n"));
+			goto childerr;
+		}
+
+		/* selinux context */
+		if (execcon && setexeccon(execcon) != 0) {
+			fprintf(stderr, _("Could not set exec context to %s.\n"), execcon);
+			goto childerr;
+		}
+
 		if (chdir(pwd->pw_dir)) {
 			perror(_("Failed to change dir to homedir"));
-			exit(-1);
+			goto childerr;
 		}
 		setsid();
 		execv(argv[optind], argv + optind);
+		fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno));
+childerr:
 		free(display);
-		perror("execv");
 		exit(-1);
-	} else {
-		waitpid(child, &status, 0);
 	}
 
-	free(tmpdir_s);
-	free(homedir_s);
-	free(scontext);
+	drop_caps();
 
+	/* parent waits for child exit to do the cleanup */
+	waitpid(child, &status, 0);
+	status_to_retval(status, status);
+
+	if (tmpdir_r) cleanup_tmpdir(tmpdir_r, tmpdir_s, pwd, 1);
+
+err:
+	free(tmpdir_r);
 	return status;
 }
-- 
1.7.6.2


[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux