Merge lp:~jdstrand/snap-confine/coding-style into lp:~snappy-dev/snap-confine/trunk

Proposed by Jamie Strandboge
Status: Merged
Merged at revision: 101
Proposed branch: lp:~jdstrand/snap-confine/coding-style
Merge into: lp:~snappy-dev/snap-confine/trunk
Diff against target: 1493 lines (+706/-669)
8 files modified
Makefile (+4/-1)
debian/changelog (+4/-0)
debian/control (+1/-1)
src/Makefile (+20/-1)
src/main.c (+492/-490)
src/seccomp.c (+129/-122)
src/utils.c (+52/-50)
src/utils.h (+4/-4)
To merge this branch: bzr merge lp:~jdstrand/snap-confine/coding-style
Reviewer Review Type Date Requested Status
Michael Vogt (community) Approve
Review via email: mp+289781@code.launchpad.net

Description of the change

  * enforce coding style:
    - add syntax-check and fmt Makefile targets
    - use 'indent -linux'
    - debian/control: Build-Depends on indent

To post a comment you must log in.
Revision history for this message
Michael Vogt (mvo) wrote :

Looks great, thanks for this. Unfortunate that we loose the bzr blame info but thats life.

review: Approve
Revision history for this message
Jamie Strandboge (jdstrand) wrote :

hrmm, yes, oh well. Thanks!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2016-02-25 15:02:17 +0000
3+++ Makefile 2016-03-22 12:19:24 +0000
4@@ -8,5 +8,8 @@
5 make -C src $@
6 make -C tests $@
7
8-check: all
9+syntax-check:
10+ make -C src syntax-check
11+
12+check: all syntax-check
13 make -C tests test
14
15=== modified file 'debian/changelog'
16--- debian/changelog 2016-03-22 11:23:59 +0000
17+++ debian/changelog 2016-03-22 12:19:24 +0000
18@@ -1,6 +1,10 @@
19 ubuntu-core-launcher (1.0.21) UNRELEASED; urgency=medium
20 * src/main.c: setup private /dev/pts
21 * debian/usr.bin.ubuntu-core-launcher: allow mounting /dev/pts
22+ * enforce coding style:
23+ - add syntax-check and fmt Makefile targets
24+ - use 'indent -linux'
25+ - debian/control: Build-Depends on indent
26
27 -- Jamie Strandboge <jamie@ubuntu.com> Tue, 22 Mar 2016 06:23:38 -0500
28
29
30=== modified file 'debian/control'
31--- debian/control 2016-01-26 15:11:20 +0000
32+++ debian/control 2016-03-22 12:19:24 +0000
33@@ -2,7 +2,7 @@
34 Section: utils
35 Priority: optional
36 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
37-Build-Depends: debhelper (>= 9), libseccomp-dev, libapparmor-dev, libudev-dev, dh-apparmor
38+Build-Depends: debhelper (>= 9), libseccomp-dev, libapparmor-dev, libudev-dev, dh-apparmor, indent
39 Standards-Version: 3.9.6
40 Vcs-Bzr: lp:~snappy-dev/ubuntu-core-launcher/trunk
41
42
43=== modified file 'src/Makefile'
44--- src/Makefile 2015-05-06 11:26:07 +0000
45+++ src/Makefile 2016-03-22 12:19:24 +0000
46@@ -2,8 +2,11 @@
47 CFLAGS= -D_GNU_SOURCE -O2 -Wall -Werror $(shell dpkg-buildflags --get CFLAGS)
48 LD_FLAGS = $(shell dpkg-buildflags --get LDFLAGS)
49 LIBS = -lapparmor -lseccomp -ludev
50+TMPDIR = ./tmp
51+FMT = indent -linux
52
53 BIN = ubuntu-core-launcher
54+HDRS = $(wildcard *.h)
55 SRCS = $(wildcard *.c)
56 OBJS = $(SRCS:.c=.o)
57
58@@ -15,7 +18,23 @@
59
60 distclean: clean
61 clean:
62- rm -f *.o ${BIN}
63+ rm -f *.o $(BIN) *~
64+ rm -rf $(TMPDIR)
65+
66+fmt:
67+ for f in $(HDRS) $(SRCS); do \
68+ echo "$(FMT) $$f ... "; \
69+ $(FMT) "$$f"; \
70+ done; \
71+
72+syntax-check:
73+ $(shell mkdir $(TMPDIR) 2>/dev/null)
74+ for f in $(HDRS) $(SRCS); do \
75+ out=$(TMPDIR)/$$f.out; \
76+ echo "Checking '$(FMT) $$f' ... "; \
77+ $(FMT) "$$f" -o "$$out"; \
78+ diff -Naur "$$f" "$$out" || exit 1; \
79+ done; \
80
81 install:
82 # create dirs
83
84=== modified file 'src/main.c'
85--- src/main.c 2016-03-21 17:33:08 +0000
86+++ src/main.c 2016-03-22 12:19:24 +0000
87@@ -41,498 +41,500 @@
88
89 #define MAX_BUF 1000
90
91-bool verify_appname(const char *appname) {
92- // these chars are allowed in a appname
93- const char* whitelist_re = "^[a-z0-9][a-z0-9+._-]+$";
94- regex_t re;
95- if (regcomp(&re, whitelist_re, REG_EXTENDED|REG_NOSUB) != 0)
96- die("can not compile regex %s", whitelist_re);
97-
98- int status = regexec(&re, appname, 0, NULL, 0);
99- regfree(&re);
100-
101- return (status == 0);
102-}
103-
104-void run_snappy_app_dev_add(struct udev *u, const char *path, const char *appname) {
105- debug("run_snappy_app_dev_add: %s %s", path, appname);
106- struct udev_device *d = udev_device_new_from_syspath(u, path);
107- if (d == NULL)
108- die("can not find %s", path);
109- dev_t devnum = udev_device_get_devnum (d);
110- udev_device_unref(d);
111-
112- int status = 0;
113- pid_t pid = fork();
114- if (pid == 0) {
115- char buf[64];
116- unsigned major = MAJOR(devnum);
117- unsigned minor = MINOR(devnum);
118- must_snprintf(buf, sizeof(buf), "%u:%u", major, minor);
119- if(execl("/lib/udev/snappy-app-dev", "/lib/udev/snappy-app-dev", "add", appname, path, buf, NULL) != 0)
120- die("execlp failed");
121- }
122- if(waitpid(pid, &status, 0) < 0)
123- die("waitpid failed");
124- if(WIFEXITED(status) && WEXITSTATUS(status) != 0)
125- die("child exited with status %i", WEXITSTATUS(status));
126- else if(WIFSIGNALED(status))
127- die("child died with signal %i", WTERMSIG(status));
128-}
129-
130-void setup_udev_snappy_assign(const char *appname) {
131- debug("setup_udev_snappy_assign");
132-
133- struct udev *u = udev_new();
134- if (u == NULL)
135- die("udev_new failed");
136-
137- const char* static_devices[] = {
138- "/sys/class/mem/null",
139- "/sys/class/mem/full",
140- "/sys/class/mem/zero",
141- "/sys/class/mem/random",
142- "/sys/class/mem/urandom",
143- "/sys/class/tty/tty",
144- "/sys/class/tty/console",
145- "/sys/class/tty/ptmx",
146- NULL,
147- };
148- int i;
149- for(i=0; static_devices[i] != NULL; i++) {
150- run_snappy_app_dev_add(u, static_devices[i], appname);
151- }
152-
153- struct udev_enumerate *devices = udev_enumerate_new(u);
154- if (devices == NULL)
155- die("udev_enumerate_new failed");
156-
157- if (udev_enumerate_add_match_tag (devices, "snappy-assign") != 0)
158- die("udev_enumerate_add_match_tag");
159-
160- if(udev_enumerate_add_match_property (devices, "SNAPPY_APP", appname) != 0)
161- die("udev_enumerate_add_match_property");
162-
163- if(udev_enumerate_scan_devices(devices) != 0)
164- die("udev_enumerate_scan failed");
165-
166- struct udev_list_entry *l = udev_enumerate_get_list_entry (devices);
167- while (l != NULL) {
168- const char *path = udev_list_entry_get_name (l);
169- if (path == NULL)
170- die("udev_list_entry_get_name failed");
171- run_snappy_app_dev_add(u, path, appname);
172- l = udev_list_entry_get_next(l);
173- }
174-
175- udev_enumerate_unref(devices);
176- udev_unref(u);
177-}
178-
179-void setup_devices_cgroup(const char *appname) {
180- debug("setup_devices_cgroup");
181-
182- // extra paranoia
183- if(!verify_appname(appname))
184- die("appname %s not allowed", appname);
185-
186- // create devices cgroup controller
187- char cgroup_dir[PATH_MAX];
188- must_snprintf(cgroup_dir, sizeof(cgroup_dir), "/sys/fs/cgroup/devices/snappy.%s/", appname);
189-
190- if (mkdir(cgroup_dir, 0755) < 0 && errno != EEXIST)
191- die("mkdir failed");
192-
193- // move ourselves into it
194- char cgroup_file[PATH_MAX];
195- must_snprintf(cgroup_file, sizeof(cgroup_file), "%s%s", cgroup_dir, "tasks");
196-
197- char buf[128];
198- must_snprintf(buf, sizeof(buf), "%i", getpid());
199- write_string_to_file(cgroup_file, buf);
200-
201- // deny by default
202- must_snprintf(cgroup_file, sizeof(cgroup_file), "%s%s", cgroup_dir, "devices.deny");
203- write_string_to_file(cgroup_file, "a");
204-
205-}
206-
207-bool snappy_udev_setup_required(const char *appname) {
208- debug("snappy_udev_setup_required");
209-
210- // extra paranoia
211- if(!verify_appname(appname))
212- die("appname %s not allowed", appname);
213-
214- char override_file[PATH_MAX];
215- must_snprintf(override_file, sizeof(override_file), "/var/lib/apparmor/clicks/%s.json.additional", appname);
216-
217- // if a snap package gets unrestricted apparmor access we need to setup
218- // a device cgroup.
219- //
220- // the "needle" string is what gives this access so we search for that
221- // here
222- const char *needle =
223- "{" "\n"
224- " \"write_path\": [" "\n"
225- " \"/dev/**\"" "\n"
226- " ]," "\n"
227- " \"read_path\": [" "\n"
228- " \"/run/udev/data/*\"" "\n"
229- " ]\n"
230- "}";
231- debug("looking for: '%s'", needle);
232- char content[strlen(needle)];
233-
234- int fd = open(override_file, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
235- if (fd < 0)
236- return false;
237- int n = read(fd, content, sizeof(content));
238- close(fd);
239- if (n < sizeof(content))
240- return false;
241-
242- // memcpy so that we don't have to deal with \0 in the input
243- if (memcmp(content, needle, strlen(needle)) == 0) {
244- debug("found needle, need to apply udev setup");
245- return true;
246- }
247-
248- return false;
249-}
250-
251-bool is_running_on_classic_ubuntu() {
252- return (access("/var/lib/dpkg/status", F_OK) == 0);
253-}
254-
255-void setup_private_mount(const char* appname) {
256- uid_t uid = getuid();
257- gid_t gid = getgid();
258- char tmpdir[MAX_BUF] = {0};
259-
260- // Create a 0700 base directory, this is the base dir that is
261- // protected from other users.
262- //
263- // Under that basedir, we put a 1777 /tmp dir that is then bind
264- // mounted for the applications to use
265- must_snprintf(tmpdir, sizeof(tmpdir), "/tmp/snap.%d_%s_XXXXXX", uid, appname);
266- if (mkdtemp(tmpdir) == NULL) {
267- die("unable to create tmpdir");
268- }
269-
270- // now we create a 1777 /tmp inside our private dir
271- mode_t old_mask = umask(0);
272- char *d = strdup(tmpdir);
273- if (!d) {
274- die("Out of memory");
275- }
276- must_snprintf(tmpdir, sizeof(tmpdir), "%s/tmp", d);
277- free(d);
278-
279- if (mkdir(tmpdir, 01777) != 0) {
280- die("unable to create /tmp inside private dir");
281- }
282- umask(old_mask);
283-
284- // MS_BIND is there from linux 2.4
285- if (mount(tmpdir, "/tmp", NULL, MS_BIND, NULL) != 0) {
286- die("unable to bind private /tmp");
287- }
288- // MS_PRIVATE needs linux > 2.6.11
289- if (mount("none", "/tmp", NULL, MS_PRIVATE, NULL) != 0) {
290- die("unable to make /tmp/ private");
291- }
292-
293- // do the chown after the bind mount to avoid potential shenanigans
294- if (chown("/tmp/", uid, gid) < 0) {
295- die("unable to chown tmpdir");
296- }
297-
298- // ensure we set the various TMPDIRs to our newly created tmpdir
299- const char *tmpd[] = {"TMPDIR", "TEMPDIR", "SNAP_APP_TMPDIR", NULL};
300- int i;
301- for (i=0; tmpd[i] != NULL; i++) {
302- if (setenv(tmpd[i], "/tmp", 1) != 0) {
303- die("unable to set '%s'", tmpd[i]);
304- }
305- }
306-}
307-
308-void setup_private_pts() {
309- // See https://www.kernel.org/doc/Documentation/filesystems/devpts.txt
310- //
311- // Ubuntu by default uses devpts 'single-instance' mode where /dev/pts/ptmx
312- // is mounted with ptmxmode=0000. We don't want to change the startup
313- // scripts though, so we follow the instructions in point '4' of
314- // 'User-space changes' in the above doc. In other words, after
315- // unshare(CLONE_NEWNS), we mount devpts with -o newinstance,ptmxmode=0666
316- // and then bind mount /dev/pts/ptmx onto /dev/ptmx
317-
318- struct stat st;
319-
320- // Make sure /dev/pts/ptmx exists, otherwise we are in legacy mode which
321- // doesn't provide the isolation we require.
322- if (stat("/dev/pts/ptmx", &st) != 0) {
323- die("/dev/pts/ptmx does not exist");
324- }
325- // Make sure /dev/ptmx exists so we can bind mount over it
326- if (stat("/dev/ptmx", &st) != 0) {
327- die("/dev/ptmx does not exist");
328- }
329-
330- // Since multi-instance, use ptmxmode=0666. The other options are copied
331- // from /etc/default/devpts
332- if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL,
333- "newinstance,ptmxmode=0666,mode=0620,gid=5")) {
334- die("unable to mount a new instance of '/dev/pts'");
335- }
336-
337- if (mount("/dev/pts/ptmx", "/dev/ptmx", "none", MS_BIND, 0)) {
338- die("unable to mount '/dev/pts/ptmx'->'/dev/ptmx'");
339- }
340-}
341-
342-void setup_snappy_os_mounts() {
343- debug("setup_snappy_os_mounts()\n");
344-
345- // FIXME: hardcoded "ubuntu-core.*"
346- glob_t glob_res;
347- if (glob("/snaps/ubuntu-core*/current/", 0, NULL, &glob_res) != 0) {
348- die("can not find a snappy os");
349- }
350- if ((glob_res.gl_pathc =! 1)) {
351- die("expected 1 os snap, found %i", (int)glob_res.gl_pathc);
352- }
353- char *mountpoint = glob_res.gl_pathv[0];
354-
355- // we mount some whitelisted directories
356- //
357- // Note that we do not mount "/etc/" from snappy. We could do that,
358- // but if we do we need to ensure that data like /etc/{hostname,hosts,
359- // passwd,groups} is in sync between the two systems (probably via
360- // selected bind mounts of those files).
361- const char *mounts[] = {"/bin", "/sbin", "/lib", "/lib64", "/usr"};
362- for (int i=0; i < sizeof(mounts)/sizeof(char*); i++) {
363- // we mount the OS snap /bin over the real /bin in this NS
364- const char *dst = mounts[i];
365-
366- char buf[512];
367- must_snprintf(buf, sizeof(buf), "%s%s", mountpoint, dst);
368- const char *src = buf;
369-
370- debug("mounting %s -> %s\n", src, dst);
371- if (mount(src, dst, NULL, MS_BIND, NULL) != 0) {
372- die("unable to bind %s to %s", src, dst);
373- }
374- }
375-
376- globfree(&glob_res);
377-}
378-
379-void setup_slave_mount_namespace() {
380- // unshare() and CLONE_NEWNS require linux >= 2.6.16 and glibc >= 2.14
381- // if using an older glibc, you'd need -D_BSD_SOURCE or -D_SVID_SORUCE.
382- if (unshare(CLONE_NEWNS) < 0) {
383- die("unable to set up mount namespace");
384- }
385-
386- // make our "/" a rslave of the real "/". this means that
387- // mounts from the host "/" get propagated to our namespace
388- // (i.e. we see new media mounts)
389- if (mount("none", "/", NULL, MS_REC|MS_SLAVE, NULL) != 0) {
390- die("can not make make / rslave");
391- }
392-}
393-
394-void mkpath(const char *const path) {
395- // If asked to create an empty path, return immediately.
396- if (strlen(path) == 0) {
397- return;
398- }
399-
400- // We're going to use strtok_r, which needs to modify the path, so we'll make
401- // a copy of it.
402- char *path_copy = strdup(path);
403- if (path_copy == NULL) {
404- die("failed to create user data directory");
405- }
406-
407- // Open flags to use while we walk the user data path:
408- // - Don't follow symlinks
409- // - Don't allow child access to file descriptor
410- // - Only open a directory (fail otherwise)
411- int open_flags = O_NOFOLLOW | O_CLOEXEC | O_DIRECTORY;
412-
413- // We're going to create each path segment via openat/mkdirat calls instead
414- // of mkdir calls, to avoid following symlinks and placing the user data
415- // directory somewhere we never intended for it to go. The first step is to
416- // get an initial file descriptor.
417- int fd = AT_FDCWD;
418- if (path_copy[0] == '/') {
419- fd = open("/", open_flags);
420- if (fd < 0) {
421- free(path_copy);
422- die("failed to create user data directory");
423- }
424- }
425-
426- // strtok_r needs a pointer to keep track of where it is in the string.
427- char *path_walker;
428-
429- // Initialize tokenizer and obtain first path segment.
430- char *path_segment = strtok_r(path_copy, "/", &path_walker);
431- while (path_segment) {
432- // Try to create the directory. It's okay if it already existed, but any
433- // other error is fatal.
434- if (mkdirat(fd, path_segment, 0755) < 0 && errno != EEXIST) {
435- close(fd);
436- free(path_copy);
437- die("failed to create user data directory");
438- }
439-
440- // Open the parent directory we just made (and close the previous one) so
441- // we can continue down the path.
442- int previous_fd = fd;
443- fd = openat(fd, path_segment, open_flags);
444- close(previous_fd);
445- if (fd < 0) {
446- free(path_copy);
447- die("failed to create user data directory");
448- }
449-
450- // Obtain the next path segment.
451- path_segment = strtok_r(NULL, "/", &path_walker);
452- }
453-
454- // Close the descriptor for the final directory in the path.
455- close(fd);
456-
457- free(path_copy);
458-}
459-
460-void setup_user_data() {
461- const char *user_data = getenv("SNAP_USER_DATA");
462-
463- // If $SNAP_USER_DATA wasn't defined, check the deprecated
464- // $SNAP_APP_USER_DATA_PATH.
465- if (user_data == NULL) {
466- user_data = getenv("SNAP_APP_USER_DATA_PATH");
467- // If it's still not defined, there's nothing to do. No need to die,
468- // there's simply no directory to create.
469- if (user_data == NULL) {
470- return;
471- }
472- }
473-
474- // Only support absolute paths.
475- if (user_data[0] != '/') {
476- die("user data directory must be an absolute path");
477- }
478-
479- mkpath(user_data);
480+bool verify_appname(const char *appname)
481+{
482+ // these chars are allowed in a appname
483+ const char *whitelist_re = "^[a-z0-9][a-z0-9+._-]+$";
484+ regex_t re;
485+ if (regcomp(&re, whitelist_re, REG_EXTENDED | REG_NOSUB) != 0)
486+ die("can not compile regex %s", whitelist_re);
487+
488+ int status = regexec(&re, appname, 0, NULL, 0);
489+ regfree(&re);
490+
491+ return (status == 0);
492+}
493+
494+void run_snappy_app_dev_add(struct udev *u, const char *path,
495+ const char *appname)
496+{
497+ debug("run_snappy_app_dev_add: %s %s", path, appname);
498+ struct udev_device *d = udev_device_new_from_syspath(u, path);
499+ if (d == NULL)
500+ die("can not find %s", path);
501+ dev_t devnum = udev_device_get_devnum(d);
502+ udev_device_unref(d);
503+
504+ int status = 0;
505+ pid_t pid = fork();
506+ if (pid == 0) {
507+ char buf[64];
508+ unsigned major = MAJOR(devnum);
509+ unsigned minor = MINOR(devnum);
510+ must_snprintf(buf, sizeof(buf), "%u:%u", major, minor);
511+ if (execl
512+ ("/lib/udev/snappy-app-dev", "/lib/udev/snappy-app-dev",
513+ "add", appname, path, buf, NULL) != 0)
514+ die("execlp failed");
515+ }
516+ if (waitpid(pid, &status, 0) < 0)
517+ die("waitpid failed");
518+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
519+ die("child exited with status %i", WEXITSTATUS(status));
520+ else if (WIFSIGNALED(status))
521+ die("child died with signal %i", WTERMSIG(status));
522+}
523+
524+void setup_udev_snappy_assign(const char *appname)
525+{
526+ debug("setup_udev_snappy_assign");
527+
528+ struct udev *u = udev_new();
529+ if (u == NULL)
530+ die("udev_new failed");
531+
532+ const char *static_devices[] = {
533+ "/sys/class/mem/null",
534+ "/sys/class/mem/full",
535+ "/sys/class/mem/zero",
536+ "/sys/class/mem/random",
537+ "/sys/class/mem/urandom",
538+ "/sys/class/tty/tty",
539+ "/sys/class/tty/console",
540+ "/sys/class/tty/ptmx",
541+ NULL,
542+ };
543+ int i;
544+ for (i = 0; static_devices[i] != NULL; i++) {
545+ run_snappy_app_dev_add(u, static_devices[i], appname);
546+ }
547+
548+ struct udev_enumerate *devices = udev_enumerate_new(u);
549+ if (devices == NULL)
550+ die("udev_enumerate_new failed");
551+
552+ if (udev_enumerate_add_match_tag(devices, "snappy-assign") != 0)
553+ die("udev_enumerate_add_match_tag");
554+
555+ if (udev_enumerate_add_match_property(devices, "SNAPPY_APP", appname) !=
556+ 0)
557+ die("udev_enumerate_add_match_property");
558+
559+ if (udev_enumerate_scan_devices(devices) != 0)
560+ die("udev_enumerate_scan failed");
561+
562+ struct udev_list_entry *l = udev_enumerate_get_list_entry(devices);
563+ while (l != NULL) {
564+ const char *path = udev_list_entry_get_name(l);
565+ if (path == NULL)
566+ die("udev_list_entry_get_name failed");
567+ run_snappy_app_dev_add(u, path, appname);
568+ l = udev_list_entry_get_next(l);
569+ }
570+
571+ udev_enumerate_unref(devices);
572+ udev_unref(u);
573+}
574+
575+void setup_devices_cgroup(const char *appname)
576+{
577+ debug("setup_devices_cgroup");
578+
579+ // extra paranoia
580+ if (!verify_appname(appname))
581+ die("appname %s not allowed", appname);
582+
583+ // create devices cgroup controller
584+ char cgroup_dir[PATH_MAX];
585+ must_snprintf(cgroup_dir, sizeof(cgroup_dir),
586+ "/sys/fs/cgroup/devices/snappy.%s/", appname);
587+
588+ if (mkdir(cgroup_dir, 0755) < 0 && errno != EEXIST)
589+ die("mkdir failed");
590+
591+ // move ourselves into it
592+ char cgroup_file[PATH_MAX];
593+ must_snprintf(cgroup_file, sizeof(cgroup_file), "%s%s", cgroup_dir,
594+ "tasks");
595+
596+ char buf[128];
597+ must_snprintf(buf, sizeof(buf), "%i", getpid());
598+ write_string_to_file(cgroup_file, buf);
599+
600+ // deny by default
601+ must_snprintf(cgroup_file, sizeof(cgroup_file), "%s%s", cgroup_dir,
602+ "devices.deny");
603+ write_string_to_file(cgroup_file, "a");
604+
605+}
606+
607+bool snappy_udev_setup_required(const char *appname)
608+{
609+ debug("snappy_udev_setup_required");
610+
611+ // extra paranoia
612+ if (!verify_appname(appname))
613+ die("appname %s not allowed", appname);
614+
615+ char override_file[PATH_MAX];
616+ must_snprintf(override_file, sizeof(override_file),
617+ "/var/lib/apparmor/clicks/%s.json.additional", appname);
618+
619+ // if a snap package gets unrestricted apparmor access we need to setup
620+ // a device cgroup.
621+ //
622+ // the "needle" string is what gives this access so we search for that
623+ // here
624+ const char *needle =
625+ "{" "\n"
626+ " \"write_path\": [" "\n"
627+ " \"/dev/**\"" "\n"
628+ " ]," "\n"
629+ " \"read_path\": [" "\n" " \"/run/udev/data/*\"" "\n" " ]\n" "}";
630+ debug("looking for: '%s'", needle);
631+ char content[strlen(needle)];
632+
633+ int fd = open(override_file, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
634+ if (fd < 0)
635+ return false;
636+ int n = read(fd, content, sizeof(content));
637+ close(fd);
638+ if (n < sizeof(content))
639+ return false;
640+
641+ // memcpy so that we don't have to deal with \0 in the input
642+ if (memcmp(content, needle, strlen(needle)) == 0) {
643+ debug("found needle, need to apply udev setup");
644+ return true;
645+ }
646+
647+ return false;
648+}
649+
650+bool is_running_on_classic_ubuntu()
651+{
652+ return (access("/var/lib/dpkg/status", F_OK) == 0);
653+}
654+
655+void setup_private_mount(const char *appname)
656+{
657+ uid_t uid = getuid();
658+ gid_t gid = getgid();
659+ char tmpdir[MAX_BUF] = { 0 };
660+
661+ // Create a 0700 base directory, this is the base dir that is
662+ // protected from other users.
663+ //
664+ // Under that basedir, we put a 1777 /tmp dir that is then bind
665+ // mounted for the applications to use
666+ must_snprintf(tmpdir, sizeof(tmpdir), "/tmp/snap.%d_%s_XXXXXX", uid,
667+ appname);
668+ if (mkdtemp(tmpdir) == NULL) {
669+ die("unable to create tmpdir");
670+ }
671+ // now we create a 1777 /tmp inside our private dir
672+ mode_t old_mask = umask(0);
673+ char *d = strdup(tmpdir);
674+ if (!d) {
675+ die("Out of memory");
676+ }
677+ must_snprintf(tmpdir, sizeof(tmpdir), "%s/tmp", d);
678+ free(d);
679+
680+ if (mkdir(tmpdir, 01777) != 0) {
681+ die("unable to create /tmp inside private dir");
682+ }
683+ umask(old_mask);
684+
685+ // MS_BIND is there from linux 2.4
686+ if (mount(tmpdir, "/tmp", NULL, MS_BIND, NULL) != 0) {
687+ die("unable to bind private /tmp");
688+ }
689+ // MS_PRIVATE needs linux > 2.6.11
690+ if (mount("none", "/tmp", NULL, MS_PRIVATE, NULL) != 0) {
691+ die("unable to make /tmp/ private");
692+ }
693+ // do the chown after the bind mount to avoid potential shenanigans
694+ if (chown("/tmp/", uid, gid) < 0) {
695+ die("unable to chown tmpdir");
696+ }
697+ // ensure we set the various TMPDIRs to our newly created tmpdir
698+ const char *tmpd[] = { "TMPDIR", "TEMPDIR", "SNAP_APP_TMPDIR", NULL };
699+ int i;
700+ for (i = 0; tmpd[i] != NULL; i++) {
701+ if (setenv(tmpd[i], "/tmp", 1) != 0) {
702+ die("unable to set '%s'", tmpd[i]);
703+ }
704+ }
705+}
706+
707+void setup_private_pts()
708+{
709+ // See https://www.kernel.org/doc/Documentation/filesystems/devpts.txt
710+ //
711+ // Ubuntu by default uses devpts 'single-instance' mode where /dev/pts/ptmx
712+ // is mounted with ptmxmode=0000. We don't want to change the startup
713+ // scripts though, so we follow the instructions in point '4' of
714+ // 'User-space changes' in the above doc. In other words, after
715+ // unshare(CLONE_NEWNS), we mount devpts with -o newinstance,ptmxmode=0666
716+ // and then bind mount /dev/pts/ptmx onto /dev/ptmx
717+
718+ struct stat st;
719+
720+ // Make sure /dev/pts/ptmx exists, otherwise we are in legacy mode which
721+ // doesn't provide the isolation we require.
722+ if (stat("/dev/pts/ptmx", &st) != 0) {
723+ die("/dev/pts/ptmx does not exist");
724+ }
725+ // Make sure /dev/ptmx exists so we can bind mount over it
726+ if (stat("/dev/ptmx", &st) != 0) {
727+ die("/dev/ptmx does not exist");
728+ }
729+ // Since multi-instance, use ptmxmode=0666. The other options are copied
730+ // from /etc/default/devpts
731+ if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL,
732+ "newinstance,ptmxmode=0666,mode=0620,gid=5")) {
733+ die("unable to mount a new instance of '/dev/pts'");
734+ }
735+
736+ if (mount("/dev/pts/ptmx", "/dev/ptmx", "none", MS_BIND, 0)) {
737+ die("unable to mount '/dev/pts/ptmx'->'/dev/ptmx'");
738+ }
739+}
740+
741+void setup_snappy_os_mounts()
742+{
743+ debug("setup_snappy_os_mounts()\n");
744+
745+ // FIXME: hardcoded "ubuntu-core.*"
746+ glob_t glob_res;
747+ if (glob("/snaps/ubuntu-core*/current/", 0, NULL, &glob_res) != 0) {
748+ die("can not find a snappy os");
749+ }
750+ if ((glob_res.gl_pathc = !1)) {
751+ die("expected 1 os snap, found %i", (int)glob_res.gl_pathc);
752+ }
753+ char *mountpoint = glob_res.gl_pathv[0];
754+
755+ // we mount some whitelisted directories
756+ //
757+ // Note that we do not mount "/etc/" from snappy. We could do that,
758+ // but if we do we need to ensure that data like /etc/{hostname,hosts,
759+ // passwd,groups} is in sync between the two systems (probably via
760+ // selected bind mounts of those files).
761+ const char *mounts[] = { "/bin", "/sbin", "/lib", "/lib64", "/usr" };
762+ for (int i = 0; i < sizeof(mounts) / sizeof(char *); i++) {
763+ // we mount the OS snap /bin over the real /bin in this NS
764+ const char *dst = mounts[i];
765+
766+ char buf[512];
767+ must_snprintf(buf, sizeof(buf), "%s%s", mountpoint, dst);
768+ const char *src = buf;
769+
770+ debug("mounting %s -> %s\n", src, dst);
771+ if (mount(src, dst, NULL, MS_BIND, NULL) != 0) {
772+ die("unable to bind %s to %s", src, dst);
773+ }
774+ }
775+
776+ globfree(&glob_res);
777+}
778+
779+void setup_slave_mount_namespace()
780+{
781+ // unshare() and CLONE_NEWNS require linux >= 2.6.16 and glibc >= 2.14
782+ // if using an older glibc, you'd need -D_BSD_SOURCE or -D_SVID_SORUCE.
783+ if (unshare(CLONE_NEWNS) < 0) {
784+ die("unable to set up mount namespace");
785+ }
786+ // make our "/" a rslave of the real "/". this means that
787+ // mounts from the host "/" get propagated to our namespace
788+ // (i.e. we see new media mounts)
789+ if (mount("none", "/", NULL, MS_REC | MS_SLAVE, NULL) != 0) {
790+ die("can not make make / rslave");
791+ }
792+}
793+
794+void mkpath(const char *const path)
795+{
796+ // If asked to create an empty path, return immediately.
797+ if (strlen(path) == 0) {
798+ return;
799+ }
800+ // We're going to use strtok_r, which needs to modify the path, so we'll make
801+ // a copy of it.
802+ char *path_copy = strdup(path);
803+ if (path_copy == NULL) {
804+ die("failed to create user data directory");
805+ }
806+ // Open flags to use while we walk the user data path:
807+ // - Don't follow symlinks
808+ // - Don't allow child access to file descriptor
809+ // - Only open a directory (fail otherwise)
810+ int open_flags = O_NOFOLLOW | O_CLOEXEC | O_DIRECTORY;
811+
812+ // We're going to create each path segment via openat/mkdirat calls instead
813+ // of mkdir calls, to avoid following symlinks and placing the user data
814+ // directory somewhere we never intended for it to go. The first step is to
815+ // get an initial file descriptor.
816+ int fd = AT_FDCWD;
817+ if (path_copy[0] == '/') {
818+ fd = open("/", open_flags);
819+ if (fd < 0) {
820+ free(path_copy);
821+ die("failed to create user data directory");
822+ }
823+ }
824+ // strtok_r needs a pointer to keep track of where it is in the string.
825+ char *path_walker;
826+
827+ // Initialize tokenizer and obtain first path segment.
828+ char *path_segment = strtok_r(path_copy, "/", &path_walker);
829+ while (path_segment) {
830+ // Try to create the directory. It's okay if it already existed, but any
831+ // other error is fatal.
832+ if (mkdirat(fd, path_segment, 0755) < 0 && errno != EEXIST) {
833+ close(fd);
834+ free(path_copy);
835+ die("failed to create user data directory");
836+ }
837+ // Open the parent directory we just made (and close the previous one) so
838+ // we can continue down the path.
839+ int previous_fd = fd;
840+ fd = openat(fd, path_segment, open_flags);
841+ close(previous_fd);
842+ if (fd < 0) {
843+ free(path_copy);
844+ die("failed to create user data directory");
845+ }
846+ // Obtain the next path segment.
847+ path_segment = strtok_r(NULL, "/", &path_walker);
848+ }
849+
850+ // Close the descriptor for the final directory in the path.
851+ close(fd);
852+
853+ free(path_copy);
854+}
855+
856+void setup_user_data()
857+{
858+ const char *user_data = getenv("SNAP_USER_DATA");
859+
860+ // If $SNAP_USER_DATA wasn't defined, check the deprecated
861+ // $SNAP_APP_USER_DATA_PATH.
862+ if (user_data == NULL) {
863+ user_data = getenv("SNAP_APP_USER_DATA_PATH");
864+ // If it's still not defined, there's nothing to do. No need to die,
865+ // there's simply no directory to create.
866+ if (user_data == NULL) {
867+ return;
868+ }
869+ }
870+ // Only support absolute paths.
871+ if (user_data[0] != '/') {
872+ die("user data directory must be an absolute path");
873+ }
874+
875+ mkpath(user_data);
876 }
877
878 int main(int argc, char **argv)
879 {
880- const int NR_ARGS = 3;
881- if(argc < NR_ARGS+1)
882- die("Usage: %s <appname> <apparmor> <binary>", argv[0]);
883-
884- const char *appname = argv[1];
885- const char *aa_profile = argv[2];
886- const char *binary = argv[3];
887- unsigned real_uid = getuid();
888- unsigned real_gid = getgid();
889-
890- if(!verify_appname(appname))
891- die("appname %s not allowed", appname);
892-
893- // this code always needs to run as root for the cgroup/udev setup,
894- // however for the tests we allow it to run as non-root
895- if(geteuid() != 0 && getenv("UBUNTU_CORE_LAUNCHER_NO_ROOT") == NULL) {
896- die("need to run as root or suid");
897- }
898-
899- if(geteuid() == 0) {
900-
901- // ensure we run in our own slave mount namespace, this will
902- // create a new mount namespace and make it a slave of "/"
903- //
904- // Note that this means that no mount actions inside our
905- // namespace are propagated to the main "/". We need this
906- // both for the private /tmp we create and for the bind
907- // mounts we do on a classic ubuntu system
908- //
909- // This also means you can't run an automount daemon unter
910- // this launcher
911- setup_slave_mount_namespace();
912-
913- // do the mounting if run on a non-native snappy system
914- if(is_running_on_classic_ubuntu()) {
915- setup_snappy_os_mounts();
916- }
917-
918- // set up private mounts
919- setup_private_mount(appname);
920-
921- // set up private /dev/pts
922- setup_private_pts();
923-
924- // this needs to happen as root
925- if(snappy_udev_setup_required(appname)) {
926- setup_devices_cgroup(appname);
927- setup_udev_snappy_assign(appname);
928- }
929-
930- // the rest does not so temporarily drop privs back to calling user
931- // (we'll permanently drop after loading seccomp)
932- if (setegid(real_gid) != 0)
933- die("setegid failed");
934- if (seteuid(real_uid) != 0)
935- die("seteuid failed");
936-
937- if(real_gid != 0 && geteuid() == 0)
938- die("dropping privs did not work");
939- if(real_uid != 0 && getegid() == 0)
940- die("dropping privs did not work");
941- }
942-
943- // Ensure that the user data path exists.
944- setup_user_data();
945-
946- // https://wiki.ubuntu.com/SecurityTeam/Specifications/SnappyConfinement
947-
948- int rc = 0;
949- // set apparmor rules
950- rc = aa_change_onexec(aa_profile);
951- if (rc != 0) {
952- if (getenv("SNAPPY_LAUNCHER_INSIDE_TESTS") == NULL)
953- die("aa_change_onexec failed with %i", rc);
954- }
955-
956- // set seccomp
957- rc = seccomp_load_filters(aa_profile);
958- if (rc != 0)
959- die("seccomp_load_filters failed with %i", rc);
960-
961- // Permanently drop if not root
962- if (geteuid() == 0) {
963- // Note that we do not call setgroups() here because its ok
964- // that the user keeps the groups he already belongs to
965- if (setgid(real_gid) != 0)
966- die("setgid failed");
967- if (setuid(real_uid) != 0)
968- die("setuid failed");
969-
970- if(real_gid != 0 && (getuid() == 0 || geteuid() == 0))
971- die("permanently dropping privs did not work");
972- if(real_uid != 0 && (getgid() == 0 || getegid() == 0))
973- die("permanently dropping privs did not work");
974- }
975-
976- // and exec the new binary
977- argv[NR_ARGS] = (char*)binary,
978- execv(binary, (char *const*)&argv[NR_ARGS]);
979- perror("execv failed");
980- return 1;
981+ const int NR_ARGS = 3;
982+ if (argc < NR_ARGS + 1)
983+ die("Usage: %s <appname> <apparmor> <binary>", argv[0]);
984+
985+ const char *appname = argv[1];
986+ const char *aa_profile = argv[2];
987+ const char *binary = argv[3];
988+ unsigned real_uid = getuid();
989+ unsigned real_gid = getgid();
990+
991+ if (!verify_appname(appname))
992+ die("appname %s not allowed", appname);
993+
994+ // this code always needs to run as root for the cgroup/udev setup,
995+ // however for the tests we allow it to run as non-root
996+ if (geteuid() != 0 && getenv("UBUNTU_CORE_LAUNCHER_NO_ROOT") == NULL) {
997+ die("need to run as root or suid");
998+ }
999+
1000+ if (geteuid() == 0) {
1001+
1002+ // ensure we run in our own slave mount namespace, this will
1003+ // create a new mount namespace and make it a slave of "/"
1004+ //
1005+ // Note that this means that no mount actions inside our
1006+ // namespace are propagated to the main "/". We need this
1007+ // both for the private /tmp we create and for the bind
1008+ // mounts we do on a classic ubuntu system
1009+ //
1010+ // This also means you can't run an automount daemon unter
1011+ // this launcher
1012+ setup_slave_mount_namespace();
1013+
1014+ // do the mounting if run on a non-native snappy system
1015+ if (is_running_on_classic_ubuntu()) {
1016+ setup_snappy_os_mounts();
1017+ }
1018+ // set up private mounts
1019+ setup_private_mount(appname);
1020+
1021+ // set up private /dev/pts
1022+ setup_private_pts();
1023+
1024+ // this needs to happen as root
1025+ if (snappy_udev_setup_required(appname)) {
1026+ setup_devices_cgroup(appname);
1027+ setup_udev_snappy_assign(appname);
1028+ }
1029+ // the rest does not so temporarily drop privs back to calling user
1030+ // (we'll permanently drop after loading seccomp)
1031+ if (setegid(real_gid) != 0)
1032+ die("setegid failed");
1033+ if (seteuid(real_uid) != 0)
1034+ die("seteuid failed");
1035+
1036+ if (real_gid != 0 && geteuid() == 0)
1037+ die("dropping privs did not work");
1038+ if (real_uid != 0 && getegid() == 0)
1039+ die("dropping privs did not work");
1040+ }
1041+ // Ensure that the user data path exists.
1042+ setup_user_data();
1043+
1044+ // https://wiki.ubuntu.com/SecurityTeam/Specifications/SnappyConfinement
1045+
1046+ int rc = 0;
1047+ // set apparmor rules
1048+ rc = aa_change_onexec(aa_profile);
1049+ if (rc != 0) {
1050+ if (getenv("SNAPPY_LAUNCHER_INSIDE_TESTS") == NULL)
1051+ die("aa_change_onexec failed with %i", rc);
1052+ }
1053+ // set seccomp
1054+ rc = seccomp_load_filters(aa_profile);
1055+ if (rc != 0)
1056+ die("seccomp_load_filters failed with %i", rc);
1057+
1058+ // Permanently drop if not root
1059+ if (geteuid() == 0) {
1060+ // Note that we do not call setgroups() here because its ok
1061+ // that the user keeps the groups he already belongs to
1062+ if (setgid(real_gid) != 0)
1063+ die("setgid failed");
1064+ if (setuid(real_uid) != 0)
1065+ die("setuid failed");
1066+
1067+ if (real_gid != 0 && (getuid() == 0 || geteuid() == 0))
1068+ die("permanently dropping privs did not work");
1069+ if (real_uid != 0 && (getgid() == 0 || getegid() == 0))
1070+ die("permanently dropping privs did not work");
1071+ }
1072+ // and exec the new binary
1073+ argv[NR_ARGS] = (char *)binary,
1074+ execv(binary, (char *const *)&argv[NR_ARGS]);
1075+ perror("execv failed");
1076+ return 1;
1077 }
1078
1079=== modified file 'src/seccomp.c'
1080--- src/seccomp.c 2016-03-21 17:22:39 +0000
1081+++ src/seccomp.c 2016-03-22 12:19:24 +0000
1082@@ -29,133 +29,140 @@
1083 char *filter_profile_dir = "/var/lib/snappy/seccomp/profiles/";
1084
1085 // strip whitespace from the end of the given string (inplace)
1086-size_t trim_right(char *s, size_t slen) {
1087- while(slen > 0 && isspace(s[slen - 1])) {
1088- s[--slen] = 0;
1089- }
1090- return slen;
1091+size_t trim_right(char *s, size_t slen)
1092+{
1093+ while (slen > 0 && isspace(s[slen - 1])) {
1094+ s[--slen] = 0;
1095+ }
1096+ return slen;
1097 }
1098
1099 int seccomp_load_filters(const char *filter_profile)
1100 {
1101- debug("seccomp_load_filters %s", filter_profile);
1102- int rc = 0;
1103- int syscall_nr = -1;
1104- scmp_filter_ctx ctx = NULL;
1105- FILE *f = NULL;
1106- size_t lineno = 0;
1107-
1108- ctx = seccomp_init(SCMP_ACT_KILL);
1109- if (ctx == NULL)
1110- return ENOMEM;
1111-
1112- // Disable NO_NEW_PRIVS because it interferes with exec transitions in
1113- // AppArmor. Unfortunately this means that security policies must be very
1114- // careful to not allow the following otherwise apps can escape the snadbox:
1115- // - seccomp syscall
1116- // - prctl with PR_SET_SECCOMP
1117- // - ptrace (trace) in AppArmor
1118- // - capability sys_admin in AppArmor
1119- // Note that with NO_NEW_PRIVS disabled, CAP_SYS_ADMIN is required to change
1120- // the seccomp sandbox.
1121- if (getenv("UBUNTU_CORE_LAUNCHER_NO_ROOT") == NULL) {
1122- rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0);
1123- if (rc != 0) {
1124- fprintf(stderr, "Cannot disable nnp\n");
1125- return -1;
1126- }
1127- }
1128-
1129- if (getenv("SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR") != NULL)
1130- filter_profile_dir = getenv("SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR");
1131-
1132- char profile_path[128];
1133- if (snprintf(profile_path, sizeof(profile_path), "%s/%s", filter_profile_dir, filter_profile) < 0) {
1134- goto out;
1135- }
1136-
1137- f = fopen(profile_path, "r");
1138- if (f == NULL) {
1139- fprintf(stderr, "Can not open %s (%s)\n", profile_path, strerror(errno));
1140- return -1;
1141- }
1142- // 80 characters + '\n' + '\0'
1143- char buf[82];
1144- while (fgets(buf, sizeof(buf), f) != NULL)
1145- {
1146- size_t len;
1147-
1148- lineno++;
1149-
1150- // comment, ignore
1151- if(buf[0] == '#')
1152- continue;
1153-
1154- // ensure the entire line was read
1155- len = strlen(buf);
1156- if (len == 0)
1157- continue;
1158- else if (buf[len - 1] != '\n' && len > (sizeof(buf) - 2)) {
1159- fprintf(stderr, "seccomp filter line %zu was too long (%zu characters max)\n", lineno, sizeof(buf) - 2);
1160- rc = -1;
1161- goto out;
1162- }
1163-
1164- // kill final newline
1165- len = trim_right(buf, len);
1166- if (len == 0)
1167- continue;
1168-
1169- // check for special "@unrestricted" command
1170- if (strncmp(buf, "@unrestricted", sizeof(buf)) == 0)
1171- goto out;
1172-
1173- // syscall not available on this arch/kernel
1174- // as this is a syscall whitelist its ok and the error can be ignored
1175- syscall_nr = seccomp_syscall_resolve_name(buf);
1176- if (syscall_nr == __NR_SCMP_ERROR)
1177- continue;
1178-
1179- // a normal line with a syscall
1180- rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, syscall_nr, 0);
1181- if (rc != 0) {
1182- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall_nr, 0);
1183- if (rc != 0) {
1184- fprintf(stderr, "seccomp_rule_add failed with %i for '%s'\n", rc, buf);
1185- goto out;
1186- }
1187- }
1188- }
1189-
1190- // raise privileges to load seccomp policy since we don't have nnp
1191- if (getenv("UBUNTU_CORE_LAUNCHER_NO_ROOT") == NULL) {
1192- if (seteuid(0) != 0)
1193- die("seteuid failed");
1194- if (geteuid() != 0)
1195- die("raising privs before seccomp_load did not work");
1196- }
1197-
1198- // load it into the kernel
1199- rc = seccomp_load(ctx);
1200-
1201- if (rc != 0) {
1202- fprintf(stderr, "seccomp_load failed with %i\n", rc);
1203- goto out;
1204- }
1205+ debug("seccomp_load_filters %s", filter_profile);
1206+ int rc = 0;
1207+ int syscall_nr = -1;
1208+ scmp_filter_ctx ctx = NULL;
1209+ FILE *f = NULL;
1210+ size_t lineno = 0;
1211+
1212+ ctx = seccomp_init(SCMP_ACT_KILL);
1213+ if (ctx == NULL)
1214+ return ENOMEM;
1215+
1216+ // Disable NO_NEW_PRIVS because it interferes with exec transitions in
1217+ // AppArmor. Unfortunately this means that security policies must be very
1218+ // careful to not allow the following otherwise apps can escape the snadbox:
1219+ // - seccomp syscall
1220+ // - prctl with PR_SET_SECCOMP
1221+ // - ptrace (trace) in AppArmor
1222+ // - capability sys_admin in AppArmor
1223+ // Note that with NO_NEW_PRIVS disabled, CAP_SYS_ADMIN is required to change
1224+ // the seccomp sandbox.
1225+ if (getenv("UBUNTU_CORE_LAUNCHER_NO_ROOT") == NULL) {
1226+ rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0);
1227+ if (rc != 0) {
1228+ fprintf(stderr, "Cannot disable nnp\n");
1229+ return -1;
1230+ }
1231+ }
1232+
1233+ if (getenv("SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR") != NULL)
1234+ filter_profile_dir =
1235+ getenv("SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR");
1236+
1237+ char profile_path[128];
1238+ if (snprintf
1239+ (profile_path, sizeof(profile_path), "%s/%s", filter_profile_dir,
1240+ filter_profile) < 0) {
1241+ goto out;
1242+ }
1243+
1244+ f = fopen(profile_path, "r");
1245+ if (f == NULL) {
1246+ fprintf(stderr, "Can not open %s (%s)\n", profile_path,
1247+ strerror(errno));
1248+ return -1;
1249+ }
1250+ // 80 characters + '\n' + '\0'
1251+ char buf[82];
1252+ while (fgets(buf, sizeof(buf), f) != NULL) {
1253+ size_t len;
1254+
1255+ lineno++;
1256+
1257+ // comment, ignore
1258+ if (buf[0] == '#')
1259+ continue;
1260+
1261+ // ensure the entire line was read
1262+ len = strlen(buf);
1263+ if (len == 0)
1264+ continue;
1265+ else if (buf[len - 1] != '\n' && len > (sizeof(buf) - 2)) {
1266+ fprintf(stderr,
1267+ "seccomp filter line %zu was too long (%zu characters max)\n",
1268+ lineno, sizeof(buf) - 2);
1269+ rc = -1;
1270+ goto out;
1271+ }
1272+ // kill final newline
1273+ len = trim_right(buf, len);
1274+ if (len == 0)
1275+ continue;
1276+
1277+ // check for special "@unrestricted" command
1278+ if (strncmp(buf, "@unrestricted", sizeof(buf)) == 0)
1279+ goto out;
1280+
1281+ // syscall not available on this arch/kernel
1282+ // as this is a syscall whitelist its ok and the error can be ignored
1283+ syscall_nr = seccomp_syscall_resolve_name(buf);
1284+ if (syscall_nr == __NR_SCMP_ERROR)
1285+ continue;
1286+
1287+ // a normal line with a syscall
1288+ rc = seccomp_rule_add_exact(ctx, SCMP_ACT_ALLOW, syscall_nr, 0);
1289+ if (rc != 0) {
1290+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall_nr,
1291+ 0);
1292+ if (rc != 0) {
1293+ fprintf(stderr,
1294+ "seccomp_rule_add failed with %i for '%s'\n",
1295+ rc, buf);
1296+ goto out;
1297+ }
1298+ }
1299+ }
1300+
1301+ // raise privileges to load seccomp policy since we don't have nnp
1302+ if (getenv("UBUNTU_CORE_LAUNCHER_NO_ROOT") == NULL) {
1303+ if (seteuid(0) != 0)
1304+ die("seteuid failed");
1305+ if (geteuid() != 0)
1306+ die("raising privs before seccomp_load did not work");
1307+ }
1308+ // load it into the kernel
1309+ rc = seccomp_load(ctx);
1310+
1311+ if (rc != 0) {
1312+ fprintf(stderr, "seccomp_load failed with %i\n", rc);
1313+ goto out;
1314+ }
1315
1316 out:
1317- // drop privileges again
1318- if (geteuid() == 0) {
1319- unsigned real_uid = getuid();
1320- if (seteuid(real_uid) != 0)
1321- die("seteuid failed");
1322- if (real_uid != 0 && geteuid() == 0)
1323- die("dropping privs after seccomp_load did not work");
1324- }
1325+ // drop privileges again
1326+ if (geteuid() == 0) {
1327+ unsigned real_uid = getuid();
1328+ if (seteuid(real_uid) != 0)
1329+ die("seteuid failed");
1330+ if (real_uid != 0 && geteuid() == 0)
1331+ die("dropping privs after seccomp_load did not work");
1332+ }
1333
1334- if (f != NULL) {
1335- fclose(f);
1336- }
1337- seccomp_release(ctx);
1338- return rc;
1339+ if (f != NULL) {
1340+ fclose(f);
1341+ }
1342+ seccomp_release(ctx);
1343+ return rc;
1344 }
1345
1346=== modified file 'src/utils.c'
1347--- src/utils.c 2015-06-11 10:57:01 +0000
1348+++ src/utils.c 2016-03-22 12:19:24 +0000
1349@@ -24,64 +24,66 @@
1350
1351 void die(const char *msg, ...)
1352 {
1353- va_list va;
1354- va_start(va, msg);
1355- vfprintf(stderr, msg, va);
1356- va_end(va);
1357+ va_list va;
1358+ va_start(va, msg);
1359+ vfprintf(stderr, msg, va);
1360+ va_end(va);
1361
1362- if (errno != 0) {
1363- perror(". errmsg");
1364- } else {
1365- fprintf(stderr, "\n");
1366- }
1367- exit(1);
1368+ if (errno != 0) {
1369+ perror(". errmsg");
1370+ } else {
1371+ fprintf(stderr, "\n");
1372+ }
1373+ exit(1);
1374 }
1375
1376 bool error(const char *msg, ...)
1377 {
1378- va_list va;
1379- va_start(va, msg);
1380- vfprintf(stderr, msg, va);
1381- va_end(va);
1382+ va_list va;
1383+ va_start(va, msg);
1384+ vfprintf(stderr, msg, va);
1385+ va_end(va);
1386
1387- return false;
1388+ return false;
1389 }
1390
1391 void debug(const char *msg, ...)
1392 {
1393- if(getenv("UBUNTU_CORE_LAUNCHER_DEBUG") == NULL)
1394- return;
1395-
1396- va_list va;
1397- va_start(va, msg);
1398- fprintf(stderr, "DEBUG: ");
1399- vfprintf(stderr, msg, va);
1400- fprintf(stderr, "\n");
1401- va_end(va);
1402-}
1403-
1404-void write_string_to_file(const char *filepath, const char *buf) {
1405- debug("write_string_to_file %s %s", filepath, buf);
1406- FILE *f = fopen(filepath, "w");
1407- if (f == NULL)
1408- die("fopen %s failed", filepath);
1409- if (fwrite(buf, strlen(buf), 1, f) != 1)
1410- die("fwrite failed");
1411- if (fflush(f) != 0)
1412- die("fflush failed");
1413- fclose(f);
1414-}
1415-
1416-int must_snprintf(char *str, size_t size, const char *format, ...) {
1417- int n = -1;
1418-
1419- va_list va;
1420- va_start(va, format);
1421- n = vsnprintf(str, size, format, va);
1422- va_end(va);
1423-
1424- if(n < 0 || n >= size)
1425- die("failed to snprintf %s", str);
1426-
1427- return n;
1428+ if (getenv("UBUNTU_CORE_LAUNCHER_DEBUG") == NULL)
1429+ return;
1430+
1431+ va_list va;
1432+ va_start(va, msg);
1433+ fprintf(stderr, "DEBUG: ");
1434+ vfprintf(stderr, msg, va);
1435+ fprintf(stderr, "\n");
1436+ va_end(va);
1437+}
1438+
1439+void write_string_to_file(const char *filepath, const char *buf)
1440+{
1441+ debug("write_string_to_file %s %s", filepath, buf);
1442+ FILE *f = fopen(filepath, "w");
1443+ if (f == NULL)
1444+ die("fopen %s failed", filepath);
1445+ if (fwrite(buf, strlen(buf), 1, f) != 1)
1446+ die("fwrite failed");
1447+ if (fflush(f) != 0)
1448+ die("fflush failed");
1449+ fclose(f);
1450+}
1451+
1452+int must_snprintf(char *str, size_t size, const char *format, ...)
1453+{
1454+ int n = -1;
1455+
1456+ va_list va;
1457+ va_start(va, format);
1458+ n = vsnprintf(str, size, format, va);
1459+ va_end(va);
1460+
1461+ if (n < 0 || n >= size)
1462+ die("failed to snprintf %s", str);
1463+
1464+ return n;
1465 }
1466
1467=== modified file 'src/utils.h'
1468--- src/utils.h 2015-05-20 11:10:29 +0000
1469+++ src/utils.h 2016-03-22 12:19:24 +0000
1470@@ -20,19 +20,19 @@
1471 #define CORE_LAUNCHER_UTILS_H
1472
1473 __attribute__ ((noreturn))
1474-__attribute__ ((format (printf, 1, 2)))
1475+ __attribute__ ((format(printf, 1, 2)))
1476 void die(const char *fmt, ...);
1477
1478-__attribute__ ((format (printf, 1, 2)))
1479+__attribute__ ((format(printf, 1, 2)))
1480 bool error(const char *fmt, ...);
1481
1482-__attribute__ ((format (printf, 1, 2)))
1483+__attribute__ ((format(printf, 1, 2)))
1484 void debug(const char *fmt, ...);
1485
1486 void write_string_to_file(const char *filepath, const char *buf);
1487
1488 // snprintf version that dies on any error condition
1489-__attribute__ ((format (printf, 3, 4)))
1490+__attribute__ ((format(printf, 3, 4)))
1491 int must_snprintf(char *str, size_t size, const char *format, ...);
1492
1493 #endif

Subscribers

People subscribed via source and target branches