diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index 40e4e975..819027b2 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -2110,6 +2110,96 @@ flatpak_dir_pull_oci (FlatpakDir *self, return TRUE; } +static gboolean +ensure_safe_objdir (int dir_fd, const char *rel_path, GError **error) +{ + g_auto(GLnxDirFdIterator) iter = {0}; + + if (!glnx_dirfd_iterator_init_at (dir_fd, rel_path, TRUE, &iter, error)) + return FALSE; + + while (TRUE) + { + struct dirent *dent; + + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&iter, &dent, NULL, error)) + return FALSE; + + if (dent == NULL) + break; + + if (dent->d_type == DT_DIR) + { + if (!ensure_safe_objdir (iter.fd, dent->d_name, error)) + return FALSE; + } + else + { + struct stat stbuf; + if (fstatat (iter.fd, dent->d_name, &stbuf, 0) == 0 && + ((stbuf.st_mode & ~S_IFMT) & ~0775) != 0) + return flatpak_fail (error, "Invalid file mode 0%04o", stbuf.st_mode); + + if (g_str_has_suffix (dent->d_name, ".dirmeta")) + { + glnx_fd_close int dirmeta_fd = -1; + g_autoptr(GBytes) data = NULL; + g_autoptr(GVariant) variant = NULL; + guint32 mode; + + dirmeta_fd = openat (iter.fd, dent->d_name, O_RDONLY | O_CLOEXEC); + if (dirmeta_fd < 0) + flatpak_fail(error, "Can't read dirmeta"); + + data = glnx_fd_readall_bytes (dirmeta_fd, NULL, error); + if (!data) + return FALSE; + variant = g_variant_new_from_bytes (OSTREE_DIRMETA_GVARIANT_FORMAT, data, TRUE); + g_variant_ref_sink (variant); + + g_variant_get_child (variant, 2, "u", &mode); + mode = GUINT32_FROM_BE (mode); + if (((mode & ~S_IFMT) & ~0775) != 0) + return flatpak_fail (error, "Invalid directory mode 0%04o", mode); + } + } + } + + return TRUE; +} + + +static gboolean +ensure_safe_staging_permissions (OstreeRepo *repo, GError **error) +{ + g_auto(GLnxDirFdIterator) tmp_iter = {0}; + + /* We don't know which stage dir is in use, so check all */ + + if (!glnx_dirfd_iterator_init_at (ostree_repo_get_dfd (repo), "tmp", TRUE, &tmp_iter, error)) + return FALSE; + + while (TRUE) + { + struct dirent *dent; + + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&tmp_iter, &dent, NULL, error)) + return FALSE; + + if (dent == NULL) + break; + + if (dent->d_type == DT_DIR && g_str_has_prefix (dent->d_name, "staging-") && + !ensure_safe_objdir (tmp_iter.fd, dent->d_name, error)) + { + glnx_shutil_rm_rf_at (tmp_iter.fd, dent->d_name, NULL, NULL); + return FALSE; + } + } + + return TRUE; +} + gboolean flatpak_dir_pull (FlatpakDir *self, const char *repository, @@ -2238,6 +2328,9 @@ flatpak_dir_pull (FlatpakDir *self, goto out; + if (!ensure_safe_staging_permissions (repo, error)) + goto out; + if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; diff --git a/tests/test-run.sh b/tests/test-run.sh index 0f0d3aa9..2b70ff2c 100755 --- a/tests/test-run.sh +++ b/tests/test-run.sh @@ -24,7 +24,7 @@ set -euo pipefail skip_without_bwrap skip_without_user_xattrs -echo "1..10" +echo "1..12" setup_repo install_repo @@ -338,3 +338,34 @@ ${FLATPAK} build-export ${FL_GPGARGS} repos/test ${DIR} ${FLATPAK} ${U} update org.test.OldVersion echo "ok version checks" + +rm -rf app +flatpak build-init app org.test.Writable org.test.Platform org.test.Platform +mkdir -p app/files/a-dir +chmod a+rwx app/files/a-dir +flatpak build-finish --command=hello.sh app +ostree --repo=repos/test commit ${FL_GPGARGS} --branch=app/org.test.Writable/$ARCH/master app +update_repo + +if ${FLATPAK} ${U} install test-repo org.test.Writable &> err.txt; then + assert_not_reached "Should not be able to install with world-writable directory" +fi +assert_file_has_content err.txt [Ii]nvalid + +echo "ok no world writable dir" + +rm -rf app +flatpak build-init app org.test.Setuid org.test.Platform org.test.Platform +mkdir -p app/files/ +touch app/files/exe +chmod u+s app/files/exe +flatpak build-finish --command=hello.sh app +ostree --repo=repos/test commit ${FL_GPGARGS} --branch=app/org.test.Setuid/$ARCH/master app +update_repo + +if ${FLATPAK} ${U} install test-repo org.test.Setuid &> err2.txt; then + assert_not_reached "Should not be able to install with setuid file" +fi +assert_file_has_content err2.txt [Ii]nvalid + +echo "ok no setuid"