Version in base suite: 2.37.4-1 Base version: snapd_2.37.4-1 Target version: snapd_2.37.4-1+deb10u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/snapd/snapd_2.37.4-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/snapd/snapd_2.37.4-1+deb10u1.dsc changelog | 12 patches/cve202144730/0010-cmd-libsnap-confine-private-Fix-use-of-uninitialised.patch | 31 + patches/cve202144730/0011-cmd-libsnap-confine-private-Defend-against-hardlink-.patch | 147 ++++++++ patches/cve202144730/0012-cmd-libsnap-confine-private-Don-t-fail-open-on-appar.patch | 55 +++ patches/cve202144730/0013-cmd-libsnap-confine-private-Tighten-AppArmor-label-c.patch | 52 ++ patches/cve202144730/0014-cmd-snap-confine-Remove-execute-permission-from-AppA.patch | 36 ++ patches/cve202144730/0015-cmd-snap-confine-Prevent-user-controlled-race-in-set.patch | 174 ++++++++++ patches/series | 6 8 files changed, 513 insertions(+) diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar -> baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar -> baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar -> baz -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar -> baz -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/bar -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/baz -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/baz -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> bar: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> bar: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> bar -> baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> bar -> baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> bar -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> bar -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> baz: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> baz -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> baz -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/foo -> qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/m5ZK4we5ex/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/qux: Too many levels of symbolic links diff: /srv/release.debian.org/tmp/ADPeESbdp6/snapd-2.37.4/tests/lib/snaps/test-snapd-validate-container-failures/hell/qux: Too many levels of symbolic links diff -Nru snapd-2.37.4/debian/changelog snapd-2.37.4/debian/changelog --- snapd-2.37.4/debian/changelog 2019-02-28 17:21:26.000000000 +0000 +++ snapd-2.37.4/debian/changelog 2022-02-17 15:29:46.000000000 +0000 @@ -1,3 +1,15 @@ +snapd (2.37.4-1+deb10u1) buster-security; urgency=medium + + * SECURITY UPDATE: local privilege escalation + - d/p/cve202144730: Add validations of the + location of the snap-confine binary within snapd. + - d/p/cve202144730: Fix race condition in snap-confine + when preparing a private mount namespace for a snap. + - CVE-2021-44730 + - CVE-2021-44731 + + -- Michael Vogt Thu, 17 Feb 2022 16:29:46 +0100 + snapd (2.37.4-1) unstable; urgency=medium * New upstream release diff -Nru snapd-2.37.4/debian/patches/cve202144730/0010-cmd-libsnap-confine-private-Fix-use-of-uninitialised.patch snapd-2.37.4/debian/patches/cve202144730/0010-cmd-libsnap-confine-private-Fix-use-of-uninitialised.patch --- snapd-2.37.4/debian/patches/cve202144730/0010-cmd-libsnap-confine-private-Fix-use-of-uninitialised.patch 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.37.4/debian/patches/cve202144730/0010-cmd-libsnap-confine-private-Fix-use-of-uninitialised.patch 2022-02-17 12:28:49.000000000 +0000 @@ -0,0 +1,31 @@ +From ad67f006c42645a900485bca434f9f8179519dac Mon Sep 17 00:00:00 2001 +From: Alex Murray +Date: Wed, 17 Nov 2021 14:23:25 +1030 +Subject: [PATCH 10/36] cmd/libsnap-confine-private: Fix use of uninitialised + variable + +Ensure xdg_runtime_dir_env is zero initialisd in +sc_call_snap_update_ns_as_user() otherwise when XDG_RUNTIME_DIR is not +defined the uninitialised contents of this buffer will be passed to +snap-update-ns. This is unlikely to be an issue in practice as +snap-update-ns is quite defensive in terms of environment variable handing +already. + +Signed-off-by: Alex Murray +--- + cmd/libsnap-confine-private/tool.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: snapd-2.37.4/cmd/libsnap-confine-private/tool.c +=================================================================== +--- snapd-2.37.4.orig/cmd/libsnap-confine-private/tool.c ++++ snapd-2.37.4/cmd/libsnap-confine-private/tool.c +@@ -105,7 +105,7 @@ void sc_call_snap_update_ns_as_user(int + snap_name); + + const char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR"); +- char xdg_runtime_dir_env[PATH_MAX+strlen("XDG_RUNTIME_DIR=")]; ++ char xdg_runtime_dir_env[PATH_MAX+sizeof("XDG_RUNTIME_DIR=")] = { 0 }; + if (xdg_runtime_dir != NULL) { + sc_must_snprintf(xdg_runtime_dir_env, + sizeof(xdg_runtime_dir_env), diff -Nru snapd-2.37.4/debian/patches/cve202144730/0011-cmd-libsnap-confine-private-Defend-against-hardlink-.patch snapd-2.37.4/debian/patches/cve202144730/0011-cmd-libsnap-confine-private-Defend-against-hardlink-.patch --- snapd-2.37.4/debian/patches/cve202144730/0011-cmd-libsnap-confine-private-Defend-against-hardlink-.patch 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.37.4/debian/patches/cve202144730/0011-cmd-libsnap-confine-private-Defend-against-hardlink-.patch 2022-02-17 15:29:46.000000000 +0000 @@ -0,0 +1,147 @@ +From 54e71e7750f73a28f5a47fe04dd058360e24c0e9 Mon Sep 17 00:00:00 2001 +From: Alex Murray +Date: Wed, 17 Nov 2021 14:27:22 +1030 +Subject: [PATCH 11/36] cmd/libsnap-confine-private: Defend against hardlink + attacks + +When snap-confine goes to execute other helper binaries (snap-update-ns +etc) via sc_open_snapd_tool(), these other binaries are located relative to +the currently executing snap-confine process via /proc/self/exe. Since it +is possible for regular users to hardlink setuid binaries when +fs.protected_hardlinks is 0, it is possible to hardlink snap-confine to +another location and then place an attacker controlled binary in place of +snap-update-ns and have this executed as root by snap-confine. Protect +against this by checking that snap-confine is located either within +/usr/lib/snapd or within the core or snapd snaps as expected. + +This resolves CVE-2021-44730. + +Signed-off-by: Alex Murray +--- + cmd/libsnap-confine-private/tool.c | 11 ++++++-- + cmd/libsnap-confine-private/utils-test.c | 32 ++++++++++++++++++++++++ + cmd/libsnap-confine-private/utils.c | 13 ++++++++++ + cmd/libsnap-confine-private/utils.h | 6 +++++ + 4 files changed, 60 insertions(+), 2 deletions(-) + +Index: snapd-2.37.4/cmd/libsnap-confine-private/tool.c +=================================================================== +--- snapd-2.37.4.orig/cmd/libsnap-confine-private/tool.c ++++ snapd-2.37.4/cmd/libsnap-confine-private/tool.c +@@ -154,14 +154,21 @@ static int sc_open_snapd_tool(const char + // want to store the terminating '\0'. The readlink system call doesn't add + // terminating null, but our initialization of buf handles this for us. + char buf[PATH_MAX + 1] = { 0 }; +- if (readlink("/proc/self/exe", buf, sizeof buf) < 0) { ++ if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) < 0) { + die("cannot readlink /proc/self/exe"); + } + if (buf[0] != '/') { // this shouldn't happen, but make sure have absolute path + die("readlink /proc/self/exe returned relative path"); + } ++ // as we are looking up other tools relative to our own path, check ++ // we are located where we think we should be - otherwise we ++ // may have been hardlink'd elsewhere and then may execute the ++ // wrong tool as a result ++ if (!sc_is_expected_path(buf)) { ++ die("running from unexpected location: %s", buf); ++ } + char *dir_name = dirname(buf); +- int dir_fd SC_CLEANUP(sc_cleanup_close) = 1; ++ int dir_fd SC_CLEANUP(sc_cleanup_close) = -1; + dir_fd = open(dir_name, O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (dir_fd < 0) { + die("cannot open path %s", dir_name); +Index: snapd-2.37.4/cmd/libsnap-confine-private/utils-test.c +=================================================================== +--- snapd-2.37.4.orig/cmd/libsnap-confine-private/utils-test.c ++++ snapd-2.37.4/cmd/libsnap-confine-private/utils-test.c +@@ -71,6 +71,37 @@ static void test_parse_bool(void) + g_assert_cmpint(errno, ==, EFAULT); + } + ++static void test_sc_is_expected_path(void) ++{ ++ struct { ++ const char *path; ++ bool expected; ++ } test_cases[] = { ++ {"/tmp/snap-confine", false}, ++ {"/tmp/foo", false}, ++ {"/home/ ", false}, ++ {"/usr/lib/snapd/snap-confine1", false}, ++ {"/usr/lib/snapd/snap—confine", false}, ++ {"/snap/core/usr/lib/snapd/snap-confine", false}, ++ {"/snap/core/x1x/usr/lib/snapd/snap-confine", false}, ++ {"/snap/core/z1/usr/lib/snapd/snap-confine", false}, ++ {"/snap/cꓳre/1/usr/lib/snapd/snap-confine", false}, ++ {"/snap/snapd1/1/usr/lib/snapd/snap-confine", false}, ++ {"/snap/core/current/usr/lib/snapd/snap-confine", false}, ++ {"/usr/lib/snapd/snap-confine", true}, ++ {"/usr/libexec/snapd/snap-confine", true}, ++ {"/snap/core/1/usr/lib/snapd/snap-confine", true}, ++ {"/snap/core/x1/usr/lib/snapd/snap-confine", true}, ++ {"/snap/snapd/1/usr/lib/snapd/snap-confine", true}, ++ {"/snap/snapd/1/usr/libexec/snapd/snap-confine", false}, ++ }; ++ size_t i; ++ for (i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) { ++ bool result = sc_is_expected_path(test_cases[i].path); ++ g_assert_cmpint(result, ==, test_cases[i].expected); ++ } ++} ++ + static void test_die(void) + { + if (g_test_subprocess()) { +@@ -194,6 +225,7 @@ static void test_sc_nonfatal_mkpath__abs + static void __attribute__ ((constructor)) init(void) + { + g_test_add_func("/utils/parse_bool", test_parse_bool); ++ g_test_add_func("/utils/sc_is_expected_path", test_sc_is_expected_path); + g_test_add_func("/utils/die", test_die); + g_test_add_func("/utils/die_with_errno", test_die_with_errno); + g_test_add_func("/utils/sc_nonfatal_mkpath/relative", +Index: snapd-2.37.4/cmd/libsnap-confine-private/utils.c +=================================================================== +--- snapd-2.37.4.orig/cmd/libsnap-confine-private/utils.c ++++ snapd-2.37.4/cmd/libsnap-confine-private/utils.c +@@ -16,6 +16,7 @@ + */ + #include + #include ++#include + #include + #include + #include +@@ -217,3 +218,15 @@ int sc_nonfatal_mkpath(const char *const + } + return 0; + } ++ ++bool sc_is_expected_path(const char *path) ++{ ++ const char *expected_path_re = ++ "^(/snap/(snapd|core)/x?[0-9]+/usr/lib|/usr/lib(exec)?)/snapd/snap-confine$"; ++ regex_t re; ++ if (regcomp(&re, expected_path_re, REG_EXTENDED | REG_NOSUB) != 0) ++ die("can not compile regex %s", expected_path_re); ++ int status = regexec(&re, path, 0, NULL, 0); ++ regfree(&re); ++ return status == 0; ++} +Index: snapd-2.37.4/cmd/libsnap-confine-private/utils.h +=================================================================== +--- snapd-2.37.4.orig/cmd/libsnap-confine-private/utils.h ++++ snapd-2.37.4/cmd/libsnap-confine-private/utils.h +@@ -60,4 +60,10 @@ void write_string_to_file(const char *fi + **/ + __attribute__ ((warn_unused_result)) + int sc_nonfatal_mkpath(const char *const path, mode_t mode); ++ ++/** ++ * Return true if path is a valid path for the snap-confine binary ++ **/ ++__attribute__((warn_unused_result)) ++bool sc_is_expected_path(const char *path); + #endif diff -Nru snapd-2.37.4/debian/patches/cve202144730/0012-cmd-libsnap-confine-private-Don-t-fail-open-on-appar.patch snapd-2.37.4/debian/patches/cve202144730/0012-cmd-libsnap-confine-private-Don-t-fail-open-on-appar.patch --- snapd-2.37.4/debian/patches/cve202144730/0012-cmd-libsnap-confine-private-Don-t-fail-open-on-appar.patch 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.37.4/debian/patches/cve202144730/0012-cmd-libsnap-confine-private-Don-t-fail-open-on-appar.patch 2022-02-17 12:24:51.000000000 +0000 @@ -0,0 +1,55 @@ +From c6011693a816f7f8a5b0c7858ddce91c6ef1a352 Mon Sep 17 00:00:00 2001 +From: Alex Murray +Date: Wed, 17 Nov 2021 14:32:09 +1030 +Subject: [PATCH 12/36] cmd/libsnap-confine-private: Don't fail open on + apparmor confinement + +aa_is_enabled() can be made to fail by setting low open file limits or +similar - in this case, snap-confine would continue executing as though it +were unconfined. However, this can be detected by checking errno more +closely - so only fail open when we know AppArmor either is not supported +or has been explicitly disabled at boot and otherwise fail closed. + +Signed-off-by: Alex Murray +--- + cmd/libsnap-confine-private/apparmor-support.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/cmd/libsnap-confine-private/apparmor-support.c b/cmd/libsnap-confine-private/apparmor-support.c +index eac0912d3e..9930e42168 100644 +--- a/cmd/libsnap-confine-private/apparmor-support.c ++++ b/cmd/libsnap-confine-private/apparmor-support.c +@@ -53,18 +53,24 @@ void sc_init_apparmor_support(struct sc_apparmor *apparmor) + debug + ("apparmor is available on the system but has been disabled at boot"); + break; +- case ENOENT: +- debug +- ("apparmor is available but the interface but the interface is not available"); +- break; + case EPERM: + // NOTE: fall-through + case EACCES: + debug + ("insufficient permissions to determine if apparmor is enabled"); +- break; ++ // since snap-confine is setuid root this should ++ // never happen so likely someone is trying to ++ // manipulate our execution environment - fail hard ++ ++ // fall-through ++ case ENOENT: ++ case ENOMEM: + default: +- debug("apparmor is not enabled: %s", strerror(errno)); ++ // this shouldn't happen under normal usage so it ++ // is possible someone is trying to manipulate our ++ // execution environment - fail hard ++ die("aa_is_enabled() failed unexpectedly (%s)", ++ strerror(errno)); + break; + } + apparmor->is_confined = false; +-- +2.32.0 + diff -Nru snapd-2.37.4/debian/patches/cve202144730/0013-cmd-libsnap-confine-private-Tighten-AppArmor-label-c.patch snapd-2.37.4/debian/patches/cve202144730/0013-cmd-libsnap-confine-private-Tighten-AppArmor-label-c.patch --- snapd-2.37.4/debian/patches/cve202144730/0013-cmd-libsnap-confine-private-Tighten-AppArmor-label-c.patch 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.37.4/debian/patches/cve202144730/0013-cmd-libsnap-confine-private-Tighten-AppArmor-label-c.patch 2022-02-17 12:24:53.000000000 +0000 @@ -0,0 +1,52 @@ +From 4a2eb78e56ca1ea689756c066034b9f95ce76c8e Mon Sep 17 00:00:00 2001 +From: Alex Murray +Date: Wed, 17 Nov 2021 14:37:39 +1030 +Subject: [PATCH 13/36] cmd/libsnap-confine-private: Tighten AppArmor label + check + +Only consider snap-confine as confined by AppArmor when the AppArmor label +matches an expected path location for the snap-confine binary, rather than +just if the label is not "unconfined". This ensures snap-confine will fail +to execute if it is executed under a more permissive AppArmor profile than +expected. + +Signed-off-by: Alex Murray +--- + cmd/libsnap-confine-private/apparmor-support.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/cmd/libsnap-confine-private/apparmor-support.c b/cmd/libsnap-confine-private/apparmor-support.c +index 9930e42168..a0f342f538 100644 +--- a/cmd/libsnap-confine-private/apparmor-support.c ++++ b/cmd/libsnap-confine-private/apparmor-support.c +@@ -20,6 +20,7 @@ + #endif + + #include "apparmor-support.h" ++#include "utils.h" + + #include + #include +@@ -87,13 +88,13 @@ void sc_init_apparmor_support(struct sc_apparmor *apparmor) + } + debug("apparmor label on snap-confine is: %s", label); + debug("apparmor mode is: %s", mode); +- // The label has a special value "unconfined" that is applied to all +- // processes without a dedicated profile. If that label is used then the +- // current process is not confined. All other labels imply confinement. +- if (label != NULL && strcmp(label, SC_AA_UNCONFINED_STR) == 0) { +- apparmor->is_confined = false; +- } else { ++ // expect to be confined by a profile with the name of a valid ++ // snap-confine binary since if not we may be executed under a ++ // profile with more permissions than expected ++ if (label != NULL && sc_is_expected_path(label)) { + apparmor->is_confined = true; ++ } else { ++ apparmor->is_confined = false; + } + // There are several possible results for the confinement type (mode) that + // are checked for below. +-- +2.32.0 + diff -Nru snapd-2.37.4/debian/patches/cve202144730/0014-cmd-snap-confine-Remove-execute-permission-from-AppA.patch snapd-2.37.4/debian/patches/cve202144730/0014-cmd-snap-confine-Remove-execute-permission-from-AppA.patch --- snapd-2.37.4/debian/patches/cve202144730/0014-cmd-snap-confine-Remove-execute-permission-from-AppA.patch 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.37.4/debian/patches/cve202144730/0014-cmd-snap-confine-Remove-execute-permission-from-AppA.patch 2022-02-17 15:29:46.000000000 +0000 @@ -0,0 +1,36 @@ +From b82d0408481ccf70eac328a9c7d8e48df0793d17 Mon Sep 17 00:00:00 2001 +From: Alex Murray +Date: Wed, 17 Nov 2021 14:43:41 +1030 +Subject: [PATCH 14/36] cmd/snap-confine: Remove execute permission from + AppArmor profile + +The snap-confine AppArmor profile cargo-culted a work-around for the +handling of encryptfs encrypted home directories from the AppArmor +base abstraction. Unfortunately this includes permission to execute +arbitrary binaries from within the user's Private home directory +and so could be used to trick snap-confine to execute arbitrary +user-controlled binaries, which when combined with other flaws in +snap-confine could then be used to try and escape confinement. + +Signed-off-by: Alex Murray +--- + cmd/snap-confine/snap-confine.apparmor.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +Index: snapd-2.37.4/cmd/snap-confine/snap-confine.apparmor.in +=================================================================== +--- snapd-2.37.4.orig/cmd/snap-confine/snap-confine.apparmor.in ++++ snapd-2.37.4/cmd/snap-confine/snap-confine.apparmor.in +@@ -338,10 +338,10 @@ + # stacked filesystems generally. + # encrypted ~/.Private and old-style encrypted $HOME + @{HOME}/.Private/ r, +- @{HOME}/.Private/** mrixwlk, ++ @{HOME}/.Private/** mrwlk, + # new-style encrypted $HOME + @{HOMEDIRS}/.ecryptfs/*/.Private/ r, +- @{HOMEDIRS}/.ecryptfs/*/.Private/** mrixwlk, ++ @{HOMEDIRS}/.ecryptfs/*/.Private/** mrwlk, + + # Allow snap-confine to move to the void + /var/lib/snapd/void/ r, diff -Nru snapd-2.37.4/debian/patches/cve202144730/0015-cmd-snap-confine-Prevent-user-controlled-race-in-set.patch snapd-2.37.4/debian/patches/cve202144730/0015-cmd-snap-confine-Prevent-user-controlled-race-in-set.patch --- snapd-2.37.4/debian/patches/cve202144730/0015-cmd-snap-confine-Prevent-user-controlled-race-in-set.patch 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.37.4/debian/patches/cve202144730/0015-cmd-snap-confine-Prevent-user-controlled-race-in-set.patch 2022-02-17 15:25:17.000000000 +0000 @@ -0,0 +1,174 @@ +From acb2b4cdb89520b4682aae6cd9fb17dbd2732587 Mon Sep 17 00:00:00 2001 +From: Alex Murray +Date: Thu, 18 Nov 2021 11:03:45 +1030 +Subject: [PATCH 15/36] cmd/snap-confine: Prevent user-controlled race in + setup_private_mount + +When setting up the private mount namespace for a snap, snap-confine tries +to reuse the existing /tmp/snap.$SNAP_NAME directory if it already +exists. However, a user could create this directory before snap-confine is +executed and hence snap-confine would reuse it, along with any contents +that already existed. This could allow a user to symlink their own contents +into this directory and snap-confine would then mount that into the snap's +mount namespace. Finally this could allow an unprivileged attacker to cause +snap-confine to escape confinement by causing it to be executed under a +less restrictive AppArmor profile when this vulnerability is combined with +others. Fix this by moving the erroneous directory out of the way if it +doesn't have the expected permissions / ownership so we can re-create it +with the correct restrictive permissions. + +This resolves CVE-2021-44731. + +Signed-off-by: Alex Murray +--- + cmd/snap-confine/mount-support-test.c | 41 +++++++++ + cmd/snap-confine/mount-support.c | 114 ++++++++++++++++++++------ + 2 files changed, 131 insertions(+), 24 deletions(-) + +Index: snapd-2.37.4/cmd/snap-confine/mount-support-test.c +=================================================================== +--- snapd-2.37.4.orig/cmd/snap-confine/mount-support-test.c ++++ snapd-2.37.4/cmd/snap-confine/mount-support-test.c +@@ -21,6 +21,7 @@ + #include "mount-support-nvidia.c" + + #include ++#include + + static void replace_slashes_with_NUL(char *path, size_t len) + { +Index: snapd-2.37.4/cmd/snap-confine/mount-support.c +=================================================================== +--- snapd-2.37.4.orig/cmd/snap-confine/mount-support.c ++++ snapd-2.37.4/cmd/snap-confine/mount-support.c +@@ -14,6 +14,7 @@ + * along with this program. If not, see . + * + */ ++ + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif +@@ -58,6 +59,91 @@ + **/ + #define SC_VOID_DIR "/var/lib/snapd/void" + ++static int must_mkdir_and_open_with_perms(const char *dir, uid_t uid, gid_t gid, ++ mode_t mode) ++{ ++ int retries = 10; ++ int fd; ++ ++ mkdir: ++ if (--retries == 0) { ++ die("lost race to create dir %s too many times", dir); ++ } ++ // Ignore EEXIST since we want to reuse and we will open with ++ // O_NOFOLLOW, below. ++ if (mkdir(dir, 0700) < 0 && errno != EEXIST) { ++ die("cannot create directory %s", dir); ++ } ++ fd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); ++ if (fd < 0) { ++ // if is not a directory then remove it and try again ++ if (errno == ENOTDIR && unlink(dir) == 0) { ++ goto mkdir; ++ } ++ die("cannot open directory %s", dir); ++ } ++ // ensure base_dir has the expected permissions since it may have ++ // already existed ++ struct stat st; ++ if (fstat(fd, &st) < 0) { ++ die("cannot stat base directory %s", dir); ++ } ++ if (st.st_uid != uid || st.st_gid != gid ++ || st.st_mode != (S_IFDIR | mode)) { ++ unsigned char random[10] = { 0 }; ++ char random_dir[MAX_BUF] = { 0 }; ++ int offset; ++ size_t i; ++ ++ // base_dir isn't what we expect - create a random ++ // directory name and rename the existing erroneous ++ // base_dir to this then try recreating it again - NOTE we ++ // don't use mkdtemp() here since we don't want to actually ++ // create the directory yet as we want rename() to do that ++ // for us ++#ifdef SYS_getrandom ++ // use syscall(SYS_getrandom) since getrandom() is ++ // not available on older glibc ++ if (syscall(SYS_getrandom, random, sizeof(random), 0) != ++ sizeof(random)) { ++ die("cannot get random bytes"); ++ } ++#else ++ // use /dev/urandom on older systems which don't support ++ // SYS_getrandom ++ int rfd = open("/dev/urandom", O_RDONLY); ++ if (rfd < 0) { ++ die("cannot open /dev/urandom"); ++ } ++ if (read(rfd, random, sizeof(random)) != sizeof(random)) { ++ die("cannot get random bytes"); ++ } ++ close(rfd); ++#endif ++ offset = ++ sc_must_snprintf(random_dir, sizeof(random_dir), "%s.", ++ dir); ++ for (i = 0; i < sizeof(random); i++) { ++ offset += ++ sc_must_snprintf(random_dir + offset, ++ sizeof(random_dir) - offset, ++ "%02x", (unsigned int)random[i]); ++ } ++ // try and get dir which we own by renaming it to something ++ // else then creating it again ++ ++ // TODO - change this to use renameat2(RENAME_EXCHANGE) ++ // once we can use a newer version of glibc for snapd ++ if (rename(dir, random_dir) < 0) { ++ die("cannot rename base_dir to random_dir '%s'", ++ random_dir); ++ } ++ close(fd); ++ goto mkdir; ++ } ++ return fd; ++} ++ + // TODO: simplify this, after all it is just a tmpfs + // TODO: fold this into bootstrap + static void setup_private_mount(const char *snap_name) +@@ -65,6 +151,7 @@ static void setup_private_mount(const ch + uid_t uid = getuid(); + gid_t gid = getgid(); + char tmpdir[MAX_BUF] = { 0 }; ++ int base_dir_fd SC_CLEANUP(sc_cleanup_close) = -1; + + // Create a 0700 base directory, this is the base dir that is + // protected from other users. +@@ -73,17 +160,17 @@ static void setup_private_mount(const ch + // mounted for the applications to use + sc_must_snprintf(tmpdir, sizeof(tmpdir), "/tmp/snap.%d_%s_XXXXXX", uid, + snap_name); +- if (mkdtemp(tmpdir) == NULL) { +- die("cannot create temporary directory essential for private /tmp"); +- } ++ base_dir_fd = must_mkdir_and_open_with_perms(tmpdir, 0, 0, 0700); + // now we create a 1777 /tmp inside our private dir + mode_t old_mask = umask(0); + char *d = sc_strdup(tmpdir); + sc_must_snprintf(tmpdir, sizeof(tmpdir), "%s/tmp", d); + free(d); + +- if (mkdir(tmpdir, 01777) != 0) { +- die("cannot create temporary directory for private /tmp"); ++ // Create /tmp/snap.$SNAP_NAME/tmp 01777 root.root Ignore EEXIST since we ++ // want to reuse and we will open with O_NOFOLLOW, below. ++ if (mkdirat(base_dir_fd, "tmp", 01777) < 0 && errno != EEXIST) { ++ die("cannot create private tmp directory %s/tmp", tmpdir); + } + umask(old_mask); + diff -Nru snapd-2.37.4/debian/patches/series snapd-2.37.4/debian/patches/series --- snapd-2.37.4/debian/patches/series 2019-02-28 17:21:26.000000000 +0000 +++ snapd-2.37.4/debian/patches/series 2022-02-17 15:28:18.000000000 +0000 @@ -6,3 +6,9 @@ 0006-systemd-disable-snapfuse-system.patch 0007-i18n-use-dummy-localizations-to-avoid-dependencies.patch 0010-man-page-sections.patch +cve202144730/0010-cmd-libsnap-confine-private-Fix-use-of-uninitialised.patch +cve202144730/0011-cmd-libsnap-confine-private-Defend-against-hardlink-.patch +cve202144730/0012-cmd-libsnap-confine-private-Don-t-fail-open-on-appar.patch +cve202144730/0013-cmd-libsnap-confine-private-Tighten-AppArmor-label-c.patch +cve202144730/0014-cmd-snap-confine-Remove-execute-permission-from-AppA.patch +cve202144730/0015-cmd-snap-confine-Prevent-user-controlled-race-in-set.patch