arc-setup: Validate media_profiles.xml is not a symlink
Validate /run/arc/oem/etc/media_profiles.xml is not a symlink before
writing to it.
BUG=chromium:1133047
TEST=emerge arc-setup
TEST=cros_workon_make --board=${BOARD} chromeos-base/arc-setup --test
Change-Id: I71d3c91f444d3e601ca6283fe005b3856a12f16c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2486949
Commit-Queue: Ereth McKnight-MacNeil <ereth@chromium.org>
Commit-Queue: Greg Kerr <kerrnel@chromium.org>
Tested-by: Ereth McKnight-MacNeil <ereth@chromium.org>
Tested-by: Greg Kerr <kerrnel@chromium.org>
Reviewed-by: Allen Webb <allenwebb@google.com>
Reviewed-by: Long Cheng <lgcheng@google.com>
diff --git a/arc/setup/arc_setup.cc b/arc/setup/arc_setup.cc
index 1330811..911cbcf 100644
--- a/arc/setup/arc_setup.cc
+++ b/arc/setup/arc_setup.cc
@@ -844,9 +844,15 @@
base::FilePath(oem_mount_directory)
.Append("etc")
.Append(arc_paths_->media_profile_file);
- EXIT_IF(!base::CopyFile(media_profile_xml, new_media_profile_xml));
- EXIT_IF(
- !Chown(kHostArcCameraUid, kHostArcCameraGid, new_media_profile_xml));
+ brillo::SafeFD dest_parent(
+ brillo::SafeFD::Root()
+ .first.OpenExistingDir(new_media_profile_xml.DirName())
+ .first);
+ (void)dest_parent.Unlink(new_media_profile_xml.BaseName().value());
+ EXIT_IF(!SafeCopyFile(
+ media_profile_xml, brillo::SafeFD::Root().first /*src_parent*/,
+ new_media_profile_xml.BaseName(), std::move(dest_parent),
+ 0644 /*permissions*/, kHostArcCameraUid, kHostArcCameraGid));
}
base::FilePath hardware_features_xml("/etc/hardware_features.xml");
@@ -863,7 +869,15 @@
const base::FilePath platform_xml_file =
base::FilePath(oem_mount_directory)
.Append(arc_paths_->platform_xml_file_relative);
- EXIT_IF(!base::CopyFile(hardware_features_xml, platform_xml_file));
+ brillo::SafeFD dest_parent(
+ brillo::SafeFD::Root()
+ .first.OpenExistingDir(platform_xml_file.DirName())
+ .first);
+ (void)dest_parent.Unlink(platform_xml_file.BaseName().value());
+ EXIT_IF(!SafeCopyFile(hardware_features_xml,
+ brillo::SafeFD::Root().first /*src_parent*/,
+ platform_xml_file.BaseName(), std::move(dest_parent),
+ 0644 /*permissions*/));
// TODO(chromium:1083652) Remove dynamic shell scripts once all overlays
// are migrated to static XML config.
diff --git a/arc/setup/arc_setup_util.cc b/arc/setup/arc_setup_util.cc
index 4d10771..605aed8 100644
--- a/arc/setup/arc_setup_util.cc
+++ b/arc/setup/arc_setup_util.cc
@@ -22,6 +22,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
+#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/statfs.h>
@@ -1006,4 +1007,56 @@
return true;
}
+bool SafeCopyFile(const base::FilePath& src_path,
+ brillo::SafeFD src_parent,
+ const base::FilePath& dest_path,
+ brillo::SafeFD dest_parent,
+ mode_t permissions,
+ uid_t uid,
+ gid_t gid) {
+ struct stat st;
+ ssize_t len, ret;
+
+ if (!src_parent.is_valid()) {
+ LOG(ERROR) << "Invalid src_parent fd";
+ return false;
+ }
+
+ if (!dest_parent.is_valid()) {
+ LOG(ERROR) << "Invalid dest_parent fd";
+ return false;
+ }
+
+ brillo::SafeFD root = brillo::SafeFD::Root().first;
+ brillo::SafeFD::SafeFDResult result(
+ src_parent.OpenExistingFile(src_path, O_RDONLY | O_CLOEXEC));
+ if (brillo::SafeFD::IsError(result.second)) {
+ LOG(ERROR) << "Failed to open src path " << src_path;
+ return false;
+ }
+ brillo::SafeFD src_fd(std::move(result.first));
+
+ brillo::SafeFD dest_fd(
+ dest_parent.MakeFile(dest_path, permissions, uid, gid).first);
+ if (!dest_fd.is_valid()) {
+ LOG(ERROR) << "Failed to open dest path " << dest_path;
+ return false;
+ }
+
+ fstat(src_fd.get(), &st);
+ len = st.st_size;
+
+ do {
+ ret = sendfile(dest_fd.get(), src_fd.get(), NULL, len);
+ if (ret == -1) {
+ PLOG(ERROR) << "Fail to copy file " << src_path << " to " << dest_path
+ << errno;
+ return false;
+ }
+ len -= ret;
+ } while (len > 0 && ret > 0);
+
+ return true;
+}
+
} // namespace arc
diff --git a/arc/setup/arc_setup_util.h b/arc/setup/arc_setup_util.h
index 0d038dd..966a847 100644
--- a/arc/setup/arc_setup_util.h
+++ b/arc/setup/arc_setup_util.h
@@ -18,6 +18,7 @@
#include <base/files/file_path.h>
#include <base/files/scoped_file.h>
#include <base/macros.h>
+#include <brillo/files/safe_fd.h>
#include "arc/setup/android_sdk_version.h"
@@ -271,6 +272,16 @@
// Returns the user and group ids for a user.
bool GetUserId(const std::string& user, uid_t* user_id, gid_t* group_id);
+// Make a copy of file |src_path| to |dest_path|.
+// Use SafeFD to validate there is no symlink in the path.
+bool SafeCopyFile(const base::FilePath& src_path,
+ brillo::SafeFD src_parent,
+ const base::FilePath& dest_path,
+ brillo::SafeFD dest_parent,
+ mode_t permissions = 0640,
+ uid_t uid = getuid(),
+ gid_t gid = getgid());
+
} // namespace arc
#endif // ARC_SETUP_ARC_SETUP_UTIL_H_
diff --git a/arc/setup/arc_setup_util_test.cc b/arc/setup/arc_setup_util_test.cc
index 9228879..b625d2c 100644
--- a/arc/setup/arc_setup_util_test.cc
+++ b/arc/setup/arc_setup_util_test.cc
@@ -32,6 +32,7 @@
#include <base/time/time.h>
#include <base/timer/elapsed_timer.h>
#include <brillo/file_utils.h>
+#include <brillo/files/safe_fd.h>
#include <gtest/gtest.h>
namespace arc {
@@ -930,4 +931,23 @@
}
}
+TEST(ArcSetupUtil, SafeCopyFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ const base::FilePath src_file = temp_dir.GetPath().Append("srcfile");
+
+ // Create a new source file and write it.
+ ASSERT_TRUE(WriteToFile(src_file, 0755, "testabc"));
+
+ const base::FilePath dest_file =
+ temp_dir.GetPath().Append("dest").Append("destfile");
+ ASSERT_TRUE(SafeCopyFile(src_file, brillo::SafeFD::Root().first, dest_file,
+ brillo::SafeFD::Root().first));
+
+ const base::FilePath symlink = temp_dir.GetPath().Append("symlink");
+ ASSERT_TRUE(base::CreateSymbolicLink(dest_file, symlink));
+ ASSERT_FALSE(SafeCopyFile(src_file, brillo::SafeFD::Root().first, symlink,
+ brillo::SafeFD::Root().first));
+}
+
} // namespace arc