1e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris/*
2e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris * Authors: Dan Walsh <dwalsh@redhat.com>
3e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris * Authors: Thomas Liu <tliu@fedoraproject.org>
4e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris */
5e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris
639066bd0ac3839d9c247dd1b769906c7100a10e0Eric Paris#define _GNU_SOURCE
7d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <signal.h>
831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris#include <sys/fsuid.h>
931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris#include <sys/stat.h>
10d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <sys/types.h>
11d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <sys/wait.h>
12d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <syslog.h>
13d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <sys/mount.h>
1431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris#include <glob.h>
15d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <pwd.h>
16d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <sched.h>
17d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <string.h>
18d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <stdio.h>
194347a5c01d79778ffb9c74b02cd174b0469670c8Eric Paris#include <regex.h>
20d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <unistd.h>
21d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <stdlib.h>
22d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <cap-ng.h>
23d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <getopt.h>		/* for getopt_long() form of getopt() */
24d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <limits.h>
25d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <stdlib.h>
26d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <errno.h>
2731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris#include <fcntl.h>
28d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
29d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <selinux/selinux.h>
30d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <selinux/context.h>	/* for context-mangling functions */
31e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh#include <dirent.h>
32d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
33d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#ifdef USE_NLS
34d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <locale.h>		/* for setlocale() */
35d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#include <libintl.h>		/* for gettext() */
36d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#define _(msgid) gettext (msgid)
37d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#else
38d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#define _(msgid) (msgid)
39d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh#endif
40d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
41582fd00c7b493010f93696f0bfcc55412ab40c07Steve Lawrence#ifndef MS_REC
42582fd00c7b493010f93696f0bfcc55412ab40c07Steve Lawrence#define MS_REC 1<<14
43582fd00c7b493010f93696f0bfcc55412ab40c07Steve Lawrence#endif
44582fd00c7b493010f93696f0bfcc55412ab40c07Steve Lawrence
4570c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh#ifndef MS_SLAVE
4670c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh#define MS_SLAVE 1<<19
47582fd00c7b493010f93696f0bfcc55412ab40c07Steve Lawrence#endif
48582fd00c7b493010f93696f0bfcc55412ab40c07Steve Lawrence
49e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris#ifndef PACKAGE
50e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris#define PACKAGE "policycoreutils"	/* the name of this package lang translation */
51e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris#endif
52e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris
534347a5c01d79778ffb9c74b02cd174b0469670c8Eric Paris#define BUF_SIZE 1024
54406ae12e31ac60ccbecc67dc1314dd88491ca9cdEric Paris#define DEFAULT_PATH "/usr/bin:/bin"
55de0795a12edfa8c1239c3165bc4d6c566b6875afDan Walsh#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -C ] [ -k ] [ -t tmpdir ] [ -h homedir ] [ -Z CONTEXT ] -- executable [args] ")
56406ae12e31ac60ccbecc67dc1314dd88491ca9cdEric Paris
57406ae12e31ac60ccbecc67dc1314dd88491ca9cdEric Parisstatic int verbose = 0;
58216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walshstatic int child = 0;
59406ae12e31ac60ccbecc67dc1314dd88491ca9cdEric Paris
601f0b5bd920c8c43afb215b7455a9691198bd3a51Dan Walshstatic capng_select_t cap_set = CAPNG_SELECT_CAPS;
61149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh
62d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh/**
63d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris * This function will drop all capabilities.
64d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh */
65c4a4a1a7ed42c167a7d4bae06a1fffa8c6c9cb8dNicolas Ioossstatic int drop_caps(void)
66d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh{
67149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh	if (capng_have_capabilities(cap_set) == CAPNG_NONE)
68d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris		return 0;
69149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh	capng_clear(cap_set);
70149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh	if (capng_lock() == -1 || capng_apply(cap_set) == -1) {
71d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris		fprintf(stderr, _("Failed to drop all capabilities\n"));
72d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
73d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris	}
74d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris	return 0;
75d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris}
76d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris
77d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris/**
78d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris * This function will drop all privileges.
79d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris */
80d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Parisstatic int drop_privs(uid_t uid)
81d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris{
82d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris	if (drop_caps() == -1 || setresuid(uid, uid, uid) == -1) {
83d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris		fprintf(stderr, _("Failed to drop privileges\n"));
84d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
85d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
86d6c09608cd6a1c29fa2befd1b9769350f3bdee50Eric Paris	return 0;
87d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh}
88d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
89d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh/**
90216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh * If the user sends a siginto to seunshare, kill the child's session
91216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh */
92216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walshvoid handler(int sig) {
93216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh	if (child > 0) kill(-child,sig);
94216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh}
95216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh
96216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh/**
9789e3dd6c30edc2ffa1e52e8ed162c1085c6d6c9bEric Paris * Take care of any signal setup.
98d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh */
99d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walshstatic int set_signal_handles(void)
100d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh{
101d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	sigset_t empty;
102d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
103d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	/* Empty the signal mask in case someone is blocking a signal */
104d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (sigemptyset(&empty)) {
105d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		fprintf(stderr, "Unable to obtain empty signal set\n");
106d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
107d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
108d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
109d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
110d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
11131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* Terminate on SIGHUP */
112d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
113d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		perror("Unable to set SIGHUP handler");
114d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
115d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
116d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
117216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh	if (signal(SIGINT, handler) == SIG_ERR) {
118216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh		perror("Unable to set SIGINT handler");
119216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh		return -1;
120216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh	}
121216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh
122d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	return 0;
123d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh}
124d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
125f6558d9cecae6653e589039359465f796cca7d9aEric Paris#define status_to_retval(status,retval) do { \
126f6558d9cecae6653e589039359465f796cca7d9aEric Paris	if ((status) == -1) \
127f6558d9cecae6653e589039359465f796cca7d9aEric Paris		retval = -1; \
128f6558d9cecae6653e589039359465f796cca7d9aEric Paris	else if (WIFEXITED((status))) \
129f6558d9cecae6653e589039359465f796cca7d9aEric Paris		retval = WEXITSTATUS((status)); \
130f6558d9cecae6653e589039359465f796cca7d9aEric Paris	else if (WIFSIGNALED((status))) \
131f6558d9cecae6653e589039359465f796cca7d9aEric Paris		retval = 128 + WTERMSIG((status)); \
132f6558d9cecae6653e589039359465f796cca7d9aEric Paris	else \
133f6558d9cecae6653e589039359465f796cca7d9aEric Paris		retval = -1; \
134f6558d9cecae6653e589039359465f796cca7d9aEric Paris	} while(0)
135f6558d9cecae6653e589039359465f796cca7d9aEric Paris
136f6558d9cecae6653e589039359465f796cca7d9aEric Paris/**
137f6558d9cecae6653e589039359465f796cca7d9aEric Paris * Spawn external command using system() with dropped privileges.
138f6558d9cecae6653e589039359465f796cca7d9aEric Paris * TODO: avoid system() and use exec*() instead
139f6558d9cecae6653e589039359465f796cca7d9aEric Paris */
140f6558d9cecae6653e589039359465f796cca7d9aEric Parisstatic int spawn_command(const char *cmd, uid_t uid){
141f978b1b071fe38ecb406c1035df0912c43924b7dNicolas Iooss	int childpid;
142f6558d9cecae6653e589039359465f796cca7d9aEric Paris	int status = -1;
143f6558d9cecae6653e589039359465f796cca7d9aEric Paris
144f6558d9cecae6653e589039359465f796cca7d9aEric Paris	if (verbose > 1)
145f6558d9cecae6653e589039359465f796cca7d9aEric Paris		printf("spawn_command: %s\n", cmd);
146f6558d9cecae6653e589039359465f796cca7d9aEric Paris
147f978b1b071fe38ecb406c1035df0912c43924b7dNicolas Iooss	childpid = fork();
148f978b1b071fe38ecb406c1035df0912c43924b7dNicolas Iooss	if (childpid == -1) {
149f6558d9cecae6653e589039359465f796cca7d9aEric Paris		perror(_("Unable to fork"));
150f6558d9cecae6653e589039359465f796cca7d9aEric Paris		return status;
151f6558d9cecae6653e589039359465f796cca7d9aEric Paris	}
152f6558d9cecae6653e589039359465f796cca7d9aEric Paris
153f978b1b071fe38ecb406c1035df0912c43924b7dNicolas Iooss	if (childpid == 0) {
154f6558d9cecae6653e589039359465f796cca7d9aEric Paris		if (drop_privs(uid) != 0) exit(-1);
155f6558d9cecae6653e589039359465f796cca7d9aEric Paris
156f6558d9cecae6653e589039359465f796cca7d9aEric Paris		status = system(cmd);
157f6558d9cecae6653e589039359465f796cca7d9aEric Paris		status_to_retval(status, status);
158f6558d9cecae6653e589039359465f796cca7d9aEric Paris		exit(status);
159f6558d9cecae6653e589039359465f796cca7d9aEric Paris	}
160f6558d9cecae6653e589039359465f796cca7d9aEric Paris
161f978b1b071fe38ecb406c1035df0912c43924b7dNicolas Iooss	waitpid(childpid, &status, 0);
162f6558d9cecae6653e589039359465f796cca7d9aEric Paris	status_to_retval(status, status);
163f6558d9cecae6653e589039359465f796cca7d9aEric Paris	return status;
164f6558d9cecae6653e589039359465f796cca7d9aEric Paris}
165f6558d9cecae6653e589039359465f796cca7d9aEric Paris
166d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh/**
167bf22cff3ea931abfe431856b015390600f969770Eric Paris * Check file/directory ownership, struct stat * must be passed to the
168bf22cff3ea931abfe431856b015390600f969770Eric Paris * functions.
169bf22cff3ea931abfe431856b015390600f969770Eric Paris */
170bf22cff3ea931abfe431856b015390600f969770Eric Parisstatic int check_owner_uid(uid_t uid, const char *file, struct stat *st) {
171bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (S_ISLNK(st->st_mode)) {
172bf22cff3ea931abfe431856b015390600f969770Eric Paris		fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
173bf22cff3ea931abfe431856b015390600f969770Eric Paris		return -1;
174bf22cff3ea931abfe431856b015390600f969770Eric Paris	}
175bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (st->st_uid != uid) {
176bf22cff3ea931abfe431856b015390600f969770Eric Paris		fprintf(stderr, _("Error: %s not owned by UID %d\n"), file, uid);
177bf22cff3ea931abfe431856b015390600f969770Eric Paris		return -1;
178bf22cff3ea931abfe431856b015390600f969770Eric Paris	}
179bf22cff3ea931abfe431856b015390600f969770Eric Paris	return 0;
180bf22cff3ea931abfe431856b015390600f969770Eric Paris}
181bf22cff3ea931abfe431856b015390600f969770Eric Paris
182bf22cff3ea931abfe431856b015390600f969770Eric Parisstatic int check_owner_gid(gid_t gid, const char *file, struct stat *st) {
183bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (S_ISLNK(st->st_mode)) {
184bf22cff3ea931abfe431856b015390600f969770Eric Paris		fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
185bf22cff3ea931abfe431856b015390600f969770Eric Paris		return -1;
186bf22cff3ea931abfe431856b015390600f969770Eric Paris	}
187bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (st->st_gid != gid) {
188bf22cff3ea931abfe431856b015390600f969770Eric Paris		fprintf(stderr, _("Error: %s not owned by GID %d\n"), file, gid);
189bf22cff3ea931abfe431856b015390600f969770Eric Paris		return -1;
190bf22cff3ea931abfe431856b015390600f969770Eric Paris	}
191bf22cff3ea931abfe431856b015390600f969770Eric Paris	return 0;
192bf22cff3ea931abfe431856b015390600f969770Eric Paris}
193bf22cff3ea931abfe431856b015390600f969770Eric Paris
194bf22cff3ea931abfe431856b015390600f969770Eric Paris#define equal_stats(one,two) \
195bf22cff3ea931abfe431856b015390600f969770Eric Paris	((one)->st_dev == (two)->st_dev && (one)->st_ino == (two)->st_ino && \
196bf22cff3ea931abfe431856b015390600f969770Eric Paris	 (one)->st_uid == (two)->st_uid && (one)->st_gid == (two)->st_gid && \
197bf22cff3ea931abfe431856b015390600f969770Eric Paris	 (one)->st_mode == (two)->st_mode)
198bf22cff3ea931abfe431856b015390600f969770Eric Paris
199bf22cff3ea931abfe431856b015390600f969770Eric Paris/**
200bf22cff3ea931abfe431856b015390600f969770Eric Paris * Sanity check specified directory.  Store stat info for future comparison, or
201bf22cff3ea931abfe431856b015390600f969770Eric Paris * compare with previously saved info to detect replaced directories.
202bf22cff3ea931abfe431856b015390600f969770Eric Paris * Note: This function does not perform owner checks.
203bf22cff3ea931abfe431856b015390600f969770Eric Paris */
204bf22cff3ea931abfe431856b015390600f969770Eric Parisstatic int verify_directory(const char *dir, struct stat *st_in, struct stat *st_out) {
205bf22cff3ea931abfe431856b015390600f969770Eric Paris	struct stat sb;
206bf22cff3ea931abfe431856b015390600f969770Eric Paris
207bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (st_out == NULL) st_out = &sb;
208bf22cff3ea931abfe431856b015390600f969770Eric Paris
209bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (lstat(dir, st_out) == -1) {
210bf22cff3ea931abfe431856b015390600f969770Eric Paris		fprintf(stderr, _("Failed to stat %s: %s\n"), dir, strerror(errno));
211bf22cff3ea931abfe431856b015390600f969770Eric Paris		return -1;
212bf22cff3ea931abfe431856b015390600f969770Eric Paris	}
213bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (! S_ISDIR(st_out->st_mode)) {
214bf22cff3ea931abfe431856b015390600f969770Eric Paris		fprintf(stderr, _("Error: %s is not a directory: %s\n"), dir, strerror(errno));
215bf22cff3ea931abfe431856b015390600f969770Eric Paris		return -1;
216bf22cff3ea931abfe431856b015390600f969770Eric Paris	}
217bf22cff3ea931abfe431856b015390600f969770Eric Paris	if (st_in && !equal_stats(st_in, st_out)) {
218bf22cff3ea931abfe431856b015390600f969770Eric Paris		fprintf(stderr, _("Error: %s was replaced by a different directory\n"), dir);
219bf22cff3ea931abfe431856b015390600f969770Eric Paris		return -1;
220bf22cff3ea931abfe431856b015390600f969770Eric Paris	}
221bf22cff3ea931abfe431856b015390600f969770Eric Paris
222bf22cff3ea931abfe431856b015390600f969770Eric Paris	return 0;
223bf22cff3ea931abfe431856b015390600f969770Eric Paris}
224bf22cff3ea931abfe431856b015390600f969770Eric Paris
225bf22cff3ea931abfe431856b015390600f969770Eric Paris/**
226d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh * This function checks to see if the shell is known in /etc/shells.
227d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh * If so, it returns 0. On error or illegal shell, it returns -1.
228d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh */
229d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walshstatic int verify_shell(const char *shell_name)
230d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh{
231d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	int rc = -1;
232d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	const char *buf;
233d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
234d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (!(shell_name && shell_name[0]))
235d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return rc;
236d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
237d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	while ((buf = getusershell()) != NULL) {
238d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		/* ignore comments */
239d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		if (*buf == '#')
240d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			continue;
241d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
242d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		/* check the shell skipping newline char */
243d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		if (!strcmp(shell_name, buf)) {
24431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			rc = 0;
245d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			break;
246d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		}
247d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
248d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	endusershell();
249d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	return rc;
250d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh}
251d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
25231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris/**
25331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * Mount directory and check that we mounted the right directory.
25431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris */
25531edb319affb5e5c6298a53ca2de62abedb01630Eric Parisstatic int seunshare_mount(const char *src, const char *dst, struct stat *src_st)
25631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris{
25770c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh	int flags = 0;
25831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	int is_tmp = 0;
25931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
260d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (verbose)
26131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		printf(_("Mounting %s on %s\n"), src, dst);
26231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
26331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (strcmp("/tmp", dst) == 0) {
26431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC;
26531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		is_tmp = 1;
26631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
26731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
26831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* mount directory */
26931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
270d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
271d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
272d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
273d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
27431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* verify whether we mounted what we expected to mount */
27531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (verify_directory(dst, src_st, NULL) < 0) return -1;
27631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
27731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* bind mount /tmp on /var/tmp too */
27831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (is_tmp) {
27931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (verbose)
28031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			printf(_("Mounting /tmp on /var/tmp\n"));
28131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
28231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (mount("/tmp", "/var/tmp",  NULL, MS_BIND | flags, NULL) < 0) {
28331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno));
28431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			return -1;
28531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
28631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
28731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
28831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	return 0;
28931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
290d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh}
291d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
29231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris/*
29331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris   If path is empy or ends with  "/." or "/.. return -1 else return 0;
29431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris */
29531edb319affb5e5c6298a53ca2de62abedb01630Eric Parisstatic int bad_path(const char *path) {
29631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	const char *ptr;
29731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	ptr = path;
29831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	while (*ptr) ptr++;
29931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (ptr == path) return -1; // ptr null
30031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	ptr--;
30131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (ptr != path && *ptr  == '.') {
30231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		ptr--;
30331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (*ptr  == '/') return -1; // path ends in /.
30431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (*ptr  == '.') {
30531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			if (ptr != path) {
30631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				ptr--;
30731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				if (*ptr  == '/') return -1; // path ends in /..
30831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			}
30931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
31031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
31131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	return 0;
31231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris}
31331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
31431edb319affb5e5c6298a53ca2de62abedb01630Eric Parisstatic int rsynccmd(const char * src, const char *dst, char **cmdbuf)
31531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris{
31631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *buf = NULL;
31731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *newbuf = NULL;
31831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	glob_t fglob;
31931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	fglob.gl_offs = 0;
32031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	int flags = GLOB_PERIOD;
32131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	unsigned int i = 0;
32231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	int rc = -1;
32331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
32431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* match glob for all files in src dir */
32531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (asprintf(&buf, "%s/*", src) == -1) {
32631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, "Out of memory\n");
32731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		return -1;
32831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
32931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
33031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (glob(buf, flags, NULL, &fglob) != 0) {
33131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		free(buf); buf = NULL;
33231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		return -1;
33331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
33431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
33531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	free(buf); buf = NULL;
33631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
33731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	for ( i=0; i < fglob.gl_pathc; i++) {
33831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		const char *path = fglob.gl_pathv[i];
33931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
34031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (bad_path(path)) continue;
34131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
34231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (!buf) {
34331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			if (asprintf(&newbuf, "\'%s\'", path) == -1) {
34431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				fprintf(stderr, "Out of memory\n");
34531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				goto err;
34631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			}
34731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		} else {
34831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			if (asprintf(&newbuf, "%s  \'%s\'", buf, path) == -1) {
34931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				fprintf(stderr, "Out of memory\n");
35031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				goto err;
35131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			}
35231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
35331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
35431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		free(buf); buf = newbuf;
35531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		newbuf = NULL;
35631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
35731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
35831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (buf) {
35931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) {
36031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, "Out of memory\n");
36131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto err;
36231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
36331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		*cmdbuf=newbuf;
36431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
36531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	else {
36631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		*cmdbuf=NULL;
36731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
36831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	rc = 0;
36931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
37031edb319affb5e5c6298a53ca2de62abedb01630Eric Pariserr:
37131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	free(buf); buf = NULL;
37231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	globfree(&fglob);
37331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	return rc;
37431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris}
37531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
37631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris/**
37731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * Clean up runtime temporary directory.  Returns 0 if no problem was detected,
37831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * >0 if some error was detected, but errors here are treated as non-fatal and
37931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * left to tmpwatch to finish incomplete cleanup.
38031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris */
38131edb319affb5e5c6298a53ca2de62abedb01630Eric Parisstatic int cleanup_tmpdir(const char *tmpdir, const char *src,
38231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	struct passwd *pwd, int copy_content)
38331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris{
38431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *cmdbuf = NULL;
38531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	int rc = 0;
38631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
38731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* rsync files back */
38831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (copy_content) {
38931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) {
39031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Out of memory\n"));
39131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			cmdbuf = NULL;
39231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			rc++;
39331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
39431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
39531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n"));
39631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			rc++;
39731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
39831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		free(cmdbuf); cmdbuf = NULL;
39931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
40031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
40131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* remove files from the runtime temporary directory */
40231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
40331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Out of memory\n"));
40431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		cmdbuf = NULL;
40531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		rc++;
40631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
40731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* this may fail if there's root-owned file left in the runtime tmpdir */
40831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
40931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	free(cmdbuf); cmdbuf = NULL;
41031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
41131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* remove runtime temporary directory */
412e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh	if ((uid_t)setfsuid(0) != 0) {
413e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		/* setfsuid does not return errror, but this check makes code checkers happy */
414221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		rc++;
415221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris	}
416221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris
41731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (rmdir(tmpdir) == -1)
41831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
419221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris	if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
420221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
421221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		rc++;
422221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris	}
42331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
424221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris	return rc;
42531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris}
42631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
42731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris/**
42831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * seunshare will create a tmpdir in /tmp, with root ownership.  The parent
42931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * process waits for it child to exit to attempt to remove the directory.  If
43031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * it fails to remove the directory, we will need to rely on tmpreaper/tmpwatch
43131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris * to clean it up.
43231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris */
43331edb319affb5e5c6298a53ca2de62abedb01630Eric Parisstatic char *create_tmpdir(const char *src, struct stat *src_st,
43431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	struct stat *out_st, struct passwd *pwd, security_context_t execcon)
43531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris{
43631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *tmpdir = NULL;
43731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *cmdbuf = NULL;
43831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	int fd_t = -1, fd_s = -1;
43931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	struct stat tmp_st;
44031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	security_context_t con = NULL;
44131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
44231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* get selinux context */
44331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (execcon) {
444221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		if ((uid_t)setfsuid(pwd->pw_uid) != 0)
445221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris			goto err;
446221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris
44731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if ((fd_s = open(src, O_RDONLY)) < 0) {
44831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno));
44931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto err;
45031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
45131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (fstat(fd_s, &tmp_st) == -1) {
45231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno));
45331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto err;
45431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
45531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (!equal_stats(src_st, &tmp_st)) {
45631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src);
45731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto err;
45831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
45931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (fgetfilecon(fd_s, &con) == -1) {
46031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Failed to get context of the directory %s: %s\n"), src, strerror(errno));
46131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto err;
46231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
46331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
46431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		/* ok to not reach this if there is an error */
465221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		if ((uid_t)setfsuid(0) != pwd->pw_uid)
466221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris			goto err;
46731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
46831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
46931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (asprintf(&tmpdir, "/tmp/.sandbox-%s-XXXXXX", pwd->pw_name) == -1) {
47031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Out of memory\n"));
47131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		tmpdir = NULL;
47231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
47331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
47431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (mkdtemp(tmpdir) == NULL) {
47531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to create temporary directory: %s\n"), strerror(errno));
47631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
47731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
47831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
47931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* temporary directory must be owned by root:user */
48031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (verify_directory(tmpdir, NULL, out_st) < 0) {
48131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
48231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
48331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
48431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (check_owner_uid(0, tmpdir, out_st) < 0)
48531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
48631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
48731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (check_owner_gid(getgid(), tmpdir, out_st) < 0)
48831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
48931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
49031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* change permissions of the temporary directory */
49131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if ((fd_t = open(tmpdir, O_RDONLY)) < 0) {
49231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno));
49331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
49431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
49531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (fstat(fd_t, &tmp_st) == -1) {
49631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
49731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
49831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
49931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (!equal_stats(out_st, &tmp_st)) {
50031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir);
50131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
50231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
50331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (fchmod(fd_t, 01770) == -1) {
50431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno));
50531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
50631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
50731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* re-stat again to pick change mode */
50831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (fstat(fd_t, out_st) == -1) {
50931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
51031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
51131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
51231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
51331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* copy selinux context */
51431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (execcon) {
51531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (fsetfilecon(fd_t, con) == -1) {
51631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Failed to set context of the directory %s: %s\n"), tmpdir, strerror(errno));
51731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto err;
51831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
51931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
52031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
521221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris	if ((uid_t)setfsuid(pwd->pw_uid) != 0)
522221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		goto err;
52331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
52431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (rsynccmd(src, tmpdir, &cmdbuf) < 0) {
52531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
52631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
52731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
52831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* ok to not reach this if there is an error */
529221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris	if ((uid_t)setfsuid(0) != pwd->pw_uid)
530221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		goto err;
53131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
53231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
53331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to populate runtime temporary directory\n"));
53431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		cleanup_tmpdir(tmpdir, src, pwd, 0);
53531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
53631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
53731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
53831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	goto good;
53931edb319affb5e5c6298a53ca2de62abedb01630Eric Pariserr:
54031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	free(tmpdir); tmpdir = NULL;
54131edb319affb5e5c6298a53ca2de62abedb01630Eric Parisgood:
54231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	free(cmdbuf); cmdbuf = NULL;
54331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	freecon(con); con = NULL;
54431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (fd_t >= 0) close(fd_t);
54531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (fd_s >= 0) close(fd_s);
54631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	return tmpdir;
54731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris}
54831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
549e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh#define PROC_BASE "/proc"
550e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
551e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walshstatic int
552e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walshkillall (security_context_t execcon)
553e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh{
554e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	DIR *dir;
555e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	security_context_t scon;
556e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	struct dirent *de;
557e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	pid_t *pid_table, pid, self;
558e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	int i;
559e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	int pids, max_pids;
560e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	int running = 0;
561e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	self = getpid();
562e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	if (!(dir = opendir(PROC_BASE))) {
563e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		return -1;
564e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	}
565e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	max_pids = 256;
566e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	pid_table = malloc(max_pids * sizeof (pid_t));
567e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	if (!pid_table) {
568e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		(void)closedir(dir);
569e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		return -1;
570e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	}
571e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	pids = 0;
572e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	context_t con;
573e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	con = context_new(execcon);
574e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	const char *mcs = context_range_get(con);
575e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	printf("mcs=%s\n", mcs);
576e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	while ((de = readdir (dir)) != NULL) {
577e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
578e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			continue;
579e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
580e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		if (pids == max_pids) {
5810a5dc30456509f10fdc062f9caecc5d3d57b4306Eric Paris			pid_t *new_pid_table = realloc(pid_table, 2*pids*sizeof(pid_t));
5820a5dc30456509f10fdc062f9caecc5d3d57b4306Eric Paris			if (!new_pid_table) {
5830a5dc30456509f10fdc062f9caecc5d3d57b4306Eric Paris				free(pid_table);
584e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh				(void)closedir(dir);
585e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh				return -1;
586e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			}
5870a5dc30456509f10fdc062f9caecc5d3d57b4306Eric Paris			pid_table = new_pid_table;
588e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			max_pids *= 2;
589e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		}
590e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		pid_table[pids++] = pid;
591e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	}
592e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
593e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	(void)closedir(dir);
594e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
595e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	for (i = 0; i < pids; i++) {
596e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		pid_t id = pid_table[i];
597e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
598e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		if (getpidcon(id, &scon) == 0) {
599e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
600e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			context_t pidcon = context_new(scon);
601e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			/* Attempt to kill remaining processes */
602e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			if (strcmp(context_range_get(pidcon), mcs) == 0)
603e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh				kill(id, SIGKILL);
604e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
605e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			context_free(pidcon);
606e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			freecon(scon);
607e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		}
608e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		running++;
609e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	}
610e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
611e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	context_free(con);
612e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	free(pid_table);
613e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	return running;
614e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh}
615e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
616d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walshint main(int argc, char **argv) {
617d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	int status = -1;
61831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	security_context_t execcon = NULL;
619d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
620d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	int clflag;		/* holds codes for command line flags */
621e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	int kill_all = 0;
622d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
62331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
62431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *tmpdir_s = NULL;	/* tmpdir spec'd by user in argv[] */
62531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	char *tmpdir_r = NULL;	/* tmpdir created by seunshare */
62631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
627e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh	struct stat st_curhomedir;
62831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	struct stat st_homedir;
62931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	struct stat st_tmpdir_s;
63031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	struct stat st_tmpdir_r;
63131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
632d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	const struct option long_options[] = {
633d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		{"homedir", 1, 0, 'h'},
634d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		{"tmpdir", 1, 0, 't'},
635e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		{"kill", 1, 0, 'k'},
636d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		{"verbose", 1, 0, 'v'},
637da7ae7951c692a60b6137ebaf6f33232a9bd63beEric Paris		{"context", 1, 0, 'Z'},
638149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh		{"capabilities", 1, 0, 'C'},
639d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		{NULL, 0, 0, 0}
640d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	};
641d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
642d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	uid_t uid = getuid();
643a0e2e16878c2aae375920f8fef8efe07bcd6ac3dDan Walsh/*
644d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (!uid) {
645d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		fprintf(stderr, _("Must not be root"));
646d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
647d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
648a0e2e16878c2aae375920f8fef8efe07bcd6ac3dDan Walsh*/
649d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
650e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris#ifdef USE_NLS
651e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris	setlocale(LC_ALL, "");
652e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris	bindtextdomain(PACKAGE, LOCALEDIR);
653e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris	textdomain(PACKAGE);
654e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris#endif
655e134013ab705e6edaf3311d4dc9db7c81e84e775Eric Paris
656d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	struct passwd *pwd=getpwuid(uid);
657d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (!pwd) {
658d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		perror(_("getpwduid failed"));
659d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
660d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
661d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
662d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (verify_shell(pwd->pw_shell) < 0) {
66331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Error: User shell is not valid\n"));
664d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
665d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
666d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
667d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	while (1) {
668149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh		clflag = getopt_long(argc, argv, "Ccvh:t:Z:", long_options, NULL);
669d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		if (clflag == -1)
670d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			break;
671d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
672d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		switch (clflag) {
673d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		case 't':
67431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			tmpdir_s = optarg;
675d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			break;
676e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		case 'k':
677e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			kill_all = 1;
678e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh			break;
679d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		case 'h':
68031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			homedir_s = optarg;
681d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			break;
682d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		case 'v':
68331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			verbose++;
684d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			break;
685149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh		case 'C':
686149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh			cap_set = CAPNG_SELECT_CAPS;
687149afc688a53839e57ca541dfa1f84c946bb6399Dan Walsh			break;
688da7ae7951c692a60b6137ebaf6f33232a9bd63beEric Paris		case 'Z':
68931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			execcon = optarg;
690da7ae7951c692a60b6137ebaf6f33232a9bd63beEric Paris			break;
691d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		default:
692d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			fprintf(stderr, "%s\n", USAGE_STRING);
693d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			return -1;
694d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		}
695d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
696d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
697d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (! homedir_s && ! tmpdir_s) {
69831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Error: tmpdir and/or homedir required\n %s\n"), USAGE_STRING);
699d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
700d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
701d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
702da7ae7951c692a60b6137ebaf6f33232a9bd63beEric Paris	if (argc - optind < 1) {
70331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Error: executable required\n %s\n"), USAGE_STRING);
70431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		return -1;
70531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	}
70631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
70731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (execcon && is_selinux_enabled() != 1) {
70831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Error: execution context specified, but SELinux is not enabled\n"));
709d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
710d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
711d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
712d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (set_signal_handles())
713d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
714d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
71531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* set fsuid to ruid */
71631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* Changing fsuid is usually required when user-specified directory is
71731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	 * on an NFS mount.  It's also desired to avoid leaking info about
71831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	 * existence of the files not accessible to the user. */
719e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh	if (((uid_t)setfsuid(uid) != 0)   && (errno != 0)) {
720e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		fprintf(stderr, _("Error: unable to setfsuid %m\n"));
721e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh
722221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		return -1;
723e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh	}
72431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
72531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* verify homedir and tmpdir */
72631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (homedir_s && (
72731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		verify_directory(homedir_s, NULL, &st_homedir) < 0 ||
72831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		check_owner_uid(uid, homedir_s, &st_homedir))) return -1;
72931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (tmpdir_s && (
73031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		verify_directory(tmpdir_s, NULL, &st_tmpdir_s) < 0 ||
73131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))) return -1;
732221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris	if ((uid_t)setfsuid(0) != uid) return -1;
73331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
73431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* create runtime tmpdir */
73531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (tmpdir_s && (tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s,
73631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris						  &st_tmpdir_r, pwd, execcon)) == NULL) {
73731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to create runtime temporary directory\n"));
738d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		return -1;
739d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
740d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
74131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* spawn child process */
742216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh	child = fork();
743d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	if (child == -1) {
744d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		perror(_("Unable to fork"));
74531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		goto err;
746d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
747d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
74831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (child == 0) {
74931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		char *display = NULL;
7505c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh		char *LANG = NULL;
7516ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh		char *RUNTIME_DIR = NULL;
75231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		int rc = -1;
753e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		char *resolved_path = NULL;
754d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
75531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (unshare(CLONE_NEWNS) < 0) {
75631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			perror(_("Failed to unshare"));
75731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto childerr;
75870c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh		}
75970c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh
76070c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh		/* Remount / as SLAVE so that nothing mounted in the namespace
76170c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh		   shows up in the parent */
76270c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh		if (mount("none", "/", NULL, MS_SLAVE | MS_REC , NULL) < 0) {
76370c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh			perror(_("Failed to make / a SLAVE mountpoint\n"));
76470c582f4e0554bb41f6bab6336a3996f9499bfebDan Walsh			goto childerr;
765d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		}
766da7ae7951c692a60b6137ebaf6f33232a9bd63beEric Paris
76731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		/* assume fsuid==ruid after this point */
768221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		if ((uid_t)setfsuid(uid) != 0) goto childerr;
76931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
770e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		resolved_path = realpath(pwd->pw_dir,NULL);
771e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		if (! resolved_path) goto childerr;
772e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh
773e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		if (verify_directory(resolved_path, NULL, &st_curhomedir) < 0)
774e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh			goto childerr;
775e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		if (check_owner_uid(uid, resolved_path, &st_curhomedir) < 0)
776e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh			goto childerr;
777e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh
77831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		/* mount homedir and tmpdir, in this order */
779e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		if (homedir_s && seunshare_mount(homedir_s, resolved_path,
78031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			&st_homedir) != 0) goto childerr;
78131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (tmpdir_s &&	seunshare_mount(tmpdir_r, "/tmp",
78231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			&st_tmpdir_r) != 0) goto childerr;
78331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
78431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (drop_privs(uid) != 0) goto childerr;
78531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
78631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		/* construct a new environment */
78731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if ((display = getenv("DISPLAY")) != NULL) {
78831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			if ((display = strdup(display)) == NULL) {
78931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				perror(_("Out of memory"));
79031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris				goto childerr;
791da7ae7951c692a60b6137ebaf6f33232a9bd63beEric Paris			}
792d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		}
793221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris
7945c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh		/* construct a new environment */
7955c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh		if ((LANG = getenv("LANG")) != NULL) {
7965c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh			if ((LANG = strdup(LANG)) == NULL) {
7975c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh				perror(_("Out of memory"));
7985c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh				goto childerr;
7995c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh			}
8005c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh		}
801221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris
8026ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh		if ((RUNTIME_DIR = getenv("XDG_RUNTIME_DIR")) != NULL) {
8036ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh			if ((RUNTIME_DIR = strdup(RUNTIME_DIR)) == NULL) {
8046ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh				perror(_("Out of memory"));
8056ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh				goto childerr;
8066ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh			}
8076ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh		}
8086ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh
80931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if ((rc = clearenv()) != 0) {
81031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			perror(_("Failed to clear environment"));
81131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto childerr;
81231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
81331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (display)
814d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			rc |= setenv("DISPLAY", display, 1);
815221e6d46651a921c843cbe5ac9b81f324a81e593Eric Paris		if (LANG)
8165c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh			rc |= setenv("LANG", LANG, 1);
8176ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh		if (RUNTIME_DIR)
8186ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh			rc |= setenv("XDG_RUNTIME_DIR", RUNTIME_DIR, 1);
819d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		rc |= setenv("HOME", pwd->pw_dir, 1);
820d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		rc |= setenv("SHELL", pwd->pw_shell, 1);
821d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		rc |= setenv("USER", pwd->pw_name, 1);
822d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		rc |= setenv("LOGNAME", pwd->pw_name, 1);
823d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		rc |= setenv("PATH", DEFAULT_PATH, 1);
82431edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		if (rc != 0) {
82531edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			fprintf(stderr, _("Failed to construct environment\n"));
82631edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto childerr;
82731edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		}
82831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
829d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		if (chdir(pwd->pw_dir)) {
830d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh			perror(_("Failed to change dir to homedir"));
83131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris			goto childerr;
832d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		}
833d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		setsid();
83474d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski
83574d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski		/* selinux context */
83674d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski		if (execcon) {
83774d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski			/* try dyntransition, since no_new_privs can interfere
83874d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski			 * with setexeccon */
83974d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski			if (setcon(execcon) != 0) {
84074d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski				/* failed; fall back to setexeccon */
84174d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski				if (setexeccon(execcon) != 0) {
84274d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski					fprintf(stderr, _("Could not set exec context to %s. %s\n"), execcon, strerror(errno));
84374d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski					goto childerr;
84474d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski				}
84574d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski			}
84674d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski		}
84774d27a97338649951108727a0a142b260ce9a28bAndy Lutomirski
848d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		execv(argv[optind], argv + optind);
84931edb319affb5e5c6298a53ca2de62abedb01630Eric Paris		fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno));
85031edb319affb5e5c6298a53ca2de62abedb01630Eric Parischilderr:
851e4488ecd87760e4894354e15935e25ea82baf23cDan Walsh		free(resolved_path);
852d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		free(display);
8535c2a0d143de7920b9edf070518d22f4e7dce5481Dan Walsh		free(LANG);
8546ee0299ab76ffc62095bda0ae84c49150a83e255Dan Walsh		free(RUNTIME_DIR);
855d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh		exit(-1);
856d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	}
857d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh
85831edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	drop_caps();
859582fd00c7b493010f93696f0bfcc55412ab40c07Steve Lawrence
86031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	/* parent waits for child exit to do the cleanup */
86131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	waitpid(child, &status, 0);
86231edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	status_to_retval(status, status);
86331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
864216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh	/* Make sure all child processes exit */
865216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh	kill(-child,SIGTERM);
866216f456401151d02b39bd3c7f47581a4b8632ab8Dan Walsh
867e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh	if (execcon && kill_all)
868e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh		killall(execcon);
869e8575bf497806eb5aea0ff0e207e35a5a1534064Dan Walsh
87031edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	if (tmpdir_r) cleanup_tmpdir(tmpdir_r, tmpdir_s, pwd, 1);
87131edb319affb5e5c6298a53ca2de62abedb01630Eric Paris
87231edb319affb5e5c6298a53ca2de62abedb01630Eric Pariserr:
87331edb319affb5e5c6298a53ca2de62abedb01630Eric Paris	free(tmpdir_r);
874d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh	return status;
875d6848ea77d9e0fed546a8286f8c62fe32be58aceDaniel J Walsh}
876