github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/cmd/libsnap-confine-private/tool.c (about) 1 /* 2 * Copyright (C) 2018 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include "tool.h" 23 24 #include <fcntl.h> 25 #include <libgen.h> 26 #include <string.h> 27 #include <sys/stat.h> 28 #include <sys/types.h> 29 #include <sys/wait.h> 30 #include <unistd.h> 31 32 #include "../libsnap-confine-private/apparmor-support.h" 33 #include "../libsnap-confine-private/cleanup-funcs.h" 34 #include "../libsnap-confine-private/string-utils.h" 35 #include "../libsnap-confine-private/utils.h" 36 37 /** 38 * sc_open_snapd_tool returns a file descriptor of the given internal executable. 39 * 40 * The executable is located based on the location of the currently executing process. 41 * The returning file descriptor can be used with fexecve function, like in sc_call_snapd_tool. 42 **/ 43 static int sc_open_snapd_tool(const char *tool_name); 44 45 /** 46 * sc_call_snapd_tool calls a snapd tool by file descriptor. 47 * 48 * The idea with calling with an open file descriptor is to allow calling executables 49 * across mount namespaces, where the executable may not be visible in the new filesystem 50 * anymore. The caller establishes an open file descriptor in one namespace and later on 51 * performs the call in another mount namespace. 52 * 53 * The environment vector has special support for expanding the string "SNAPD_DEBUG=x". 54 * If such string is present, the "x" is replaced with either "0" or "1" depending on 55 * the result of is_sc_debug_enabled(). 56 **/ 57 static void sc_call_snapd_tool(int tool_fd, const char *tool_name, char **argv, 58 char **envp); 59 60 /** 61 * sc_call_snapd_tool_with_apparmor calls a snapd tool by file descriptor, 62 * possibly confining the program with a specific apparmor profile. 63 **/ 64 static void sc_call_snapd_tool_with_apparmor(int tool_fd, const char *tool_name, 65 struct sc_apparmor *apparmor, 66 const char *aa_profile, 67 char **argv, char **envp); 68 69 int sc_open_snap_update_ns(void) 70 { 71 return sc_open_snapd_tool("snap-update-ns"); 72 } 73 74 void sc_call_snap_update_ns(int snap_update_ns_fd, const char *snap_name, 75 struct sc_apparmor *apparmor) 76 { 77 char *snap_name_copy SC_CLEANUP(sc_cleanup_string) = NULL; 78 snap_name_copy = sc_strdup(snap_name); 79 80 char aa_profile[PATH_MAX] = { 0 }; 81 sc_must_snprintf(aa_profile, sizeof aa_profile, "snap-update-ns.%s", 82 snap_name); 83 84 char *argv[] = { 85 "snap-update-ns", 86 /* This tells snap-update-ns we are calling from snap-confine and locking is in place */ 87 "--from-snap-confine", 88 snap_name_copy, NULL 89 }; 90 char *envp[] = { "SNAPD_DEBUG=x", NULL }; 91 92 /* Switch the group to root so that directories, files and locks created by 93 * snap-update-ns are owned by the root group. */ 94 sc_identity old = sc_set_effective_identity(sc_root_group_identity()); 95 sc_call_snapd_tool_with_apparmor(snap_update_ns_fd, 96 "snap-update-ns", apparmor, 97 aa_profile, argv, envp); 98 (void)sc_set_effective_identity(old); 99 } 100 101 void sc_call_snap_update_ns_as_user(int snap_update_ns_fd, 102 const char *snap_name, 103 struct sc_apparmor *apparmor) 104 { 105 char *snap_name_copy SC_CLEANUP(sc_cleanup_string) = NULL; 106 snap_name_copy = sc_strdup(snap_name); 107 108 char aa_profile[PATH_MAX] = { 0 }; 109 sc_must_snprintf(aa_profile, sizeof aa_profile, "snap-update-ns.%s", 110 snap_name); 111 112 const char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR"); 113 char xdg_runtime_dir_env[PATH_MAX + strlen("XDG_RUNTIME_DIR=")]; 114 if (xdg_runtime_dir != NULL) { 115 sc_must_snprintf(xdg_runtime_dir_env, 116 sizeof(xdg_runtime_dir_env), 117 "XDG_RUNTIME_DIR=%s", xdg_runtime_dir); 118 } 119 120 char *argv[] = { 121 "snap-update-ns", 122 /* This tells snap-update-ns we are calling from snap-confine and locking is in place */ 123 "--from-snap-confine", 124 /* This tells snap-update-ns that we want to process the per-user profile */ 125 "--user-mounts", snap_name_copy, NULL 126 }; 127 char *envp[] = { 128 /* SNAPD_DEBUG=x is replaced by sc_call_snapd_tool_with_apparmor 129 * with either SNAPD_DEBUG=0 or SNAPD_DEBUG=1, see that function 130 * for details. */ 131 "SNAPD_DEBUG=x", 132 xdg_runtime_dir_env, NULL 133 }; 134 sc_call_snapd_tool_with_apparmor(snap_update_ns_fd, 135 "snap-update-ns", apparmor, 136 aa_profile, argv, envp); 137 } 138 139 int sc_open_snap_discard_ns(void) 140 { 141 return sc_open_snapd_tool("snap-discard-ns"); 142 } 143 144 void sc_call_snap_discard_ns(int snap_discard_ns_fd, const char *snap_name) 145 { 146 char *snap_name_copy SC_CLEANUP(sc_cleanup_string) = NULL; 147 snap_name_copy = sc_strdup(snap_name); 148 char *argv[] = 149 { "snap-discard-ns", "--from-snap-confine", snap_name_copy, NULL }; 150 /* SNAPD_DEBUG=x is replaced by sc_call_snapd_tool_with_apparmor with 151 * either SNAPD_DEBUG=0 or SNAPD_DEBUG=1, see that function for details. */ 152 char *envp[] = { "SNAPD_DEBUG=x", NULL }; 153 /* Switch the group to root so that directories and locks created by 154 * snap-discard-ns are owned by the root group. */ 155 sc_identity old = sc_set_effective_identity(sc_root_group_identity()); 156 sc_call_snapd_tool(snap_discard_ns_fd, "snap-discard-ns", argv, envp); 157 (void)sc_set_effective_identity(old); 158 } 159 160 static int sc_open_snapd_tool(const char *tool_name) 161 { 162 // +1 is for the case where the link is exactly PATH_MAX long but we also 163 // want to store the terminating '\0'. The readlink system call doesn't add 164 // terminating null, but our initialization of buf handles this for us. 165 char buf[PATH_MAX + 1] = { 0 }; 166 if (readlink("/proc/self/exe", buf, sizeof buf) < 0) { 167 die("cannot readlink /proc/self/exe"); 168 } 169 if (buf[0] != '/') { // this shouldn't happen, but make sure have absolute path 170 die("readlink /proc/self/exe returned relative path"); 171 } 172 char *dir_name = dirname(buf); 173 int dir_fd SC_CLEANUP(sc_cleanup_close) = 1; 174 dir_fd = open(dir_name, O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); 175 if (dir_fd < 0) { 176 die("cannot open path %s", dir_name); 177 } 178 int tool_fd = -1; 179 tool_fd = openat(dir_fd, tool_name, O_PATH | O_NOFOLLOW | O_CLOEXEC); 180 if (tool_fd < 0) { 181 die("cannot open path %s/%s", dir_name, tool_name); 182 } 183 debug("opened %s executable as file descriptor %d", tool_name, tool_fd); 184 return tool_fd; 185 } 186 187 static void sc_call_snapd_tool(int tool_fd, const char *tool_name, char **argv, 188 char **envp) 189 { 190 sc_call_snapd_tool_with_apparmor(tool_fd, tool_name, NULL, NULL, argv, 191 envp); 192 } 193 194 static void sc_call_snapd_tool_with_apparmor(int tool_fd, const char *tool_name, 195 struct sc_apparmor *apparmor, 196 const char *aa_profile, 197 char **argv, char **envp) 198 { 199 debug("calling snapd tool %s", tool_name); 200 pid_t child = fork(); 201 if (child < 0) { 202 die("cannot fork to run snapd tool %s", tool_name); 203 } 204 if (child == 0) { 205 /* If the caller provided template environment entry for SNAPD_DEBUG 206 * then expand it to the actual value. */ 207 for (char **env = envp; 208 /* Mama mia, that's a spicy meatball. */ 209 env != NULL && *env != NULL && **env != '\0'; env++) { 210 if (sc_streq(*env, "SNAPD_DEBUG=x")) { 211 /* NOTE: this is not released, on purpose. */ 212 char *entry = sc_strdup(*env); 213 entry[strlen("SNAPD_DEBUG=x") - 1] = 214 sc_is_debug_enabled()? '1' : '0'; 215 *env = entry; 216 } 217 } 218 /* Switch apparmor profile for the process after exec. */ 219 if (apparmor != NULL && aa_profile != NULL) { 220 sc_maybe_aa_change_onexec(apparmor, aa_profile); 221 } 222 fexecve(tool_fd, argv, envp); 223 die("cannot execute snapd tool %s", tool_name); 224 } else { 225 int status = 0; 226 debug("waiting for snapd tool %s to terminate", tool_name); 227 if (waitpid(child, &status, 0) < 0) { 228 die("cannot get snapd tool %s termination status via waitpid", tool_name); 229 } 230 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { 231 die("%s failed with code %i", tool_name, 232 WEXITSTATUS(status)); 233 } else if (WIFSIGNALED(status)) { 234 die("%s killed by signal %i", tool_name, 235 WTERMSIG(status)); 236 } 237 debug("%s finished successfully", tool_name); 238 } 239 }