From 6d6620da86b924d675c7195036f70b1414d26560 Mon Sep 17 00:00:00 2001 From: "Jason T. Greene" Date: Thu, 17 Aug 2023 02:23:27 -0500 Subject: [PATCH] Add support for escaping resolv.conf symlinks Previously if resolv.conf was symlinked to a location other than /etc, or /run, a warning message would be printed and DNS would be non-functional. Instead, attempt to bind an equiavlent resolv.conf link target path in the namespace structure, so that symlink continues to function, and DNS remains operational. Signed-off-by: Jason T. Greene --- sandbox.c | 66 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/sandbox.c b/sandbox.c index 13514cb..f453dd7 100644 --- a/sandbox.c +++ b/sandbox.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #include +#include #include #include #include @@ -27,7 +28,8 @@ static int add_mount(const char *from, const char *to) MS_BIND | MS_REC | MS_SLAVE | MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL); if (ret < 0) { - fprintf(stderr, "cannot bind mount %s to %s\n", from, to); + fprintf(stderr, "cannot bind mount %s to %s (errno: %d)\n", from, to, + errno); return ret; } ret = mount("", to, "", MS_SLAVE | MS_REC, NULL); @@ -46,21 +48,54 @@ static int add_mount(const char *from, const char *to) return 0; } -/* Warn (not error) if /etc/resolv.conf is a symlink to a file outside /etc or +/* Bind /etc/resolv.conf if it is a symlink to a file outside /etc or * /run. */ -static void validate_etc_resolv_conf() +static int bind_escaped_resolv_conf(const char *root) { - char *p = realpath("/etc/resolv.conf", NULL); - if (p == NULL) { - return; + char *real_resolv = realpath("/etc/resolv.conf", NULL); + + /* Doesn't exist or is not an escaping symlink */ + if (real_resolv == NULL || g_str_has_prefix(real_resolv, "/etc") || + g_str_has_prefix(real_resolv, "/run")) { + free(real_resolv); + return 0; + } + + char *resolv_dest = g_strconcat(root, real_resolv, NULL); + char *resolv_dest_dir = g_path_get_dirname(resolv_dest); + int ret = 0; + + fprintf(stderr, + "sandbox: /etc/resolv.conf (-> %s) seems a symlink to a file " + "outside {/etc, /run}, attempting to bind it as well.\n", + real_resolv); + + ret = g_mkdir_with_parents(resolv_dest_dir, 0755); + if (ret < 0) { + fprintf(stderr, "cannot create resolve dest dir path: %s\n", + resolv_dest_dir); + goto finish; + } + + ret = creat(resolv_dest, 0755); + if (ret < 0) { + fprintf(stderr, "cannot create empty resolv.conf dest file %s\n", + resolv_dest); + goto finish; } - if (!g_str_has_prefix(p, "/etc") && !g_str_has_prefix(p, "/run")) { - fprintf(stderr, - "sandbox: /etc/resolv.conf (-> %s) seems a symlink to a file " - "outside {/etc, /run}. DNS will not work.\n", - p); + close(ret); + + ret = add_mount(real_resolv, resolv_dest); + if (ret < 0) { + fprintf(stderr, "cannot bind mount resolv.conf\n"); } - free(p); + +finish: + + free(real_resolv); + g_free(resolv_dest); + g_free(resolv_dest_dir); + return ret; } /* lock down the process doing the following: @@ -75,8 +110,6 @@ int create_sandbox() struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; struct __user_cap_data_struct data[2] = { { 0 } }; - validate_etc_resolv_conf(); - ret = unshare(CLONE_NEWNS); if (ret < 0) { fprintf(stderr, "cannot unshare new mount namespace\n"); @@ -118,6 +151,11 @@ int create_sandbox() return ret; } + ret = bind_escaped_resolv_conf("/tmp"); + if (ret < 0) { + return ret; + } + ret = add_mount("/run", "/tmp/run"); if (ret < 0) { return ret;