diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index cd129892..b2f49a07 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -6569,6 +6569,8 @@ apply_extra_data (FlatpakDir *self, "--ro-bind", flatpak_file_get_path_cached (app_files), "/app", "--bind", flatpak_file_get_path_cached (extra_files), "/app/extra", "--chdir", "/app/extra", + /* We run as root in the system-helper case, so drop all caps */ + "--cap-drop", "ALL", NULL); if (!flatpak_run_setup_base_argv (bwrap, runtime_files, NULL, runtime_ref_parts[2], @@ -6592,6 +6594,17 @@ apply_extra_data (FlatpakDir *self, g_debug ("Running /app/bin/apply_extra "); + /* We run the sandbox without caps, but it can still create files owned by itself with + * arbitrary permissions, including setuid myself. This is extra risky in the case where + * this runs as root in the system helper case. We canonicalize the permissions at the + * end, but do avoid non-canonical permissions leaking out before then we make the + * toplevel dir only accessible to the user */ + if (chmod (flatpak_file_get_path_cached (extra_files), 0700) != 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + if (!g_spawn_sync (NULL, (char **) bwrap->argv->pdata, bwrap->envp, @@ -6602,6 +6615,9 @@ apply_extra_data (FlatpakDir *self, error)) return FALSE; + if (!flatpak_canonicalize_permissions (AT_FDCWD, flatpak_file_get_path_cached (extra_files), error)) + return FALSE; + if (exit_status != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h index ad5a9193..54d317b6 100644 --- a/common/flatpak-utils-private.h +++ b/common/flatpak-utils-private.h @@ -475,6 +475,10 @@ gboolean flatpak_rm_rf (GFile *dir, GCancellable *cancellable, GError **error); +gboolean flatpak_canonicalize_permissions (int parent_dfd, + const char *rel_path, + GError **error); + char * flatpak_readlink (const char *path, GError **error); char * flatpak_resolve_link (const char *path, diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c index dc565957..6354e9a7 100644 --- a/common/flatpak-utils.c +++ b/common/flatpak-utils.c @@ -1986,6 +1986,108 @@ out: return ret; } +static gboolean +_flatpak_canonicalize_permissions (int parent_dfd, + const char *rel_path, + gboolean toplevel, + GError **error) +{ + struct stat stbuf; + gboolean res = TRUE; + + /* Note, in order to not leave non-canonical things around in case + * of error, this continues after errors, but returns the first + * error. */ + + if (TEMP_FAILURE_RETRY (fstatat (parent_dfd, rel_path, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + + if (S_ISDIR (stbuf.st_mode)) + { + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + + /* For the toplevel we set to 0700 so we can modify it, but not + expose any non-canonical files to any other user, then we set + it to 0755 afterwards. */ + if (fchmodat (parent_dfd, rel_path, toplevel ? 0700 : 0755, 0) != 0) + { + glnx_set_error_from_errno (error); + error = NULL; + res = FALSE; + } + + if (glnx_dirfd_iterator_init_at (parent_dfd, rel_path, FALSE, &dfd_iter, NULL)) + { + while (TRUE) + { + struct dirent *dent; + + if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, NULL, NULL) || dent == NULL) + break; + + if (!_flatpak_canonicalize_permissions (dfd_iter.fd, dent->d_name, FALSE, error)) + { + error = NULL; + res = FALSE; + } + } + } + + if (toplevel && + fchmodat (parent_dfd, rel_path, 0755, 0) != 0) + { + glnx_set_error_from_errno (error); + error = NULL; + res = FALSE; + } + + return res; + } + else if (S_ISREG(stbuf.st_mode)) + { + mode_t mode; + + /* If use can execute, make executable by all */ + if (stbuf.st_mode & S_IXUSR) + mode = 0755; + else /* otherwise executable by none */ + mode = 0644; + + if (fchmodat (parent_dfd, rel_path, mode, 0) != 0) + { + glnx_set_error_from_errno (error); + res = FALSE; + } + } + else if (S_ISLNK(stbuf.st_mode)) + { + /* symlinks have no permissions */ + } + else + { + /* some weird non-canonical type, lets delete it */ + if (unlinkat(parent_dfd, rel_path, 0) != 0) + { + glnx_set_error_from_errno (error); + res = FALSE; + } + } + + return res; +} + +/* Canonicalizes files to the same permissions as bare-user-only checkouts */ +gboolean +flatpak_canonicalize_permissions (int parent_dfd, + const char *rel_path, + GError **error) +{ + return _flatpak_canonicalize_permissions (parent_dfd, rel_path, TRUE, error); +} + /* Make a directory, and its parent. Don't error if it already exists. * If you want a failure mode with EEXIST, use g_file_make_directory_with_parents. */ gboolean