Add support for mount profiles #51

Merged
merged 30 commits into from Jun 30, 2016
Commits
Jump to file or symbol
Failed to load files and symbols.
+246 −7
Split
@@ -69,6 +69,9 @@
# reading seccomp filters
/{tmp/snap.rootfs_*/,}var/lib/snapd/seccomp/profiles/* r,
+
+ # reading mount profiles
+ /{tmp/snap.rootfs_*/,}var/lib/snapd/mount/*.fstab r,
# set up snap-specific private /tmp dir
capability chown,
@@ -0,0 +1,13 @@
+summary: Check that basic install works
+# This is blacklisted on debian because we first have to get the dpkg-vendor patches
+systems: [-debian-8]
+restore: |
+ snap remove hello-world
+execute: |
+ cd /
+ echo Run some hello-world stuff
+ snap install hello-world
+ hello-world.echo | grep Hello
+ hello-world.env | grep SNAP_NAME=hello-world
+ echo Ensure that we get an error if hello-world.evil does not return an error
+ if hello-world.evil; then exit 1; fi
@@ -0,0 +1,21 @@
+summary: Check that missing destination directory aborts mount processing
+# This is blacklisted on debian because we first have to get the dpkg-vendor patches
+systems: [-debian-8]
+restore: |
+ snap remove snapd-hacker-toolbelt
+ rm -rf /var/snap/snapd-hacker-toolbelt
+ rm -f /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+execute: |
+ echo "Having installed the snapd-hacker-toolbelt snap"
+ snap list | grep -q snapd-hacker-toolbelt || snap install snapd-hacker-toolbelt
+
+ echo "We can change its mount profile externally to create a read-only bind-mount"
+ echo "/var/snap/snapd-hacker-toolbelt/common/src -> /var/snap/snapd-hacker-toolbelt/common/dst"
+ mkdir -p /var/lib/snapd/mount
+ echo "/var/snap/snapd-hacker-toolbelt/common/src /var/snap/snapd-hacker-toolbelt/common/dst none bind,ro 0 0" > /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+
+ echo "We can now create the source directory, missing the destination directory"
+ mkdir -p /var/snap/snapd-hacker-toolbelt/common/src
+
+ echo "We can now run busybox.true and expect it to fail"
+ ( cd / && ! /snap/bin/snapd-hacker-toolbelt.busybox true )
@@ -0,0 +1,21 @@
+summary: Check that missing source directory aborts mount processing
+# This is blacklisted on debian because we first have to get the dpkg-vendor patches
+systems: [-debian-8]
+restore: |
+ snap remove snapd-hacker-toolbelt
+ rm -rf /var/snap/snapd-hacker-toolbelt
+ rm -f /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+execute: |
+ echo "Having installed the snapd-hacker-toolbelt snap"
+ snap list | grep -q snapd-hacker-toolbelt || snap install snapd-hacker-toolbelt
+
+ echo "We can change its mount profile externally to create a read-only bind-mount"
+ echo "/var/snap/snapd-hacker-toolbelt/common/src -> /var/snap/snapd-hacker-toolbelt/common/dst"
+ mkdir -p /var/lib/snapd/mount
+ echo "/var/snap/snapd-hacker-toolbelt/common/src /var/snap/snapd-hacker-toolbelt/common/dst none bind,ro 0 0" > /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+
+ echo "We can now create the destination directory, missing the source directory"
+ mkdir -p /var/snap/snapd-hacker-toolbelt/common/dst
+
+ echo "We can now run busybox.true and expect it to fail"
+ ( cd / && ! /snap/bin/snapd-hacker-toolbelt.busybox true )
@@ -0,0 +1,20 @@
+summary: Check that mount profiles cannot be used to mount tmpfs
+# This is blacklisted on debian because we first have to get the dpkg-vendor patches
+systems: [-debian-8]
+restore: |
+ snap remove snapd-hacker-toolbelt
+ rm -rf /var/snap/snapd-hacker-toolbelt
+ rm -f /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+execute: |
+ echo "Having installed the snapd-hacker-toolbelt snap"
+ snap list | grep -q snapd-hacker-toolbelt || snap install snapd-hacker-toolbelt
+
+ echo "We can change its mount profile externally to mount tmpfs at /var/snap/snapd-hacker-toolbelt/mnt"
+ mkdir -p /var/lib/snapd/mount
+ echo "none /var/snap/snapd-hacker-toolbelt/common/mnt tmpfs rw 0 0" > /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+
+ echo "We can now create the test mount directory"
+ mkdir -p /var/snap/snapd-hacker-toolbelt/common/mnt
+
+ echo "We can now run busybox.true and expect it to fail"
+ ( cd / && ! /snap/bin/snapd-hacker-toolbelt.busybox true )
@@ -0,0 +1,26 @@
+summary: Check that read-only bind mounts can be created
+# This is blacklisted on debian because we first have to get the dpkg-vendor patches
+systems: [-debian-8]
+restore: |
+ snap remove snapd-hacker-toolbelt
+ rm -rf /var/snap/snapd-hacker-toolbelt
+ rm -f /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+execute: |
+ echo "Having installed the snapd-hacker-toolbelt snap"
+ snap list | grep -q snapd-hacker-toolbelt || snap install snapd-hacker-toolbelt
+
+ echo "We can change its mount profile externally to create a read-only bind-mount"
+ echo "/var/snap/snapd-hacker-toolbelt/common/src -> /var/snap/snapd-hacker-toolbelt/common/dst"
+ mkdir -p /var/lib/snapd/mount
+ echo "/var/snap/snapd-hacker-toolbelt/common/src /var/snap/snapd-hacker-toolbelt/common/dst none bind,ro 0 0" > /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+
+ echo "We can now create both test directories"
+ mkdir -p /var/snap/snapd-hacker-toolbelt/common/src
+ mkdir -p /var/snap/snapd-hacker-toolbelt/common/dst
+
+ echo "And put a canary file with a random value inside"
+ value="canary-$(dd if=/dev/urandom bs=4 count=1 2>/dev/null | od -A none -t x4 | cut -f 2 -d ' ')"
+ echo "$value" > /var/snap/snapd-hacker-toolbelt/common/src/canary
+
+ echo "We can now run busybox.cat from the destination directory and expect the random value to match"
+ [ "$(cd / && /snap/bin/snapd-hacker-toolbelt.busybox cat /var/snap/snapd-hacker-toolbelt/common/dst/canary)" = "$value" ]
@@ -0,0 +1,28 @@
+summary: Check that write-only bind mounts can be created
+# This is blacklisted on debian because we first have to get the dpkg-vendor patches
+systems: [-debian-8]
+restore: |
+ snap remove snapd-hacker-toolbelt
+ rm -rf /var/snap/snapd-hacker-toolbelt
+ rm -f /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+execute: |
+ echo "Having installed the snapd-hacker-toolbelt snap"
+ snap list | grep -q snapd-hacker-toolbelt || snap install snapd-hacker-toolbelt
+
+ echo "We can change its mount profile externally to create a writable bind-mount"
+ echo "/var/snap/snapd-hacker-toolbelt/common/src -> /var/snap/snapd-hacker-toolbelt/common/dst"
+ mkdir -p /var/lib/snapd/mount
+ echo "/var/snap/snapd-hacker-toolbelt/common/src /var/snap/snapd-hacker-toolbelt/common/dst none bind,rw 0 0" > /var/lib/snapd/mount/snap.snapd-hacker-toolbelt.busybox.fstab
+
+ echo "We can now create both test directories"
+ mkdir -p /var/snap/snapd-hacker-toolbelt/common/src
+ mkdir -p /var/snap/snapd-hacker-toolbelt/common/dst
+ chmod 0777 /var/snap/snapd-hacker-toolbelt/common/dst
+
+ value="canary-$(dd if=/dev/urandom bs=4 count=1 2>/dev/null | od -A none -t x4 | cut -f 2 -d ' ')"
+
+ echo "We can now run busybox.tee to write to the file in the destination directory"
+ ( cd / && echo "$value" | /snap/bin/snapd-hacker-toolbelt.busybox tee /var/snap/snapd-hacker-toolbelt/common/dst/canary )
+
+ echo "And we should see the written value from the source directory"
+ [ "$(cat /var/snap/snapd-hacker-toolbelt/common/src/canary)" = "$value" ]
@@ -1,6 +1,9 @@
summary: Regression check for https://bugs.launchpad.net/snap-confine/+bug/1595444
# This is blacklisted on debian because we first have to get the dpkg-vendor patches
systems: [-debian-8]
+restore: |
+ snap remove snapd-hacker-toolbelt
+ rm -rf /var/snap/snapd-hacker-toolbelt
execute: |
echo "Having installed the snapd-hacker-toolbelt snap"
snap list | grep -q snapd-hacker-toolbelt || snap install snapd-hacker-toolbelt
@@ -1,4 +1,6 @@
summary: Check that ubuntu-core-launcher executes correctly
+# This is blacklisted on debian because we first have to get the dpkg-vendor patches
+systems: [-debian-8]
execute: |
echo "ubuntu-core-launcher is installed and responds to --help"
ubuntu-core-launcher --help 2>&1 | grep -F -q 'Usage: ubuntu-core-launcher <security-tag> <binary>'
View
@@ -2,15 +2,17 @@ project: snap-confine
environment:
REUSE_PROJECT: $(echo $REUSE_PROJECT)
+ PATH: /snap/bin:$PATH
backends:
linode:
key: $(echo $SPREAD_LINODE_KEY)
systems:
- ubuntu-16.04-64-grub
+ # - ubuntu-16.04-32-grub
- debian-8
-path: /remote/path/
+path: /root/spread/
exclude:
- .git
View
@@ -16,8 +16,22 @@
*/
#include <stdlib.h>
+#include <stdio.h>
+#include <mntent.h>
void sc_cleanup_string(char **ptr)
{
free(*ptr);
}
+
+void sc_cleanup_file(FILE ** ptr)
+{
+ if (*ptr != NULL)
+ fclose(*ptr);
+}
+
+void sc_cleanup_endmntent(FILE ** ptr)
+{
+ if (*ptr != NULL)
+ endmntent(*ptr);
+}
View
@@ -18,6 +18,8 @@
#ifndef SNAP_CONFINE_CLEANUP_FUNCS_H
#define SNAP_CONFINE_CLEANUP_FUNCS_H
+#include <stdlib.h>
+
/**
* Free a dynamically allocated string.
*
@@ -26,4 +28,20 @@
**/
void sc_cleanup_string(char **ptr);
+/**
+ * Close an open file.
+ *
+ * This function is designed to be used with
+ * __attribute__((cleanup(sc_cleanup_file))).
@tyhicks

tyhicks Jun 27, 2016

Collaborator

I'd suggest defining an easy-to-type macro for "attribute ((cleanup(sc_cleanup_endmntent)))". I think "autofclose" is descriptive.

+ **/
+void sc_cleanup_file(FILE ** ptr);
+
+/**
+ * Close an open file with endmntent(3)
+ *
+ * This function is designed to be used with
+ * __attribute__((cleanup(sc_cleanup_endmntent))).
+ **/
+void sc_cleanup_endmntent(FILE ** ptr);
+
#endif
View
@@ -114,6 +114,9 @@ int main(int argc, char **argv)
snappy_udev_cleanup(&udev_s);
#endif // ifdef STRICT_CONFINEMENT
+ // setup the security backend bind mounts
+ sc_setup_mount_profiles(appname);
+
// Try to re-locate back to vanilla working directory. This can fail
// because that directory is no longer present.
if (chdir(vanilla_cwd) != 0) {
View
@@ -30,11 +30,13 @@
#include <errno.h>
#include <sched.h>
#include <string.h>
+#include <mntent.h>
#include "utils.h"
#include "snap.h"
#include "classic.h"
#include "mount-support-nvidia.h"
+#include "cleanup-funcs.h"
#define MAX_BUF 1000
@@ -294,3 +296,55 @@ void setup_slave_mount_namespace()
die("can not make make / rslave");
}
}
+
+void sc_setup_mount_profiles(const char *appname)
+{
+ debug("%s: %s", __FUNCTION__, appname);
+
+ FILE *f __attribute__ ((cleanup(sc_cleanup_endmntent))) = NULL;
+ const char *mount_profile_dir = "/var/lib/snapd/mount";
+
+ char profile_path[PATH_MAX];
+ must_snprintf(profile_path, sizeof(profile_path), "%s/%s.fstab",
+ mount_profile_dir, appname);
+
+ debug("opening mount profile %s", profile_path);
+ f = setmntent(profile_path, "r");
+ // it is ok for the file to not exist
+ if (f == NULL && errno == ENOENT) {
+ debug("mount profile %s doesn't exist, ignoring", profile_path);
+ return;
+ }
+ // however any other error is a real error
+ if (f == NULL) {
+ die("cannot open %s", profile_path);
+ }
+
+ struct mntent *m = NULL;
+ while ((m = getmntent(f)) != NULL) {
+ debug("read mount entry\n"
+ "\tmnt_fsname: %s\n"
+ "\tmnt_dir: %s\n"
+ "\tmnt_type: %s\n"
+ "\tmnt_opts: %s\n"
+ "\tmnt_freq: %d\n"
+ "\tmnt_passno: %d",
+ m->mnt_fsname, m->mnt_dir, m->mnt_type,
+ m->mnt_opts, m->mnt_freq, m->mnt_passno);
+ int flags = MS_BIND | MS_RDONLY | MS_NODEV | MS_NOSUID;
+ debug("initial flags are: bind,ro,nodev,nosuid");
+ if (strcmp(m->mnt_type, "none") != 0) {
+ die("only 'none' filesystem type is supported");
+ }
+ if (hasmntopt(m, "bind") == NULL) {
+ die("the bind mount flag is mandatory");
+ }
+ if (hasmntopt(m, "rw") != NULL) {
+ flags &= ~MS_RDONLY;
+ }
+ if (mount(m->mnt_fsname, m->mnt_dir, NULL, flags, NULL) != 0) {
+ die("cannot mount %s at %s with options %s",
+ m->mnt_fsname, m->mnt_dir, m->mnt_opts);
+ }
+ }
+}
View
@@ -23,4 +23,19 @@ void setup_private_pts();
void setup_snappy_os_mounts();
void setup_slave_mount_namespace();
+/**
+ * Setup mount profiles as described by snapd.
+ *
+ * This function reads /var/lib/snapd/mount/$appname.fstab as a fstab(5) file
+ * and executes the mount requests described there.
+ *
+ * Currently only bind mounts are allowed. All bind mounts are read only by
+ * default though the `rw` flag can be used.
+ *
+ * This function is called with the rootfs being "consistent" so that it is
+ * either the core snap on an all-snap system or the core snap + punched holes
+ * on a classic system.
+ **/
+void sc_setup_mount_profiles(const char *appname);
+
#endif
View
@@ -524,12 +524,8 @@ void seccomp_load_filters(const char *filter_profile)
secure_getenv("SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR");
char profile_path[512]; // arbitrary path name limit
- int snprintf_rc = snprintf(profile_path, sizeof(profile_path), "%s/%s",
- filter_profile_dir, filter_profile);
- if (snprintf_rc < 0 || snprintf_rc >= 512) {
- errno = 0;
- die("snprintf returned unexpected value");
- }
+ must_snprintf(profile_path, sizeof(profile_path), "%s/%s",
+ filter_profile_dir, filter_profile);
f = fopen(profile_path, "r");
if (f == NULL) {