| // Copyright 2016 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <signal.h> |
| #include <sys/capability.h> |
| #include <sys/prctl.h> |
| |
| #include <base/at_exit.h> |
| #include <base/files/file_descriptor_watcher_posix.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/run_loop.h> |
| #include <base/stl_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| #include <base/task/single_thread_task_executor.h> |
| #include <brillo/syslog_logging.h> |
| #include <dbus/bus.h> |
| |
| #include "arc/obb-mounter/service.h" |
| |
| namespace { |
| |
| // Drops all capabilities except CAP_SYS_ADMIN (needed for fuse) and |
| // CAP_DAC_READ_SEARCH (needed to access /data/media/obb). |
| bool DropUnnecessaryCapabilities() { |
| const cap_value_t kKeep[2] = {CAP_SYS_ADMIN, CAP_DAC_READ_SEARCH}; |
| // Read cap_last_cap. |
| base::FilePath last_cap_path("/proc/sys/kernel/cap_last_cap"); |
| std::string contents; |
| int last_cap = 0; |
| if (!base::ReadFileToString(last_cap_path, &contents) || |
| !base::StringToInt( |
| base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING), |
| &last_cap)) { |
| LOG(ERROR) << "Failed to read cap_last_cap"; |
| return false; |
| } |
| // Drop cap bset. |
| for (int i = 0; i <= last_cap; ++i) { |
| if (std::count(kKeep, kKeep + base::size(kKeep), i) == 0) { |
| if (prctl(PR_CAPBSET_DROP, i)) { |
| PLOG(ERROR) << "Failed to drop bset " << i; |
| return false; |
| } |
| } |
| } |
| // Drop capabilities. |
| std::unique_ptr<std::remove_pointer<cap_t>::type, int (*)(void*)> cap( |
| cap_get_proc(), cap_free); |
| if (!cap) { |
| PLOG(ERROR) << "Failed to cap_get_proc()"; |
| return false; |
| } |
| if (cap_clear_flag(cap.get(), CAP_EFFECTIVE) || |
| cap_clear_flag(cap.get(), CAP_PERMITTED) || |
| cap_clear_flag(cap.get(), CAP_INHERITABLE)) { |
| PLOG(ERROR) << "Failed to cap_clear_flag()"; |
| return false; |
| } |
| if (cap_set_flag(cap.get(), CAP_EFFECTIVE, base::size(kKeep), kKeep, |
| CAP_SET) || |
| cap_set_flag(cap.get(), CAP_PERMITTED, base::size(kKeep), kKeep, |
| CAP_SET) || |
| cap_set_flag(cap.get(), CAP_INHERITABLE, base::size(kKeep), kKeep, |
| CAP_SET)) { |
| PLOG(ERROR) << "Failed to cap_set_flag()"; |
| return false; |
| } |
| if (cap_set_proc(cap.get())) { |
| PLOG(ERROR) << "Failed to cap_set_proc()"; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| // Not to make child processes zombies when they die. |
| signal(SIGCHLD, SIG_IGN); |
| |
| brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderr); |
| |
| CHECK(DropUnnecessaryCapabilities()); |
| |
| base::AtExitManager at_exit_manager; |
| base::SingleThreadTaskExecutor task_executor(base::MessagePumpType::IO); |
| base::FileDescriptorWatcher watcher(task_executor.task_runner()); |
| |
| // Connect the bus. |
| dbus::Bus::Options options; |
| options.bus_type = dbus::Bus::SYSTEM; |
| scoped_refptr<dbus::Bus> bus = new dbus::Bus(options); |
| CHECK(bus->Connect()); |
| |
| // Initialize the service. |
| arc::obb_mounter::Service service; |
| CHECK(service.Initialize(bus)); |
| |
| base::RunLoop().Run(); |
| return 0; |
| } |