From 2c8e2417deb6a0d293b15367fcd69080e285da31 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 19 Jun 2017 19:01:08 +0200 Subject: [PATCH] Ensure we don't install world-writable dirs or setuid files This is solved in a much nicer way on master, using new ostree APIs. However, here we take a brute-force approach of scanning all staged files and ensuring we don't have any files or directories with invalid modes before committing the transaction. If any bad permissions were found we delete the entire staging directory. --- common/flatpak-dir.c | 93 ++++++++++++++++++++++++++++++++++++++++++++ tests/test-run.sh | 33 +++++++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) 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"