Merge lp:~chipaca/snap-confine/unshare into lp:~snappy-dev/snap-confine/trunk

Proposed by John Lenton
Status: Merged
Merged at revision: 63
Proposed branch: lp:~chipaca/snap-confine/unshare
Merge into: lp:~snappy-dev/snap-confine/trunk
Diff against target: 173 lines (+68/-10)
5 files modified
debian/usr.bin.ubuntu-core-launcher (+7/-0)
src/Makefile (+1/-1)
src/main.c (+38/-1)
src/utils.c (+13/-7)
src/utils.h (+9/-1)
To merge this branch: bzr merge lp:~chipaca/snap-confine/unshare
Reviewer Review Type Date Requested Status
Tyler Hicks (community) Approve
Michael Vogt (community) Approve
Jamie Strandboge Pending
Review via email: mp+258367@code.launchpad.net

Commit message

Set up a private mount namespace for /tmp.

Description of the change

Missing here:
 * tests. Not sure if we can test this; you need CAP_SYS_ADMIN to call unshare().
 * apparmor or seccomp or something is blocking this. Need jdstrand's eyes.

To post a comment you must log in.
lp:~chipaca/snap-confine/unshare updated
64. By John Lenton

move setup_private_mount to the right place

Revision history for this message
Sergio Schvezov (sergiusens) wrote :

one small comment

Revision history for this message
John Lenton (chipaca) :
lp:~chipaca/snap-confine/unshare updated
65. By John Lenton

made a comment about kernel and libc versions

66. By John Lenton

"are require" indeed

67. By John Lenton

aslo commented on MS_ vars

Revision history for this message
John Lenton (chipaca) wrote :

One thing this branch does, without mentioning it (until now), is that if something fails you now get to see the error string and not just the error code.

You're welcome.

lp:~chipaca/snap-confine/unshare updated
68. By John Lenton

fixes to apparmor profile as per jdstrand

Revision history for this message
John Lenton (chipaca) wrote :

The DENIEDs you get without the changes suggested by jdstrand are:

without the first (rw, private) one:

audit: type=1400 audit(1431102661.774:53): apparmor="DENIED" operation="mount" info="failed mntpnt match" error=-13 profile="/usr/bin/ubuntu-core-launcher" name="/tmp/" pid=1928 comm="ubuntu-core-lau" flags="rw, private"

without the second (rw, bind) one:

audit: type=1400 audit(1431103430.829:55): apparmor="DENIED" operation="mount" info="failed flags match" error=-13 profile="/usr/bin/ubuntu-core-launcher" name="/tmp/" pid=1942 comm="ubuntu-core-lau" srcname="/tmp/snaps.1000/hello-world.canonical/1.0.15/tmp/" flags="rw, bind"

Revision history for this message
Michael Vogt (mvo) wrote :

Yay, nice! Code looks good!

review: Approve
Revision history for this message
Tyler Hicks (tyhicks) wrote :

I have an (inline) question about the use of stat(2).

review: Needs Information
Revision history for this message
Tyler Hicks (tyhicks) wrote :

The changes in this MP can be used by an unprivileged user to grant access to files/directories that are not intended to be reachable by that user.

For example, assume that mode on /root is set to 700, meaning that an unprivileged user cannot enter the /root directory. If a user running ubuntu-core-launcher sets SNAP_APP_TMPDIR to /root/foo, ubuntu-core-launcher will fail if /root/foo does not exist. However, it will succeed when /root/foo does exist.

Also, if /root/foo/bar was (lazily) set to be world-readable, then it could opened for reading at /tmp/bar.

review: Needs Fixing
Revision history for this message
John Lenton (chipaca) wrote :

Good catch. I'll change it so that the launcher deduces the tmpdir from the app name.

lp:~chipaca/snap-confine/unshare updated
69. By John Lenton

fixed issues found in review (and then some).

Revision history for this message
John Lenton (chipaca) wrote :

Just pushed a thing that deduces the tmpdir from appname+version and creates it.

This means we need to change the wrapper scripts, but also that the wrapper script can do less.

In a future branch, we should change the wrapper scripts one (last?) more time, to pass all the information in explicitly and have the launcher create the right environ from scratch.

Also, we should probably make the tests work again.

Revision history for this message
Tyler Hicks (tyhicks) wrote :

I didn't get to finish my review before my EOD but I have found an issue where an unprivileged user can still traverse paths that they shouldn't have access to and, at least, have arbitrary directories created:

tyhicks$ mkdir /tmp/snaps.1000
tyhicks$ ln -s /root /tmp/snaps.1000/cat
tyhicks$ sudo ls /root
tyhicks$ ./ubuntu-core-launcher cat BAD apparmor /apps/cat/cat
unable to make /tmp/ private. errmsg: Invalid argument
tyhicks$ sudo ls /root
BAD

I'll look some more tomorrow.

review: Needs Fixing
Revision history for this message
Tyler Hicks (tyhicks) wrote :

Note that the above issue is due to two things:

1) Predictable directory names used in /tmp
2) Continuing if mkdir(2) returns an error with errno set to EEXIST

This allows attackers to create symlinks in /tmp that the launcher follows.

lp:~chipaca/snap-confine/unshare updated
70. By John Lenton

moved back to not requiring the version; use mkdtemp to create the tempdir

71. By John Lenton

drop now spurious includes

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Hi John - This code is looking much more simple now. Thanks for all of the changes.

I have two small changes that need to be made (mentioned inline) and one remaining question.

How are all of the /tmp/snap.UID_appname_XXXXXX directories supposed to be cleaned up after the snap exits?

review: Needs Fixing
Revision history for this message
Tyler Hicks (tyhicks) wrote :

Here are the changes need to make the apparmor profile work (requires the two inline changes that I mentioned):

=== modified file 'debian/usr.bin.ubuntu-core-launcher'
--- debian/usr.bin.ubuntu-core-launcher 2015-05-08 16:50:56 +0000
+++ debian/usr.bin.ubuntu-core-launcher 2015-05-20 20:12:09 +0000
@@ -50,7 +50,10 @@
     # read apparmor to figure out if we need cgroups
     /var/lib/apparmor/clicks/* r,

- # make /tmp/ private, and bind-mount a private /tmp
+ # set up snap-specific private /tmp dir
+ capability chown,
+ /tmp/ w,
+ /tmp/snap.*/ w,
     mount options=(rw private) -> /tmp/,
- mount options=(rw bind) /tmp/snaps.[0-9]*/**/tmp/ -> /tmp/,
+ mount options=(rw bind) /tmp/snap.*/ -> /tmp/,
 }

lp:~chipaca/snap-confine/unshare updated
72. By John Lenton

address issues raised by tyhicks in review

Revision history for this message
John Lenton (chipaca) wrote :

Thank you for the review!
Fixed the missing /tmp/ (d'oh), moved the chown (good idea! added a comment so we remember why), done the changes to the apparmor profile.

I suspect the cleanest way to go about clenaing up /tmp/ is to have something like tmpreaper, but we haven't really discussed it yet.

Revision history for this message
Tyler Hicks (tyhicks) wrote :

This looks good to me. As you mentioned, there is the outstanding issue of /tmp/snap.* directories being left behind. This could result in a denial of service if mkdtemp() can no longer create a unique directory but I'm not too worried about that in terms of security. It is more of a usability issue, IMO. This gets my ack.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/usr.bin.ubuntu-core-launcher'
2--- debian/usr.bin.ubuntu-core-launcher 2015-04-22 15:32:05 +0000
3+++ debian/usr.bin.ubuntu-core-launcher 2015-05-20 21:14:43 +0000
4@@ -49,4 +49,11 @@
5
6 # read apparmor to figure out if we need cgroups
7 /var/lib/apparmor/clicks/* r,
8+
9+ # set up snap-specific private /tmp dir
10+ capability chown,
11+ /tmp/ w,
12+ /tmp/snap.*/ w,
13+ mount options=(rw private) -> /tmp/,
14+ mount options=(rw bind) /tmp/snap.*/ -> /tmp/,
15 }
16
17=== modified file 'src/Makefile'
18--- src/Makefile 2015-04-18 14:53:25 +0000
19+++ src/Makefile 2015-05-20 21:14:43 +0000
20@@ -1,5 +1,5 @@
21
22-CFLAGS= -O2 -Wall -Werror $(shell dpkg-buildflags --get CFLAGS)
23+CFLAGS= -D_GNU_SOURCE -O2 -Wall -Werror $(shell dpkg-buildflags --get CFLAGS)
24 LD_FLAGS = $(shell dpkg-buildflags --get LDFLAGS)
25 LIBS = -lapparmor -lseccomp -ludev
26
27
28=== modified file 'src/main.c'
29--- src/main.c 2015-04-23 12:03:47 +0000
30+++ src/main.c 2015-05-20 21:14:43 +0000
31@@ -38,6 +38,8 @@
32 #include "utils.h"
33 #include "seccomp.h"
34
35+#define MAX_BUF 1000
36+
37 bool verify_appname(const char *appname) {
38 // these chars are allowed in a appname
39 const char* whitelist_re = "^[a-z0-9][a-z0-9+._-]+$";
40@@ -198,6 +200,38 @@
41 return false;
42 }
43
44+void setup_private_mount(const char* appname) {
45+ uid_t uid = getuid();
46+ gid_t gid = getgid();
47+ char tmpdir[MAX_BUF] = {0};
48+
49+ must_snprintf(tmpdir, MAX_BUF, "/tmp/snap.%d_%s_XXXXXX", uid, appname);
50+ if (mkdtemp(tmpdir) == NULL) {
51+ die("unable to create tmpdir");
52+ }
53+
54+ // unshare() and CLONE_NEWNS require linux >= 2.6.16 and glibc >= 2.14
55+ // if using an older glibc, you'd need -D_BSD_SOURCE or -D_SVID_SORUCE.
56+ if (unshare(CLONE_NEWNS) < 0) {
57+ die("unable to set up mount namespace");
58+ }
59+
60+ // MS_PRIVATE needs linux > 2.6.11
61+ if (mount("none", "/tmp", NULL, MS_PRIVATE, NULL) != 0) {
62+ die("unable to make /tmp/ private");
63+ }
64+
65+ // MS_BIND is there from linux 2.4
66+ if (mount(tmpdir, "/tmp", NULL, MS_BIND, NULL) != 0) {
67+ die("unable to bind private /tmp");
68+ }
69+
70+ // do the chown after the bind mount to avoid potential shenanigans
71+ if (chown("/tmp/", uid, gid) < 0) {
72+ die("unable to chown tmpdir");
73+ }
74+}
75+
76 int main(int argc, char **argv)
77 {
78 const int NR_ARGS = 3;
79@@ -226,6 +260,9 @@
80 }
81
82 if(geteuid() == 0) {
83+ // set up private mounts
84+ setup_private_mount(appname);
85+
86 // this needs to happen as root
87 if(snappy_udev_setup_required(appname)) {
88 setup_devices_cgroup(appname);
89@@ -241,7 +278,7 @@
90 if (setgid(real_gid) != 0)
91 die("setgid failed");
92 if (setuid(real_uid) != 0)
93- die("seteuid failed");
94+ die("setuid failed");
95
96 if(real_gid != 0 && (getuid() == 0 || geteuid() == 0))
97 die("dropping privs did not work");
98
99=== modified file 'src/utils.c'
100--- src/utils.c 2015-04-23 10:20:17 +0000
101+++ src/utils.c 2015-05-20 21:14:43 +0000
102@@ -14,11 +14,11 @@
103 * along with this program. If not, see <http://www.gnu.org/licenses/>.
104 *
105 */
106-#include<stdio.h>
107-#include<stdlib.h>
108-#include<stdarg.h>
109-#include<string.h>
110-#include<stdio.h>
111+#include <errno.h>
112+#include <stdarg.h>
113+#include <stdio.h>
114+#include <stdlib.h>
115+#include <string.h>
116
117 #include "utils.h"
118
119@@ -29,7 +29,11 @@
120 vfprintf(stderr, msg, va);
121 va_end(va);
122
123- fprintf(stderr, "\n");
124+ if (errno != 0) {
125+ perror(". errmsg");
126+ } else {
127+ fprintf(stderr, "\n");
128+ }
129 exit(1);
130 }
131
132@@ -68,7 +72,7 @@
133 fclose(f);
134 }
135
136-void must_snprintf(char *str, size_t size, const char *format, ...) {
137+int must_snprintf(char *str, size_t size, const char *format, ...) {
138 int n = -1;
139
140 va_list va;
141@@ -78,4 +82,6 @@
142
143 if(n < 0 || n >= size)
144 die("failed to snprintf %s", str);
145+
146+ return n;
147 }
148
149=== modified file 'src/utils.h'
150--- src/utils.h 2015-04-23 10:20:17 +0000
151+++ src/utils.h 2015-05-20 21:14:43 +0000
152@@ -19,12 +19,20 @@
153 #ifndef CORE_LAUNCHER_UTILS_H
154 #define CORE_LAUNCHER_UTILS_H
155
156+__attribute__ ((noreturn))
157+__attribute__ ((format (printf, 1, 2)))
158 void die(const char *fmt, ...);
159+
160+__attribute__ ((format (printf, 1, 2)))
161 bool error(const char *fmt, ...);
162+
163+__attribute__ ((format (printf, 1, 2)))
164 void debug(const char *fmt, ...);
165+
166 void write_string_to_file(const char *filepath, const char *buf);
167
168 // snprintf version that dies on any error condition
169-void must_snprintf(char *str, size_t size, const char *format, ...);
170+__attribute__ ((format (printf, 3, 4)))
171+int must_snprintf(char *str, size_t size, const char *format, ...);
172
173 #endif

Subscribers

People subscribed via source and target branches