mirror of
https://github.com/flatpak/flatpak.git
synced 2026-03-15 21:52:08 -04:00
extra-data: Don't allow creating files with non-canonical permissions in apply_extra
When installing a flatpak with extra-data we execute the apply_extra script from the flatpak to extract the extra data files we created. This script runs with very little filesystem acces, but it does have write permissions to the location that will eventually be /app/extra in the finished flatpak. This is especially problematic for the systemwide install case, because the script is then run as root, so it could potentially create a setuid file there. Such a file would not be usable inside the sandbox (because setuid is disabled in the sandbox), but it could potentially be a problem if the user could be tricked into running the file directly on the host. This is the same behaviour as e.g. rpm or deb which both can install setuid files, but we want to guarantee that flatpak is better than that. The fix is to run the script with all capabilities dropped (bwrap --cap-drop ALL) which removes a bunch of possible attack vectors (for instance setting file capabilities). However, even without capabilities, it is possible for a user to make any file setuid to the same user, so we also need to canonicalize the permissions of all files generated by running the script. Additionally, while running the script we set the toplevel directory only be accessible to the user, meaning we will not temporarily leak any potential setuid files to other users. Note, this commit actually goes furthen than that and completely canonicalizes all the file permissions to be the same as those otherwise used by flatpak. For example we fix up cases where the script creates files writable or unreadable by non-root users. Closes: #2323 Approved by: alexlarsson
This commit is contained in:
committed by
Atomic Bot
parent
1ce0246b0d
commit
35598f46a5
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user