diff options
Diffstat (limited to 'ipc/chromium/src/base/process_util_linux.cc')
-rw-r--r-- | ipc/chromium/src/base/process_util_linux.cc | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/process_util_linux.cc b/ipc/chromium/src/base/process_util_linux.cc new file mode 100644 index 000000000..204017db5 --- /dev/null +++ b/ipc/chromium/src/base/process_util_linux.cc @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium 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 "base/process_util.h" + +#include <ctype.h> +#include <fcntl.h> +#include <memory> +#include <unistd.h> +#include <string> +#include <sys/types.h> +#include <sys/wait.h> + +#include "base/eintr_wrapper.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "nsLiteralString.h" +#include "mozilla/UniquePtr.h" + +#include "prenv.h" +#include "prmem.h" + +#ifdef MOZ_WIDGET_GONK +/* + * AID_APP is the first application UID used by Android. We're using + * it as our unprivilegied UID. This ensure the UID used is not + * shared with any other processes than our own childs. + */ +# include <private/android_filesystem_config.h> +# define CHILD_UNPRIVILEGED_UID AID_APP +# define CHILD_UNPRIVILEGED_GID AID_APP +#else +/* + * On platforms that are not gonk based, we fall back to an arbitrary + * UID. This is generally the UID for user `nobody', albeit it is not + * always the case. + */ +# define CHILD_UNPRIVILEGED_UID 65534 +# define CHILD_UNPRIVILEGED_GID 65534 +#endif + +namespace { + +enum ParsingState { + KEY_NAME, + KEY_VALUE +}; + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +class EnvironmentEnvp +{ +public: + EnvironmentEnvp() + : mEnvp(PR_DuplicateEnvironment()) {} + + explicit EnvironmentEnvp(const environment_map &em) + { + mEnvp = (char **)PR_Malloc(sizeof(char *) * (em.size() + 1)); + if (!mEnvp) { + return; + } + char **e = mEnvp; + for (environment_map::const_iterator it = em.begin(); + it != em.end(); ++it, ++e) { + std::string str = it->first; + str += "="; + str += it->second; + size_t len = str.length() + 1; + *e = static_cast<char*>(PR_Malloc(len)); + memcpy(*e, str.c_str(), len); + } + *e = NULL; + } + + ~EnvironmentEnvp() + { + if (!mEnvp) { + return; + } + for (char **e = mEnvp; *e; ++e) { + PR_Free(*e); + } + PR_Free(mEnvp); + } + + char * const *AsEnvp() { return mEnvp; } + + void ToMap(environment_map &em) + { + if (!mEnvp) { + return; + } + em.clear(); + for (char **e = mEnvp; *e; ++e) { + const char *eq; + if ((eq = strchr(*e, '=')) != NULL) { + std::string varname(*e, eq - *e); + em[varname.c_str()] = &eq[1]; + } + } + } + +private: + char **mEnvp; +}; + +class Environment : public environment_map +{ +public: + Environment() + { + EnvironmentEnvp envp; + envp.ToMap(*this); + } + + char * const *AsEnvp() { + mEnvp.reset(new EnvironmentEnvp(*this)); + return mEnvp->AsEnvp(); + } + + void Merge(const environment_map &em) + { + for (const_iterator it = em.begin(); it != em.end(); ++it) { + (*this)[it->first] = it->second; + } + } +private: + std::auto_ptr<EnvironmentEnvp> mEnvp; +}; + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + return LaunchApp(argv, fds_to_remap, environment_map(), + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + return LaunchApp(argv, fds_to_remap, env_vars_to_set, + PRIVILEGES_INHERIT, + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + ChildPrivileges privs, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]); + // Illegal to allocate memory after fork and before execvp + InjectiveMultimap fd_shuffle1, fd_shuffle2; + fd_shuffle1.reserve(fds_to_remap.size()); + fd_shuffle2.reserve(fds_to_remap.size()); + + Environment env; + env.Merge(env_vars_to_set); + char * const *envp = env.AsEnvp(); + if (!envp) { + DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0]; + return false; + } + + pid_t pid = fork(); + if (pid < 0) + return false; + + if (pid == 0) { + for (file_handle_mapping_vector::const_iterator + it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { + fd_shuffle1.push_back(InjectionArc(it->first, it->second, false)); + fd_shuffle2.push_back(InjectionArc(it->first, it->second, false)); + } + + if (!ShuffleFileDescriptors(&fd_shuffle1)) + _exit(127); + + CloseSuperfluousFds(fd_shuffle2); + + for (size_t i = 0; i < argv.size(); i++) + argv_cstr[i] = const_cast<char*>(argv[i].c_str()); + argv_cstr[argv.size()] = NULL; + + SetCurrentProcessPrivileges(privs); + + execve(argv_cstr[0], argv_cstr.get(), envp); + // if we get here, we're in serious trouble and should complain loudly + // NOTE: This is async signal unsafe; it could deadlock instead. (But + // only on debug builds; otherwise it's a signal-safe no-op.) + DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0]; + _exit(127); + } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (wait) + HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) + *process_handle = pid; + } + + return true; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, + ProcessHandle* process_handle) { + file_handle_mapping_vector no_files; + return LaunchApp(cl.argv(), no_files, wait, process_handle); +} + +void SetCurrentProcessPrivileges(ChildPrivileges privs) { + if (privs == PRIVILEGES_INHERIT) { + return; + } + + gid_t gid = CHILD_UNPRIVILEGED_GID; + uid_t uid = CHILD_UNPRIVILEGED_UID; +#ifdef MOZ_WIDGET_GONK + { + static bool checked_pix_max, pix_max_ok; + if (!checked_pix_max) { + checked_pix_max = true; + int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY); + if (fd < 0) { + DLOG(ERROR) << "Failed to open pid_max"; + _exit(127); + } + char buf[PATH_MAX]; + ssize_t len = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (len < 0) { + DLOG(ERROR) << "Failed to read pid_max"; + _exit(127); + } + buf[len] = '\0'; + int pid_max = atoi(buf); + pix_max_ok = + (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID); + } + if (!pix_max_ok) { + DLOG(ERROR) << "Can't safely get unique uid/gid"; + _exit(127); + } + gid += getpid(); + uid += getpid(); + } +#endif + if (setgid(gid) != 0) { + DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS"; + _exit(127); + } + if (setuid(uid) != 0) { + DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS"; + _exit(127); + } + if (chdir("/") != 0) + gProcessLog.print("==> could not chdir()\n"); +} + +} // namespace base |