Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 374897
Collapse All | Expand All

(-)policycoreutils-2.0.85.orig/sandbox/sandbox (-34 / +57 lines)
Lines 19-34 Link Here
19
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#
20
#
21
21
22
import os, sys, socket, random, fcntl, shutil, re, subprocess
22
import os, stat, sys, socket, random, fcntl, shutil, re, subprocess
23
import selinux
23
import selinux
24
import signal
24
import signal
25
from tempfile import mkdtemp
25
from tempfile import mkdtemp
26
import pwd
26
import pwd
27
import commands
28
import gettext
27
29
28
PROGNAME = "policycoreutils"
30
PROGNAME = "policycoreutils"
29
HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
31
SEUNSHARE = "/usr/sbin/seunshare"
32
SANDBOXSH = "/usr/share/sesandbox/sesandboxX.sh"
30
33
31
import gettext
32
gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
34
gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
33
gettext.textdomain(PROGNAME)
35
gettext.textdomain(PROGNAME)
34
36
Lines 41-46 Link Here
41
       import __builtin__
43
       import __builtin__
42
       __builtin__.__dict__['_'] = unicode
44
       __builtin__.__dict__['_'] = unicode
43
45
46
DEFAULT_WINDOWSIZE = "1000x700"
44
DEFAULT_TYPE = "sandbox_t"
47
DEFAULT_TYPE = "sandbox_t"
45
DEFAULT_X_TYPE = "sandbox_x_t"
48
DEFAULT_X_TYPE = "sandbox_x_t"
46
SAVE_FILES = {}
49
SAVE_FILES = {}
Lines 63-77 Link Here
63
    sys.stderr.flush()
66
    sys.stderr.flush()
64
    sys.exit(1)
67
    sys.exit(1)
65
68
66
def copyfile(file, dir, dest):
69
def copyfile(file, srcdir, dest):
67
       import re
70
       import re
68
       if file.startswith(dir):
71
       if file.startswith(srcdir):
69
              dname = os.path.dirname(file)
72
              dname = os.path.dirname(file)
70
              bname = os.path.basename(file)
73
              bname = os.path.basename(file)
71
              if dname == dir:
74
              if dname == srcdir:
72
                     dest = dest + "/" + bname
75
                     dest = dest + "/" + bname
73
              else:
76
              else:
74
                     newdir = re.sub(dir, dest, dname)
77
                     newdir = re.sub(srcdir, dest, dname)
75
                     if not os.path.exists(newdir):
78
                     if not os.path.exists(newdir):
76
                            os.makedirs(newdir)
79
                            os.makedirs(newdir)
77
                     dest = newdir + "/" + bname
80
                     dest = newdir + "/" + bname
Lines 81-89 Link Here
81
                            shutil.copytree(file, dest)
84
                            shutil.copytree(file, dest)
82
                     else:
85
                     else:
83
                            shutil.copy2(file, dest)
86
                            shutil.copy2(file, dest)
87
84
              except shutil.Error, elist:
88
              except shutil.Error, elist:
85
                     for e in elist:
89
                     for e in elist.message:
86
                            sys.stderr.write(e[1])
90
                            sys.stderr.write(e[2])
87
                     
91
                     
88
              SAVE_FILES[file] = (dest, os.path.getmtime(dest))
92
              SAVE_FILES[file] = (dest, os.path.getmtime(dest))
89
93
Lines 161-167 Link Here
161
                  if not self.__options.homedir or not self.__options.tmpdir:
165
                  if not self.__options.homedir or not self.__options.tmpdir:
162
                         self.usage(_("Homedir and tempdir required for level mounts"))
166
                         self.usage(_("Homedir and tempdir required for level mounts"))
163
167
164
           if not os.path.exists("/usr/sbin/seunshare"):
168
           if not os.path.exists(SEUNSHARE):
165
                  raise ValueError(_("""
169
                  raise ValueError(_("""
166
/usr/sbin/seunshare is required for the action you want to perform.  
170
/usr/sbin/seunshare is required for the action you want to perform.  
167
"""))
171
"""))
Lines 194-199 Link Here
194
                         self.__include(option, opt, i[:-1], parser)
198
                         self.__include(option, opt, i[:-1], parser)
195
                  except IOError, e:
199
                  except IOError, e:
196
                         sys.stderr.write(str(e))
200
                         sys.stderr.write(str(e))
201
                  except TypeError, e:
202
                         sys.stderr.write(str(e))
197
           fd.close()
203
           fd.close()
198
204
199
    def __copyfiles(self):
205
    def __copyfiles(self):
Lines 212-218 Link Here
212
/etc/gdm/Xsession
218
/etc/gdm/Xsession
213
""")
219
""")
214
           else:
220
           else:
215
                  command = " ".join(self.__paths)
221
                  command = self.__paths[0] + " "
222
                  for p in self.__paths[1:]:
223
                         command += "'%s' " % p
216
                  fd.write("""#! /bin/sh
224
                  fd.write("""#! /bin/sh
217
#TITLE: %s
225
#TITLE: %s
218
/usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap
226
/usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap
Lines 230-238 Link Here
230
    def __parse_options(self):
238
    def __parse_options(self):
231
        from optparse import OptionParser
239
        from optparse import OptionParser
232
        usage = _("""
240
        usage = _("""
233
sesandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] command
241
sesandbox [-h] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [ -w windowsize ] [[-i file ] ...] [ -t type ] command
234
242
235
sesandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] -S
243
sesandbox [-h] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [ -w windowsize ] [[-i file ] ...] [ -t type ] -S
236
""")
244
""")
237
        
245
        
238
        parser = OptionParser(version=self.VERSION, usage=usage)
246
        parser = OptionParser(version=self.VERSION, usage=usage)
Lines 268-273 Link Here
268
                          action="callback", callback=self.__validdir,
276
                          action="callback", callback=self.__validdir,
269
                          help=_("alternate /tmp directory to use for mounting"))
277
                          help=_("alternate /tmp directory to use for mounting"))
270
278
279
        parser.add_option("-w", "--windowsize", dest="windowsize",
280
                          type="string", default=DEFAULT_WINDOWSIZE,
281
                          help="size of the sandbox window")
282
271
        parser.add_option("-W", "--windowmanager", dest="wm",  
283
        parser.add_option("-W", "--windowmanager", dest="wm",  
272
                          type="string",
284
                          type="string",
273
                          default="/usr/bin/matchbox-window-manager -use_titlebar no",
285
                          default="/usr/bin/matchbox-window-manager -use_titlebar no",
Lines 276-287 Link Here
276
        parser.add_option("-l", "--level", dest="level", 
288
        parser.add_option("-l", "--level", dest="level", 
277
                          help=_("MCS/MLS level for the sesandbox"))
289
                          help=_("MCS/MLS level for the sesandbox"))
278
290
291
        parser.add_option("-C", "--capabilities",
292
                          action="store_true", dest="usecaps", default=False,
293
                          help="Allow apps requiring capabilities to run within the sandbox.")
294
295
279
        self.__parser=parser
296
        self.__parser=parser
280
297
281
        self.__options, cmds = parser.parse_args()
298
        self.__options, cmds = parser.parse_args()
282
299
283
        if self.__options.X_ind:
300
        if self.__options.X_ind:
284
               self.setype = DEFAULT_X_TYPE
301
               self.setype = DEFAULT_X_TYPE
302
               self.dpi=commands.getoutput("xrdb -query | grep dpi | /bin/cut -f 2")
285
        
303
        
286
        if self.__options.setype:
304
        if self.__options.setype:
287
               self.setype = self.__options.setype
305
               self.setype = self.__options.setype
Lines 300-305 Link Here
300
               self.__homedir = self.__options.homedir
318
               self.__homedir = self.__options.homedir
301
               self.__tmpdir = self.__options.tmpdir
319
               self.__tmpdir = self.__options.tmpdir
302
        else:
320
        else:
321
               if self.__options.level:
322
                      self.__homedir = self.__options.homedir
323
                      self.__tmpdir = self.__options.tmpdir
324
303
               if len(cmds) == 0:
325
               if len(cmds) == 0:
304
                      self.usage(_("Command required"))
326
                      self.usage(_("Command required"))
305
               cmds[0] = fullpath(cmds[0])
327
               cmds[0] = fullpath(cmds[0])
Lines 329-372 Link Here
329
    def __setup_dir(self):
351
    def __setup_dir(self):
330
           if self.__options.level or self.__options.session:
352
           if self.__options.level or self.__options.session:
331
                  return
353
                  return
332
           sandboxdir = HOMEDIR + "/.sesandbox"
333
           if not os.path.exists(sandboxdir):
334
                  os.mkdir(sandboxdir)
335
354
336
           if self.__options.homedir:
355
           if self.__options.homedir:
337
                  selinux.chcon(self.__options.homedir, self.__filecon, recursive=True)
356
                  selinux.chcon(self.__options.homedir, self.__filecon, recursive=True)
338
                  self.__homedir = self.__options.homedir
357
                  self.__homedir = self.__options.homedir
339
           else:
358
           else:
340
                  selinux.setfscreatecon(self.__filecon)
359
                  selinux.setfscreatecon(self.__filecon)
341
                  self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sesandbox")
360
                  self.__homedir = mkdtemp(dir="/tmp", prefix=".sesandbox_home_")
342
361
343
           if self.__options.tmpdir:
362
           if self.__options.tmpdir:
344
                  selinux.chcon(self.__options.tmpdir, self.__filecon, recursive=True)
363
                  selinux.chcon(self.__options.tmpdir, self.__filecon, recursive=True)
345
                  self.__tmpdir = self.__options.tmpdir
364
                  self.__tmpdir = self.__options.tmpdir
346
           else:
365
           else:
347
                  selinux.setfscreatecon(self.__filecon)
366
                  selinux.setfscreatecon(self.__filecon)
348
                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sesandbox")
367
                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sesandbox_tmp_")
349
           selinux.setfscreatecon(None)
368
           selinux.setfscreatecon(None)
350
           self.__copyfiles()
369
           self.__copyfiles()
351
370
352
    def __execute(self):
371
    def __execute(self):
353
           try:
372
           try:
354
                  if self.__options.X_ind:
373
                  cmds = [ SEUNSHARE, "-Z", self.__execcon ]
355
                         xmodmapfile = self.__homedir + "/.xmodmap"
374
                  if self.__options.usecaps:
356
                         xd = open(xmodmapfile,"w")
375
                         cmds.append('-C')
357
                         subprocess.Popen(["/usr/bin/xmodmap","-pke"],stdout=xd).wait()
376
                  if not self.__options.level:
358
                         xd.close()
377
                         cmds.append('-k')
359
360
                         self.__setup_sandboxrc(self.__options.wm)
361
                         
362
                         cmds = [ '/usr/sbin/seunshare', "-t", self.__tmpdir, "-h", self.__homedir, "--", self.__execcon, "/usr/share/sesandbox/sesandboxX.sh" ]
363
                         rc = subprocess.Popen(cmds).wait()
364
                         return rc
365
366
                  if self.__mount:
378
                  if self.__mount:
367
                         cmds =  [ '/usr/sbin/seunshare', "-t", self.__tmpdir, "-h", self.__homedir, "--", self.__execcon ] + self.__paths
379
                         cmds += [ "-t", self.__tmpdir, "-h", self.__homedir ]
368
                         rc = subprocess.Popen(cmds).wait()
380
369
                         return rc
381
                         if self.__options.X_ind:
382
                                xmodmapfile = self.__homedir + "/.xmodmap"
383
                                xd = open(xmodmapfile,"w")
384
                                subprocess.Popen(["/usr/bin/xmodmap","-pke"],stdout=xd).wait()
385
                                xd.close()
386
387
                                self.__setup_sandboxrc(self.__options.wm)
388
389
                                cmds += [ "--", SANDBOXSH, self.__options.windowsize, self.dpi ]
390
                         else:
391
                                cmds += [ "--" ] + self.__paths
392
                         return subprocess.Popen(cmds).wait()
370
393
371
                  selinux.setexeccon(self.__execcon)
394
                  selinux.setexeccon(self.__execcon)
372
                  rc = subprocess.Popen(self.__cmds).wait()
395
                  rc = subprocess.Popen(self.__cmds).wait()
Lines 404-410 Link Here
404
           sandbox = Sandbox()
427
           sandbox = Sandbox()
405
           rc = sandbox.main()
428
           rc = sandbox.main()
406
    except OSError, error:
429
    except OSError, error:
407
           error_exit(error.args[1])
430
           error_exit(error)
408
    except ValueError, error:
431
    except ValueError, error:
409
           error_exit(error.args[0])
432
           error_exit(error.args[0])
410
    except KeyError, error:
433
    except KeyError, error:
(-)policycoreutils-2.0.85.orig/sandbox/seunshare.c (-106 / +594 lines)
Lines 1-10 Link Here
1
/*
2
 * Authors: Dan Walsh <dwalsh@redhat.com>
3
 * Authors: Thomas Liu <tliu@fedoraproject.org>
4
 *
5
 * Does not include cgroups support (as opposed to seunshare in fedora)
6
 */
7
8
#define _GNU_SOURCE
1
#include <signal.h>
9
#include <signal.h>
2
#include <sys/types.h>
10
#include <sys/types.h>
3
#include <sys/wait.h>
11
#include <sys/wait.h>
4
#include <syslog.h>
12
#include <syslog.h>
5
#include <sys/mount.h>
13
#include <sys/mount.h>
6
#include <pwd.h>
14
#include <pwd.h>
7
#define _GNU_SOURCE
8
#include <sched.h>
15
#include <sched.h>
9
#include <string.h>
16
#include <string.h>
10
#include <stdio.h>
17
#include <stdio.h>
Lines 15-20 Link Here
15
#include <limits.h>
22
#include <limits.h>
16
#include <stdlib.h>
23
#include <stdlib.h>
17
#include <errno.h>
24
#include <errno.h>
25
#include <regex.h>
26
#include <sys/fsuid.h>
27
#include <fcntl.h>
28
#include <dirent.h>
18
29
19
#include <selinux/selinux.h>
30
#include <selinux/selinux.h>
20
#include <selinux/context.h>	/* for context-mangling functions */
31
#include <selinux/context.h>	/* for context-mangling functions */
Lines 22-27 Link Here
22
#include <sys/types.h>
33
#include <sys/types.h>
23
#include <sys/stat.h>
34
#include <sys/stat.h>
24
#include <unistd.h>
35
#include <unistd.h>
36
#include <glob.h>
37
#include <regex.h>
25
38
26
#ifdef USE_NLS
39
#ifdef USE_NLS
27
#include <locale.h>		/* for setlocale() */
40
#include <locale.h>		/* for setlocale() */
Lines 39-64 Link Here
39
#define MS_PRIVATE 1<<18
52
#define MS_PRIVATE 1<<18
40
#endif
53
#endif
41
54
55
static int verbose = 0;
56
static int child = 0;
57
58
static capng_select_t cap_set = CAPNG_SELECT_BOTH;
59
42
/**
60
/**
43
 * This function will drop all capabilities 
61
 * This function will drop all capabilities
44
 * Returns zero on success, non-zero otherwise
45
 */
62
 */
46
static int drop_capabilities(uid_t uid)
63
static int drop_caps()
47
{
64
{
48
	capng_clear(CAPNG_SELECT_BOTH);
65
	if (capng_have_capabilities(cap_set) == CAPNG_NONE)
49
66
		return 0;
50
	if (capng_lock() < 0) 
67
	capng_clear(cap_set);
68
	if (capng_lock() == -1 || capng_apply(cap_set) == -1) {
69
		fprintf(stderr, _("Failed to drop all capabilities\n"));
51
		return -1;
70
		return -1;
52
	/* Change uid */
71
	}
53
	if (setresuid(uid, uid, uid)) {
72
	return 0;
54
		fprintf(stderr, _("Error changing uid, aborting.\n"));
73
}
74
75
/**
76
 * This function will drop all privileges.
77
 */
78
static int drop_privs(uid_t uid) {
79
	if (drop_caps() == -1 || setresuid(uid, uid, uid) == -1) {
80
		fprintf(stderr, _("Failed to drop privileges\n"));
55
		return -1;
81
		return -1;
56
	}
82
	}
57
	return capng_apply(CAPNG_SELECT_BOTH);
83
	return 0;
58
}
84
}
59
85
60
#define DEFAULT_PATH "/usr/bin:/bin"
86
/**
61
static	int verbose = 0;
87
 * If the user sends a siginto to seunshare, kill the child's session
88
 */
89
void handler(int sig) {
90
	if (child > 0)
91
		kill(-child, sig);
92
}
62
93
63
/**
94
/**
64
 * Take care of any signal setup
95
 * Take care of any signal setup
Lines 81-104 Link Here
81
		return -1;
112
		return -1;
82
	}
113
	}
83
114
115
	if (signal(SIGINT, handler) == SIG_ERR) {
116
		perror("Unable to set SIGHUP handler");
117
		return -1;
118
	}
119
84
	return 0;
120
	return 0;
85
}
121
}
86
122
123
#define status_to_retval(status,retval) do { \
124
	if ((status) == -1) \
125
		retval = -1; \
126
	else if (WIFEXITED((status))) \
127
		retval = WEXITSTATUS((status)); \
128
	else if (WIFSIGNALED((status))) \
129
		retval = 128 + WTERMSIG((status)); \
130
	else \
131
		retval = -1; \
132
	} while(0)
133
134
135
/**
136
 * Spawn external command using system() with dropped privileges.
137
 * TODO: avoid system() and use exec*() instead.
138
 */
139
static int spawn_command(const char *cmd, uid_t uid) {
140
	int child;
141
	int status = -1;
142
143
	if (verbose > 1)
144
		printf("spawn_command: %s\n", cmd);
145
	
146
	child = fork();
147
	if (child == -1) {
148
		perror(_("Unable to fork"));
149
		return status;
150
	}
151
152
	if (child == 0) {
153
		if (drop_privs(uid) != 0)
154
			exit(-1);
155
		
156
		status = system(cmd);
157
		status_to_retval(status, status);
158
		exit(status);
159
	}
160
161
	waitpid(child, &status, 0);
162
	status_to_retval(status, status);
163
	return status;
164
}
165
87
/**
166
/**
88
 * This function makes sure the mounted directory is owned by the user executing
167
 * Check file/directory ownership, struct stat * must be passed to the functions.
89
 * seunshare.
90
 * If so, it returns 0. If it can not figure this out or they are different, it returns -1.
91
 */
168
 */
92
static int verify_mount(const char *mntdir, struct passwd *pwd) {
169
static int check_owner_uid(uid_t uid, const char *file, struct stat *st) {
170
	if (S_ISLNK(st->st_mode)) {
171
		fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
172
		return -1;
173
	}
174
	if (st->st_uid != uid) {
175
		fprintf(stderr, _("Error: %s not owned by UID %d\n"), file, uid);
176
		return -1;
177
	}
178
	return 0;
179
}
180
181
static int check_owner_gid(gid_t gid, const char *file, struct stat *st) {
182
	if (S_ISLNK(st->st_mode)) {
183
		fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
184
		return -1;
185
	}
186
	if (st->st_gid != gid) {
187
		fprintf(stderr, _("Error: %s not owned by GID %d\n"), file, gid);
188
		return -1;
189
	}
190
	return 0;
191
}
192
193
#define equal_stats(one,two) \
194
	((one)->st_dev == (two)->st_dev && (one)->st_ino == (two)->st_ino && \
195
	 (one)->st_uid == (two)->st_uid && (one)->st_gid == (two)->st_gid && \
196
	 (one)->st_mode == (two)->st_mode)
197
198
/**
199
 * Sanity check specified directory. Store stat info for future comparison, or compare
200
 * with previously saved info to detect replaced directories.
201
 * Note: this function does not perform owner checks.
202
 */
203
static int verify_directory(const char *dir, struct stat *st_in, struct stat *st_out) {
93
	struct stat sb;
204
	struct stat sb;
94
	if (stat(mntdir, &sb) == -1) {
205
95
		fprintf(stderr, _("Invalid mount point %s: %s\n"), mntdir, strerror(errno));
206
	if (st_out == NULL) st_out = &sb;
207
208
	if (lstat(dir, st_out) == -1) {
209
		fprintf(stderr, _("Failed to stat %s: %s\n"), dir, strerror(errno));
96
		return -1;
210
		return -1;
97
	}
211
	}
98
	if (sb.st_uid != pwd->pw_uid) {
212
	if (! S_ISDIR(st_out->st_mode)) {
99
		errno = EPERM;
213
		fprintf(stderr, _("Error: %s is not a directory: %s\n"), dir, strerror(errno));
100
		syslog(LOG_AUTHPRIV | LOG_ALERT, "%s attempted to mount an invalid directory, %s", pwd->pw_name, mntdir);
214
		return -1;
101
		perror(_("Invalid mount point, reporting to administrator"));
215
	}
216
	if (st_in && !equal_stats(st_in, st_out)) {
217
		fprintf(stderr, _("Error: %s was replaced by a different directory\n"), dir);
102
		return -1;
218
		return -1;
103
	}
219
	}
104
	return 0;
220
	return 0;
Lines 123-129 Link Here
123
239
124
		/* check the shell skipping newline char */
240
		/* check the shell skipping newline char */
125
		if (!strcmp(shell_name, buf)) {
241
		if (!strcmp(shell_name, buf)) {
126
			rc = 1;
242
			rc = 0;
127
			break;
243
			break;
128
		}
244
		}
129
	}
245
	}
Lines 131-175 Link Here
131
	return rc;
247
	return rc;
132
}
248
}
133
249
134
static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) {
250
/*
251
 * Mount directory and check that we mounted the right directory.
252
 */
253
static int seunshare_mount(const char *src, const char *dst, struct stat *src_st) {
254
	int flags = MS_REC;
255
	int is_tmp = 0;
256
135
	if (verbose)
257
	if (verbose)
136
		printf("Mount %s on %s\n", src, dst);
258
		printf(_("Mounting %s on %s\n"), src, dst);
137
	if (mount(dst, dst,  NULL, MS_BIND | MS_REC, NULL) < 0) {
259
	
260
	if (strcmp("/tmp", dst) == 0) {
261
		flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC;
262
		is_tmp = 1;
263
	}
264
265
	/* mount directory */
266
	if (mount(dst, dst, NULL, MS_BIND | flags, NULL) < 0) {
138
		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), dst, dst, strerror(errno));
267
		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), dst, dst, strerror(errno));
139
		return -1;
268
		return -1;
140
	}
269
	}
141
270
142
	if (mount(dst, dst, NULL, MS_PRIVATE | MS_REC, NULL) < 0) {
271
	if (mount(dst, dst, NULL, MS_PRIVATE | flags, NULL) < 0) {
143
		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
272
		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
144
		return -1;
273
		return -1;
145
	}
274
	}
146
275
147
	if (mount(src, dst, NULL, MS_BIND | MS_REC, NULL) < 0) {
276
	if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
148
		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
277
		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
149
		return -1;
278
		return -1;
150
	}
279
	}
151
280
152
	if (verify_mount(dst, pwd) < 0) 
281
	/* verify whether we mounted what we expected to mount */
282
	if (verify_directory(dst, src_st, NULL) < 0)
153
		return -1;
283
		return -1;
284
285
	/* bind mount /tmp on /var/tmp too */
286
	if (is_tmp) {
287
		if (verbose)
288
			printf(_("Mounting /tmp on /var/tmp\n"));
289
290
		if (mount("/var/tmp", "/var/tmp", NULL, MS_BIND | flags, NULL) < 0) {
291
			fprintf(stderr, _("Failed to mount /var/tmp on /var/tmp: %s\n"), strerror(errno));
292
			return -1;
293
		}
294
		if (mount("/var/tmp", "/var/tmp", NULL, MS_PRIVATE | flags, NULL) < 0) {
295
			fprintf(stderr, _("Failed to make /var/tmp private: %s\n"), strerror(errno));
296
			return -1;
297
		}
298
		if (mount("/tmp", "/var/tmp", NULL, MS_BIND | flags, NULL) < 0) {
299
			fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno));
300
			return -1;
301
		}
302
	}
303
304
	return 0;
154
}
305
}
155
306
156
#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] ")
307
/*
308
 * If path is empty or ends with "/." or "/.." return -1 else return 0;
309
 */
310
static int bad_path(const char *path) {
311
	const char *ptr;
312
	ptr = path;
313
	while (*ptr) ptr++;
314
	if (ptr == path) return -1; // ptr null
315
	ptr--;
316
	if (ptr != path && *ptr == '.') {
317
		ptr--;
318
		if (*ptr == '/') return -1; // path ends in /.
319
		if (*ptr == '.') {
320
			if (ptr != path) {
321
				ptr--;
322
				if (*ptr == '/') return -1; // path ends in /..
323
			}
324
		}
325
	}
326
	return 0;
327
}
328
329
static int rsynccmd(const char *src, const char *dst, char **cmdbuf) {
330
	char *buf = NULL;
331
	char *newbuf = NULL;
332
	glob_t fglob;
333
	fglob.gl_offs = 0;
334
	int flags = GLOB_PERIOD;
335
	unsigned int i = 0;
336
	int rc = -1;
337
338
	/* match glob for all files in src dir */
339
	if (asprintf(&buf, "%s/*", src) == -1) {
340
		fprintf(stderr, "Out of memory\n");
341
		return -1;
342
	}
343
344
	if (glob(buf, flags, NULL, &fglob) != 0) {
345
		free(buf);
346
		buf = NULL;
347
		return -1;
348
	}
349
350
	free(buf);
351
	buf = NULL;
352
353
	for (i=0; i < fglob.gl_pathc; i++) {
354
		const char * path = fglob.gl_pathv[i];
355
356
		if (bad_path(path))
357
			continue;
358
359
		if (!buf) {
360
			if (asprintf(&newbuf, "\'%s\'", path) == -1) {
361
				fprintf(stderr, "Out of memory\n");
362
				goto err;
363
			}
364
		} else {
365
			if (asprintf(&newbuf, "%s  \'%s\'", buf, path) == -1) {
366
				fprintf(stderr, "Out of memory\n");
367
				goto err;
368
			}
369
		}
370
371
		free(buf); buf = newbuf;
372
		newbuf = NULL;
373
	}
374
375
	if (buf) {
376
		if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) {
377
			fprintf(stderr, "Out of memory\n");
378
			goto err;
379
		}
380
		*cmdbuf = newbuf;
381
	} else {
382
		*cmdbuf = NULL;
383
	}
384
	rc = 0;
385
386
err:
387
	free(buf);
388
	buf = NULL;
389
	globfree(&fglob);
390
	return rc;
391
}
392
393
/**
394
 * Clean up runtime temporary directory. Returns 0 if no problem was detected,
395
 * >0 if some error was detected, but errors here are treated as non-fatal and
396
 * left to tmpwatch to finish incomplete cleanup.
397
 */
398
static int cleanup_tmpdir(const char *tmpdir, const char *src, struct passwd *pwd, int copy_content) {
399
	char *cmdbuf = NULL;
400
	int rc = 0;
401
402
	/* rsync files back */
403
	if (copy_content) {
404
		if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) {
405
			fprintf(stderr, _("Out of memory\n"));
406
			cmdbuf = NULL;
407
			rc++;
408
		}
409
		if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
410
			fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n"));
411
			rc++;
412
		}
413
		free(cmdbuf);
414
		cmdbuf = NULL;
415
	}
416
417
	/* remove files from the runtime temporary directory */
418
	if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
419
		fprintf(stderr, _("Out of memory\n"));
420
		cmdbuf = NULL;
421
		rc++;
422
	}
423
	/* this may fail if there's root-owned file left in the runtime tmpdir */
424
	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0)
425
		rc++;
426
	free(cmdbuf);
427
	cmdbuf = NULL;
428
429
	/* remove runtime temporary directory */
430
	setfsuid(0);
431
	if (rmdir(tmpdir) == -1)
432
		fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
433
	setfsuid(pwd->pw_uid);
434
435
	return 0;
436
}
437
438
/**
439
 * seunshare will create a tmpdir in /tmp, with root ownership. The parent process
440
 * waits for its child to exit to attempt to remove the directory. If it fails to remove
441
 * the directory, we will need to rely on tmpreaper/tmpwatch to clean it up.
442
 */
443
static char *create_tmpdir(const char *src, struct stat *src_st, struct stat *out_st, struct passwd *pwd, security_context_t execcon) {
444
	char *tmpdir = NULL;
445
	char *cmdbuf = NULL;
446
	int fd_t = -1, fd_s = -1;
447
	struct stat tmp_st;
448
	security_context_t con = NULL;
449
450
	/* get selinux context */
451
	if (execcon) {
452
		setfsuid(pwd->pw_uid);
453
		if ((fd_s = open(src, O_RDONLY)) < 0) {
454
			fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno));
455
			goto err;
456
		}
457
		if (fstat(fd_s, &tmp_st) == -1) {
458
			fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno));
459
			goto err;
460
		}
461
		if (!equal_stats(src_st, &tmp_st)) {
462
			fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src);
463
			goto err;
464
		}
465
466
		/* ok to not reach this if there is an error */
467
		setfsuid(0);
468
	}
469
470
	if (asprintf(&tmpdir, "/tmp/.sandbox-%s-XXXXXX", pwd->pw_name) == -1) {
471
		fprintf(stderr, _("Out of memory\n"));
472
		tmpdir = NULL;
473
		goto err;
474
	}
475
	if (mkdtemp(tmpdir) == NULL) {
476
		fprintf(stderr, _("Failed to create temporary directory: %s\n"), strerror(errno));
477
		goto err;
478
	}
479
480
	/* temporary directory must be owned by root:user */
481
	if (verify_directory(tmpdir, NULL, out_st) < 0) {
482
		goto err;
483
	}
484
	if (check_owner_uid(0, tmpdir, out_st) < 0) goto err;
485
	if (check_owner_gid(getgid(), tmpdir, out_st) < 0) goto err;
486
487
	/* change permission of the temporary directory */
488
	if ((fd_t = open(tmpdir, O_RDONLY)) < 0) {
489
		fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno));
490
		goto err;
491
	}
492
	if (fstat(fd_t, &tmp_st) == -1) {
493
		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
494
		goto err;
495
	}
496
	if (!equal_stats(out_st, &tmp_st)) {
497
		fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir);
498
		goto err;
499
	}
500
	if (fchmod(fd_t, 01770) == -1) {
501
		fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno));
502
		goto err;
503
	}
504
	/* re-stat again to pick change mode */
505
	if (fstat(fd_t, out_st) == -1) {
506
		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
507
		goto err;
508
	}
509
510
	/* copy selinux context */
511
	if (execcon) {
512
		if (fsetfilecon(fd_t, con) == -1) {
513
			fprintf(stderr, _("Failed to set context of the directory %s: %s\n"), tmpdir, strerror(errno));
514
			goto err;
515
		}
516
	}
517
518
	setfsuid(pwd->pw_uid);
519
520
	if (rsynccmd(src, tmpdir, &cmdbuf) < 0) {
521
		goto err;
522
	}
523
	
524
	/* ok to not reach this if there is an error */
525
	setfsuid(0);
526
527
	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
528
		fprintf(stderr, _("Failed to populate runtime temporary directory\n"));
529
		cleanup_tmpdir(tmpdir, src, pwd, 0);
530
		goto err;
531
	}
532
533
	goto good;
534
err:
535
	free(tmpdir);
536
	tmpdir = NULL;
537
good:
538
	free(cmdbuf);
539
	cmdbuf = NULL;
540
	freecon(con);
541
	con = NULL;
542
	if (fd_t >= 0)
543
		close(fd_t);
544
	if (fd_s >= 0)
545
		close(fd_s);
546
	return tmpdir;
547
}
548
549
#define DEFAULT_PATH "/usr/bin:/bin"
550
#define USAGE_STRING _("USAGE: seunshare [ -v ] -C -t tmpdir -h homedir [-Z context] -- executable [args]")
551
#define PROC_BASE "/proc"
552
553
static int killall (security_context_t execcon) {
554
	DIR *dir;
555
	security_context_t scon;
556
	struct dirent *de;
557
	pid_t *pid_table, pid, self;
558
	int i;
559
	int pids, max_pids;
560
	int running = 0;
561
	self = getpid();
562
	if (!(dir = opendir(PROC_BASE))) {
563
		return -1;
564
	}
565
	max_pids = 256;
566
	pid_table = malloc(max_pids * sizeof(pid_t));
567
	if (!pid_table) {
568
		return -1;
569
	}
570
	pids = 0;
571
	context_t con;
572
	con = context_new(execcon);
573
	const char *mcs = context_range_get(con);
574
	printf("mcs=%s\n", mcs);
575
	while ((de = readdir(dir)) != NULL) {
576
		if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
577
			continue;
578
579
		if (pids == max_pids) {
580
			if(!(pid_table = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
581
				return -1;
582
			}
583
			max_pids *= 2;
584
		}
585
		pid_table[pids++] = pid;
586
	}
587
588
	(void)closedir(dir);
589
590
	for (i = 0; i < pids; i++) {
591
		pid_t id = pid_table[i];
592
593
		if (getpidcon(id, &scon) == 0) {
594
			context_t pidcon = context_new(scon);
595
			/* Attempt to kill remaining processes */
596
			if (strcmp(context_range_get(pidcon), mcs) == 0)
597
				kill(id, SIGKILL);
598
599
			context_free(pidcon);
600
			freecon(scon);
601
		}
602
		running++;
603
	}
604
605
	context_free(con);
606
	free(pid_table);
607
	return running;
608
}
157
609
158
int main(int argc, char **argv) {
610
int main(int argc, char **argv) {
159
	int rc;
160
	int status = -1;
611
	int status = -1;
612
	security_context_t execcon = NULL;
161
613
162
	security_context_t scontext;
163
164
	int flag_index;		/* flag index in argv[] */
165
	int clflag;		/* holds codes for command line flags */
614
	int clflag;		/* holds codes for command line flags */
166
	char *tmpdir_s = NULL;	/* tmpdir spec'd by user in argv[] */
615
	int kill_all = 0;
616
167
	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
617
	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
618
	char *tmpdir_s = NULL; /* tmpdir spec'd by user in argv[] */
619
	char * tmpdir_r = NULL; /* tmpdir created by seunshare */
620
621
	struct stat st_homedir;
622
	struct stat st_tmpdir_s;
623
	struct stat st_tmpdir_r;
168
624
169
	const struct option long_options[] = {
625
	const struct option long_options[] = {
170
		{"homedir", 1, 0, 'h'},
626
		{"homedir", 1, 0, 'h'},
171
		{"tmpdir", 1, 0, 't'},
627
		{"tmpdir", 1, 0, 't'},
628
		{"kill", 1, 0, 'k'},
172
		{"verbose", 1, 0, 'v'},
629
		{"verbose", 1, 0, 'v'},
630
		{"context", 1, 0, 'Z'},
631
		{"capabilities", 1, 0, 'C'},
173
		{NULL, 0, 0, 0}
632
		{NULL, 0, 0, 0}
174
	};
633
	};
175
634
Lines 187-220 Link Here
187
	}
646
	}
188
647
189
	if (verify_shell(pwd->pw_shell) < 0) {
648
	if (verify_shell(pwd->pw_shell) < 0) {
190
		fprintf(stderr, _("Error!  Shell is not valid.\n"));
649
		fprintf(stderr, _("Error: User shell is not valid.\n"));
191
		return -1;
650
		return -1;
192
	}
651
	}
193
652
194
	while (1) {
653
	while (1) {
195
		clflag = getopt_long(argc, argv, "h:t:", long_options,
654
		clflag = getopt_long(argc, argv, "Cvh:t:Z", long_options, NULL);
196
				     &flag_index);
197
		if (clflag == -1)
655
		if (clflag == -1)
198
			break;
656
			break;
199
657
200
		switch (clflag) {
658
		switch (clflag) {
201
		case 't':
659
		case 't':
202
			if (!(tmpdir_s = realpath(optarg, NULL))) {
660
			tmpdir_s = optarg;
203
				fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno));
661
			break;
204
				return -1;
662
		case 'k':
205
			}
663
			kill_all = 1;
206
			if (verify_mount(tmpdir_s, pwd) < 0) return -1;
207
			break;
664
			break;
208
		case 'h':
665
		case 'h':
209
			if (!(homedir_s = realpath(optarg, NULL))) {
666
			homedir_s = optarg;
210
				fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno));
211
				return -1;
212
			}
213
			if (verify_mount(homedir_s, pwd) < 0) return -1;
214
			if (verify_mount(pwd->pw_dir, pwd) < 0) return -1;
215
			break;
667
			break;
216
		case 'v':
668
		case 'v':
217
			verbose = 1;
669
			verbose++;
670
			break;
671
		case 'C':
672
			cap_set = CAPNG_SELECT_CAPS;
673
			break;
674
		case 'Z':
675
			execcon = optarg;
218
			break;
676
			break;
219
		default:
677
		default:
220
			fprintf(stderr, "%s\n", USAGE_STRING);
678
			fprintf(stderr, "%s\n", USAGE_STRING);
Lines 223-296 Link Here
223
	}
681
	}
224
682
225
	if (! homedir_s && ! tmpdir_s) {
683
	if (! homedir_s && ! tmpdir_s) {
226
		fprintf(stderr, _("Error: tmpdir and/or homedir required \n"),
684
		fprintf(stderr, _("Error: tmpdir and/or homedir required\n %s\n"), USAGE_STRING);
227
			"%s\n", USAGE_STRING);
228
		return -1;
685
		return -1;
229
	}
686
	}
230
687
231
	if (argc - optind < 2) {
688
	if (argc - optind < 1) {
232
		fprintf(stderr, _("Error: context and executable required \n"),
689
		fprintf(stderr, _("Error: executable required \n %s\n"), USAGE_STRING);
233
			"%s\n", USAGE_STRING);
234
		return -1;
690
		return -1;
235
	}
691
	}
236
692
237
	scontext = argv[optind++];
693
	if (execcon && is_selinux_enabled() != -1) {
238
	
694
		fprintf(stderr, _("Error: execution context specified, but SELinux is not enabled\n"));
239
	if (set_signal_handles())
240
		return -1;
241
242
        if (unshare(CLONE_NEWNS) < 0) {
243
		perror(_("Failed to unshare"));
244
		return -1;
695
		return -1;
245
	}
696
	}
246
697
247
	if (homedir_s && tmpdir_s && (strncmp(pwd->pw_dir, tmpdir_s, strlen(pwd->pw_dir)) == 0)) {
698
	if (set_signal_handles()) return -1;
248
	    if (seunshare_mount(tmpdir_s, "/tmp", pwd) < 0)
699
249
		    return -1;
700
	/* set fsuid to ruid */
250
	    if (seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0)
701
	/* Changing fsuid is usually required when user-specified directory is
251
		    return -1;
702
	 * on an NFS mount. It's also desired to avoid leaking info about
252
	} else {			
703
	 * existence of the files not accessible to the user.
253
		if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0)
704
	 */
254
				return -1;
705
	setfsuid(uid);
255
				
256
		if (tmpdir_s && seunshare_mount(tmpdir_s, "/tmp", pwd) < 0)
257
				return -1;
258
	}
259
706
260
	if (drop_capabilities(uid)) {
707
	/* verify homedir and tmpdir */
261
		perror(_("Failed to drop all capabilities"));
708
	if (homedir_s && (
709
		verify_directory(homedir_s, NULL, &st_homedir) < 0 ||
710
		check_owner_uid(uid, homedir_s, &st_homedir))) return -1;
711
	if (tmpdir_s && (
712
		verify_directory(tmpdir_s, NULL, &st_tmpdir_s) < 0 ||
713
		check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))) return -1;
714
	setfsuid(0);
715
716
	/* create runtime tmpdir */
717
	if (tmpdir_s && (tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s, &st_tmpdir_r, pwd, execcon)) == NULL) {
718
		fprintf(stderr, _("Failed to create runtime temporary directory\n"));
262
		return -1;
719
		return -1;
263
	}
720
	}
264
721
265
	int child = fork();
722
	/* spawn child process */
723
	child = fork();
266
	if (child == -1) {
724
	if (child == -1) {
267
		perror(_("Unable to fork"));
725
		perror(_("Unable to fork"));
268
		return -1;
726
		goto err;
269
	}
727
	}
270
728
271
	if (!child) {
729
	if (child == 0) {
272
		char *display=NULL;
730
		char *display = NULL;
273
		/* Construct a new environment */
731
		int rc = -1;
274
		char *d = getenv("DISPLAY");
732
275
		if (d) {
733
		if (unshare(CLONE_NEWNS) < 0) {
276
			display =  strdup(d);
734
			perror(_("Failed to unshare"));
277
			if (!display) {
735
			goto childerr;
278
				perror(_("Out of memory"));
279
				exit(-1);
280
			}
281
		}
736
		}
282
737
283
		if ((rc = clearenv())) {
738
		/* assume fsuid == ruid after this point */
284
			perror(_("Unable to clear environment"));
739
		setfsuid(uid);
285
			free(display);
740
286
			exit(-1);
741
		/* mount homedir and tmpdir, in this order */
742
		if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, &st_homedir) != 0) goto childerr;
743
		if (tmpdir_s && seunshare_mount(tmpdir_r, "/tmp", &st_tmpdir_r) != 0) goto childerr;
744
745
		if (drop_privs(uid) != 0) goto childerr;
746
747
		/* construct a new environment */
748
		if ((display = getenv("DISPLAY")) != NULL) {
749
			if ((display = strdup(display)) == NULL) {
750
				perror(_("Out of memory"));
751
				goto childerr;
752
			}
287
		}
753
		}
288
		
754
	
289
		if (setexeccon(scontext)) {
755
		if ((rc = clearenv()) != 0) {
290
			fprintf(stderr, _("Could not set exec context to %s.\n"),
756
			perror(_("Failed to clear environment"));
291
				scontext);
757
			goto childerr;
292
			free(display);
293
			exit(-1);
294
		}
758
		}
295
759
296
		if (display) 
760
		if (display) 
Lines 300-321 Link Here
300
		rc |= setenv("USER", pwd->pw_name, 1);
764
		rc |= setenv("USER", pwd->pw_name, 1);
301
		rc |= setenv("LOGNAME", pwd->pw_name, 1);
765
		rc |= setenv("LOGNAME", pwd->pw_name, 1);
302
		rc |= setenv("PATH", DEFAULT_PATH, 1);
766
		rc |= setenv("PATH", DEFAULT_PATH, 1);
303
		
767
	
768
		if (rc != 0) {
769
			fprintf(stderr, _("Failed to construct environment\n"));
770
			goto childerr;
771
		}
772
773
		/* selinux context */
774
		if (execcon && setexeccon(execcon) != 0) {
775
			fprintf(stderr, _("Could not set exec context to %s.\n"), execcon);
776
			goto childerr;
777
		}
778
304
		if (chdir(pwd->pw_dir)) {
779
		if (chdir(pwd->pw_dir)) {
305
			perror(_("Failed to change dir to homedir"));
780
			perror(_("Failed to change dir to homedir"));
306
			exit(-1);
781
			goto childerr;
307
		}
782
		}
783
308
		setsid();
784
		setsid();
785
309
		execv(argv[optind], argv + optind);
786
		execv(argv[optind], argv + optind);
787
		fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno));
788
childerr:
310
		free(display);
789
		free(display);
311
		perror("execv");
312
		exit(-1);
790
		exit(-1);
313
	} else {
314
		waitpid(child, &status, 0);
315
	}
791
	}
316
792
317
	free(tmpdir_s);
793
	drop_caps();
318
	free(homedir_s);
319
794
795
	/* parent waits for child exit to do the cleanup */
796
	waitpid(child, &status, 0);
797
	status_to_retval(status, status);
798
799
	/* Make sure all child processes exit */
800
	kill(-child, SIGTERM);
801
802
	if (execcon && kill_all)
803
		killall(execcon);
804
	
805
	if (tmpdir_r) cleanup_tmpdir(tmpdir_r, tmpdir_s, pwd, 1);
806
err:
807
	free(tmpdir_r);
320
	return status;
808
	return status;
321
}
809
}

Return to bug 374897