audio_sandbox_hook_linux.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "services/audio/audio_sandbox_hook_linux.h"
  5. #include <dlfcn.h>
  6. #include <unistd.h>
  7. #include <string>
  8. #include <vector>
  9. #include "base/environment.h"
  10. #include "base/files/file_path.h"
  11. #include "base/files/file_util.h"
  12. #include "base/logging.h"
  13. #include "base/path_service.h"
  14. #include "base/strings/string_piece.h"
  15. #include "base/strings/stringprintf.h"
  16. #include "sandbox/linux/syscall_broker/broker_command.h"
  17. #include "sandbox/linux/syscall_broker/broker_file_permission.h"
  18. using sandbox::syscall_broker::BrokerFilePermission;
  19. using sandbox::syscall_broker::MakeBrokerCommandSet;
  20. namespace audio {
  21. namespace {
  22. #if defined(USE_ALSA)
  23. void AddAlsaFilePermissions(std::vector<BrokerFilePermission>* permissions) {
  24. base::FilePath home_dir;
  25. base::PathService::Get(base::DIR_HOME, &home_dir);
  26. const base::FilePath asoundrc =
  27. home_dir.Append(FILE_PATH_LITERAL(".asoundrc"));
  28. const std::string read_only_filenames[]{"/etc/asound.conf", "/proc/cpuinfo",
  29. "/etc/group", "/etc/nsswitch.conf",
  30. asoundrc.value()};
  31. for (const auto& filename : read_only_filenames)
  32. permissions->push_back(BrokerFilePermission::ReadOnly(filename));
  33. permissions->push_back(
  34. BrokerFilePermission::ReadOnlyRecursive("/usr/share/alsa/"));
  35. permissions->push_back(
  36. BrokerFilePermission::ReadWriteCreateRecursive("/dev/snd/"));
  37. static const base::FilePath::CharType dev_aload_path[] =
  38. FILE_PATH_LITERAL("/dev/aloadC");
  39. for (int i = 0; i <= 31; ++i) {
  40. permissions->push_back(BrokerFilePermission::ReadWrite(
  41. base::StringPrintf("%s%d", dev_aload_path, i)));
  42. }
  43. }
  44. #endif
  45. #if defined(USE_PULSEAUDIO)
  46. // Utility function used to grant permissions on paths used by PulseAudio which
  47. // are specified through environment variables. |recursive_only| is used to
  48. // determine if the path itself should be allowed access or only its content.
  49. void AllowAccessToEnvSpecifiedPath(
  50. base::StringPiece variable_name,
  51. std::vector<BrokerFilePermission>* permissions,
  52. bool recursive_only) {
  53. std::unique_ptr<base::Environment> env(base::Environment::Create());
  54. std::string path_value;
  55. if (!env->GetVar(variable_name, &path_value))
  56. return;
  57. const base::FilePath pa_config_path(path_value);
  58. if (pa_config_path.empty())
  59. return;
  60. if (!recursive_only) {
  61. permissions->push_back(BrokerFilePermission::ReadWriteCreate(
  62. pa_config_path.StripTrailingSeparators().value()));
  63. }
  64. permissions->push_back(BrokerFilePermission::ReadWriteCreateRecursive(
  65. pa_config_path.AsEndingWithSeparator().value()));
  66. }
  67. void AddPulseAudioFilePermissions(
  68. std::vector<BrokerFilePermission>* permissions) {
  69. base::FilePath home_dir;
  70. base::PathService::Get(base::DIR_HOME, &home_dir);
  71. const base::FilePath xauthority_path =
  72. home_dir.Append(FILE_PATH_LITERAL(".Xauthority"));
  73. // Calling read() system call on /proc/self/exe returns broker process' path,
  74. // and it's used by pulse audio for creating a new context.
  75. const std::string read_only_filenames[]{
  76. "/etc/machine-id", "/proc/self/exe",
  77. "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache",
  78. "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules", xauthority_path.value()};
  79. for (const auto& filename : read_only_filenames)
  80. permissions->push_back(BrokerFilePermission::ReadOnly(filename));
  81. // In certain situations, pulse runs stat() on the home directory.
  82. permissions->push_back(
  83. BrokerFilePermission::StatOnlyWithIntermediateDirs(home_dir.value()));
  84. permissions->push_back(
  85. BrokerFilePermission::ReadOnlyRecursive("/etc/pulse/"));
  86. // At times, Pulse tries to create the directory even if the directory already
  87. // exists and fails if the mkdir() operation returns anything other than
  88. // "success" or "exists".
  89. const char* pulse_home_dirs[] = {".pulse", ".config/pulse"};
  90. for (const char* pulse_home_dir : pulse_home_dirs) {
  91. const base::FilePath pulse_home_path = home_dir.Append(pulse_home_dir);
  92. permissions->push_back(
  93. BrokerFilePermission::ReadWriteCreate(pulse_home_path.value()));
  94. permissions->push_back(BrokerFilePermission::ReadWriteCreateRecursive(
  95. pulse_home_path.AsEndingWithSeparator().value()));
  96. }
  97. // Pulse might also need to create directories in tmp of the form
  98. // "/tmp/pulse-<random string>".
  99. permissions->push_back(
  100. BrokerFilePermission::ReadWriteCreateRecursive("/tmp/"));
  101. const char* env_tmp_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
  102. for (const char* env_tmp_path : env_tmp_paths) {
  103. AllowAccessToEnvSpecifiedPath(env_tmp_path, permissions,
  104. /*recursive_only=*/true);
  105. }
  106. // Read up the Pulse paths specified via environment variable and allow for
  107. // read/write/create recursively on the directory.
  108. const char* env_pulse_paths[] = {"PULSE_CONFIG_PATH", "PULSE_RUNTIME_PATH",
  109. "PULSE_STATE_PATH"};
  110. for (const char* env_pulse_path : env_pulse_paths) {
  111. AllowAccessToEnvSpecifiedPath(env_pulse_path, permissions,
  112. /*recursive_only=*/false);
  113. }
  114. const char* run_user_paths[] = {"/run/user", "/var/run/user"};
  115. for (const char* run_user_path : run_user_paths) {
  116. const std::string path =
  117. base::StringPrintf("%s/%d", run_user_path, getuid());
  118. permissions->push_back(BrokerFilePermission::ReadWriteCreate(path));
  119. permissions->push_back(
  120. BrokerFilePermission::ReadWriteCreate(path + "/pulse"));
  121. permissions->push_back(
  122. BrokerFilePermission::ReadWriteCreateRecursive(path + "/pulse/"));
  123. }
  124. }
  125. #endif
  126. std::vector<BrokerFilePermission> GetAudioFilePermissions() {
  127. std::vector<BrokerFilePermission> permissions{
  128. BrokerFilePermission::ReadOnly("/dev/urandom"),
  129. BrokerFilePermission::ReadOnly("/sys/devices/system/cpu"),
  130. BrokerFilePermission::ReadOnlyRecursive("/usr/share/locale/"),
  131. BrokerFilePermission::ReadWriteCreateRecursive("/dev/shm/")};
  132. #if defined(USE_PULSEAUDIO)
  133. AddPulseAudioFilePermissions(&permissions);
  134. #endif
  135. #if defined(USE_ALSA)
  136. AddAlsaFilePermissions(&permissions);
  137. #endif
  138. return permissions;
  139. }
  140. void LoadAudioLibraries() {
  141. const std::string libraries[]{"libasound.so.2", "libpulse.so.0",
  142. "libnss_files.so.2", "libnss_compat.so.2"};
  143. for (const auto& library_name : libraries) {
  144. if (nullptr ==
  145. dlopen(library_name.c_str(), RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE)) {
  146. LOG(WARNING) << "dlopen: failed to open " << library_name
  147. << " with error: " << dlerror();
  148. }
  149. }
  150. }
  151. } // namespace
  152. bool AudioPreSandboxHook(sandbox::policy::SandboxLinux::Options options) {
  153. LoadAudioLibraries();
  154. auto* instance = sandbox::policy::SandboxLinux::GetInstance();
  155. instance->StartBrokerProcess(MakeBrokerCommandSet({
  156. sandbox::syscall_broker::COMMAND_ACCESS,
  157. #if defined(USE_PULSEAUDIO)
  158. sandbox::syscall_broker::COMMAND_MKDIR,
  159. #endif
  160. sandbox::syscall_broker::COMMAND_OPEN,
  161. sandbox::syscall_broker::COMMAND_READLINK,
  162. sandbox::syscall_broker::COMMAND_STAT,
  163. sandbox::syscall_broker::COMMAND_UNLINK,
  164. }),
  165. GetAudioFilePermissions(),
  166. sandbox::policy::SandboxLinux::PreSandboxHook(),
  167. options);
  168. // TODO(https://crbug.com/850878) enable namespace sandbox. Currently, if
  169. // enabled, connect() on pulse native socket fails with ENOENT (called from
  170. // pa_context_connect).
  171. return true;
  172. }
  173. } // namespace audio