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
=== modified file 'debian/usr.bin.ubuntu-core-launcher'
--- debian/usr.bin.ubuntu-core-launcher 2015-04-22 15:32:05 +0000
+++ debian/usr.bin.ubuntu-core-launcher 2015-05-20 21:14:43 +0000
@@ -49,4 +49,11 @@
4949
50 # read apparmor to figure out if we need cgroups50 # read apparmor to figure out if we need cgroups
51 /var/lib/apparmor/clicks/* r,51 /var/lib/apparmor/clicks/* r,
52
53 # set up snap-specific private /tmp dir
54 capability chown,
55 /tmp/ w,
56 /tmp/snap.*/ w,
57 mount options=(rw private) -> /tmp/,
58 mount options=(rw bind) /tmp/snap.*/ -> /tmp/,
52}59}
5360
=== modified file 'src/Makefile'
--- src/Makefile 2015-04-18 14:53:25 +0000
+++ src/Makefile 2015-05-20 21:14:43 +0000
@@ -1,5 +1,5 @@
11
2CFLAGS= -O2 -Wall -Werror $(shell dpkg-buildflags --get CFLAGS)2CFLAGS= -D_GNU_SOURCE -O2 -Wall -Werror $(shell dpkg-buildflags --get CFLAGS)
3LD_FLAGS = $(shell dpkg-buildflags --get LDFLAGS)3LD_FLAGS = $(shell dpkg-buildflags --get LDFLAGS)
4LIBS = -lapparmor -lseccomp -ludev4LIBS = -lapparmor -lseccomp -ludev
55
66
=== modified file 'src/main.c'
--- src/main.c 2015-04-23 12:03:47 +0000
+++ src/main.c 2015-05-20 21:14:43 +0000
@@ -38,6 +38,8 @@
38#include "utils.h"38#include "utils.h"
39#include "seccomp.h"39#include "seccomp.h"
4040
41#define MAX_BUF 1000
42
41bool verify_appname(const char *appname) {43bool verify_appname(const char *appname) {
42 // these chars are allowed in a appname44 // these chars are allowed in a appname
43 const char* whitelist_re = "^[a-z0-9][a-z0-9+._-]+$";45 const char* whitelist_re = "^[a-z0-9][a-z0-9+._-]+$";
@@ -198,6 +200,38 @@
198 return false;200 return false;
199}201}
200202
203void setup_private_mount(const char* appname) {
204 uid_t uid = getuid();
205 gid_t gid = getgid();
206 char tmpdir[MAX_BUF] = {0};
207
208 must_snprintf(tmpdir, MAX_BUF, "/tmp/snap.%d_%s_XXXXXX", uid, appname);
209 if (mkdtemp(tmpdir) == NULL) {
210 die("unable to create tmpdir");
211 }
212
213 // unshare() and CLONE_NEWNS require linux >= 2.6.16 and glibc >= 2.14
214 // if using an older glibc, you'd need -D_BSD_SOURCE or -D_SVID_SORUCE.
215 if (unshare(CLONE_NEWNS) < 0) {
216 die("unable to set up mount namespace");
217 }
218
219 // MS_PRIVATE needs linux > 2.6.11
220 if (mount("none", "/tmp", NULL, MS_PRIVATE, NULL) != 0) {
221 die("unable to make /tmp/ private");
222 }
223
224 // MS_BIND is there from linux 2.4
225 if (mount(tmpdir, "/tmp", NULL, MS_BIND, NULL) != 0) {
226 die("unable to bind private /tmp");
227 }
228
229 // do the chown after the bind mount to avoid potential shenanigans
230 if (chown("/tmp/", uid, gid) < 0) {
231 die("unable to chown tmpdir");
232 }
233}
234
201int main(int argc, char **argv)235int main(int argc, char **argv)
202{236{
203 const int NR_ARGS = 3;237 const int NR_ARGS = 3;
@@ -226,6 +260,9 @@
226 }260 }
227261
228 if(geteuid() == 0) {262 if(geteuid() == 0) {
263 // set up private mounts
264 setup_private_mount(appname);
265
229 // this needs to happen as root266 // this needs to happen as root
230 if(snappy_udev_setup_required(appname)) {267 if(snappy_udev_setup_required(appname)) {
231 setup_devices_cgroup(appname);268 setup_devices_cgroup(appname);
@@ -241,7 +278,7 @@
241 if (setgid(real_gid) != 0)278 if (setgid(real_gid) != 0)
242 die("setgid failed");279 die("setgid failed");
243 if (setuid(real_uid) != 0)280 if (setuid(real_uid) != 0)
244 die("seteuid failed");281 die("setuid failed");
245282
246 if(real_gid != 0 && (getuid() == 0 || geteuid() == 0))283 if(real_gid != 0 && (getuid() == 0 || geteuid() == 0))
247 die("dropping privs did not work");284 die("dropping privs did not work");
248285
=== modified file 'src/utils.c'
--- src/utils.c 2015-04-23 10:20:17 +0000
+++ src/utils.c 2015-05-20 21:14:43 +0000
@@ -14,11 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *15 *
16 */16 */
17#include<stdio.h>17#include <errno.h>
18#include<stdlib.h>18#include <stdarg.h>
19#include<stdarg.h>19#include <stdio.h>
20#include<string.h>20#include <stdlib.h>
21#include<stdio.h>21#include <string.h>
2222
23#include "utils.h"23#include "utils.h"
2424
@@ -29,7 +29,11 @@
29 vfprintf(stderr, msg, va);29 vfprintf(stderr, msg, va);
30 va_end(va);30 va_end(va);
3131
32 fprintf(stderr, "\n");32 if (errno != 0) {
33 perror(". errmsg");
34 } else {
35 fprintf(stderr, "\n");
36 }
33 exit(1);37 exit(1);
34}38}
3539
@@ -68,7 +72,7 @@
68 fclose(f);72 fclose(f);
69}73}
7074
71void must_snprintf(char *str, size_t size, const char *format, ...) {75int must_snprintf(char *str, size_t size, const char *format, ...) {
72 int n = -1;76 int n = -1;
7377
74 va_list va;78 va_list va;
@@ -78,4 +82,6 @@
7882
79 if(n < 0 || n >= size)83 if(n < 0 || n >= size)
80 die("failed to snprintf %s", str);84 die("failed to snprintf %s", str);
85
86 return n;
81}87}
8288
=== modified file 'src/utils.h'
--- src/utils.h 2015-04-23 10:20:17 +0000
+++ src/utils.h 2015-05-20 21:14:43 +0000
@@ -19,12 +19,20 @@
19#ifndef CORE_LAUNCHER_UTILS_H19#ifndef CORE_LAUNCHER_UTILS_H
20#define CORE_LAUNCHER_UTILS_H20#define CORE_LAUNCHER_UTILS_H
2121
22__attribute__ ((noreturn))
23__attribute__ ((format (printf, 1, 2)))
22void die(const char *fmt, ...);24void die(const char *fmt, ...);
25
26__attribute__ ((format (printf, 1, 2)))
23bool error(const char *fmt, ...);27bool error(const char *fmt, ...);
28
29__attribute__ ((format (printf, 1, 2)))
24void debug(const char *fmt, ...);30void debug(const char *fmt, ...);
31
25void write_string_to_file(const char *filepath, const char *buf);32void write_string_to_file(const char *filepath, const char *buf);
2633
27// snprintf version that dies on any error condition34// snprintf version that dies on any error condition
28void must_snprintf(char *str, size_t size, const char *format, ...);35__attribute__ ((format (printf, 3, 4)))
36int must_snprintf(char *str, size_t size, const char *format, ...);
2937
30#endif38#endif

Subscribers

People subscribed via source and target branches