Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : // Copyright (c) 2008 The Chromium Authors. All rights reserved.
4 : // Use of this source code is governed by a BSD-style license that can be
5 : // found in the LICENSE file.
6 :
7 : #include "base/process_util.h"
8 :
9 : #include <ctype.h>
10 : #include <fcntl.h>
11 : #include <memory>
12 : #include <unistd.h>
13 : #include <string>
14 : #include <sys/types.h>
15 : #include <sys/wait.h>
16 :
17 : #include "base/eintr_wrapper.h"
18 : #include "base/file_util.h"
19 : #include "base/logging.h"
20 : #include "base/string_util.h"
21 : #include "nsLiteralString.h"
22 : #include "mozilla/UniquePtr.h"
23 :
24 : #include "prenv.h"
25 :
26 : #ifdef MOZ_WIDGET_GONK
27 : /*
28 : * AID_APP is the first application UID used by Android. We're using
29 : * it as our unprivilegied UID. This ensure the UID used is not
30 : * shared with any other processes than our own childs.
31 : */
32 : # include <private/android_filesystem_config.h>
33 : # define CHILD_UNPRIVILEGED_UID AID_APP
34 : # define CHILD_UNPRIVILEGED_GID AID_APP
35 : #else
36 : /*
37 : * On platforms that are not gonk based, we fall back to an arbitrary
38 : * UID. This is generally the UID for user `nobody', albeit it is not
39 : * always the case.
40 : */
41 : # define CHILD_UNPRIVILEGED_UID 65534
42 : # define CHILD_UNPRIVILEGED_GID 65534
43 : #endif
44 :
45 : namespace {
46 :
47 : enum ParsingState {
48 : KEY_NAME,
49 : KEY_VALUE
50 : };
51 :
52 3 : static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
53 :
54 : } // namespace
55 :
56 : namespace base {
57 :
58 : class EnvironmentEnvp
59 : {
60 : public:
61 3 : EnvironmentEnvp()
62 3 : : mEnvp(PR_DuplicateEnvironment()) {}
63 :
64 3 : explicit EnvironmentEnvp(const environment_map &em)
65 3 : {
66 3 : mEnvp = (char**) malloc(sizeof(char *) * (em.size() + 1));
67 3 : if (!mEnvp) {
68 0 : return;
69 : }
70 3 : char **e = mEnvp;
71 882 : for (environment_map::const_iterator it = em.begin();
72 588 : it != em.end(); ++it, ++e) {
73 582 : std::string str = it->first;
74 291 : str += "=";
75 291 : str += it->second;
76 291 : size_t len = str.length() + 1;
77 291 : *e = static_cast<char*>(malloc(len));
78 291 : memcpy(*e, str.c_str(), len);
79 : }
80 3 : *e = NULL;
81 : }
82 :
83 6 : ~EnvironmentEnvp()
84 6 : {
85 6 : if (!mEnvp) {
86 0 : return;
87 : }
88 588 : for (char **e = mEnvp; *e; ++e) {
89 582 : free(*e);
90 : }
91 6 : free(mEnvp);
92 6 : }
93 :
94 3 : char * const *AsEnvp() { return mEnvp; }
95 :
96 3 : void ToMap(environment_map &em)
97 : {
98 3 : if (!mEnvp) {
99 0 : return;
100 : }
101 3 : em.clear();
102 294 : for (char **e = mEnvp; *e; ++e) {
103 : const char *eq;
104 291 : if ((eq = strchr(*e, '=')) != NULL) {
105 582 : std::string varname(*e, eq - *e);
106 291 : em[varname.c_str()] = &eq[1];
107 : }
108 : }
109 : }
110 :
111 : private:
112 : char **mEnvp;
113 : };
114 :
115 3 : class Environment : public environment_map
116 : {
117 : public:
118 3 : Environment()
119 3 : {
120 6 : EnvironmentEnvp envp;
121 3 : envp.ToMap(*this);
122 3 : }
123 :
124 3 : char * const *AsEnvp() {
125 3 : mEnvp.reset(new EnvironmentEnvp(*this));
126 3 : return mEnvp->AsEnvp();
127 : }
128 :
129 3 : void Merge(const environment_map &em)
130 : {
131 9 : for (const_iterator it = em.begin(); it != em.end(); ++it) {
132 6 : (*this)[it->first] = it->second;
133 : }
134 3 : }
135 : private:
136 : std::auto_ptr<EnvironmentEnvp> mEnvp;
137 : };
138 :
139 1 : bool LaunchApp(const std::vector<std::string>& argv,
140 : const file_handle_mapping_vector& fds_to_remap,
141 : bool wait, ProcessHandle* process_handle) {
142 2 : return LaunchApp(argv, fds_to_remap, environment_map(),
143 2 : wait, process_handle);
144 : }
145 :
146 1 : bool LaunchApp(const std::vector<std::string>& argv,
147 : const file_handle_mapping_vector& fds_to_remap,
148 : const environment_map& env_vars_to_set,
149 : bool wait, ProcessHandle* process_handle,
150 : ProcessArchitecture arch) {
151 1 : return LaunchApp(argv, fds_to_remap, env_vars_to_set,
152 : PRIVILEGES_INHERIT,
153 1 : wait, process_handle);
154 : }
155 :
156 3 : bool LaunchApp(const std::vector<std::string>& argv,
157 : const file_handle_mapping_vector& fds_to_remap,
158 : const environment_map& env_vars_to_set,
159 : ChildPrivileges privs,
160 : bool wait, ProcessHandle* process_handle,
161 : ProcessArchitecture arch) {
162 9 : mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]);
163 : // Illegal to allocate memory after fork and before execvp
164 6 : InjectiveMultimap fd_shuffle1, fd_shuffle2;
165 3 : fd_shuffle1.reserve(fds_to_remap.size());
166 3 : fd_shuffle2.reserve(fds_to_remap.size());
167 :
168 6 : Environment env;
169 3 : env.Merge(env_vars_to_set);
170 3 : char * const *envp = env.AsEnvp();
171 3 : if (!envp) {
172 0 : DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0];
173 0 : return false;
174 : }
175 :
176 3 : pid_t pid = fork();
177 3 : if (pid < 0)
178 0 : return false;
179 :
180 3 : if (pid == 0) {
181 0 : for (file_handle_mapping_vector::const_iterator
182 0 : it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
183 0 : fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
184 0 : fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
185 : }
186 :
187 0 : if (!ShuffleFileDescriptors(&fd_shuffle1))
188 0 : _exit(127);
189 :
190 0 : CloseSuperfluousFds(fd_shuffle2);
191 :
192 0 : for (size_t i = 0; i < argv.size(); i++)
193 0 : argv_cstr[i] = const_cast<char*>(argv[i].c_str());
194 0 : argv_cstr[argv.size()] = NULL;
195 :
196 0 : SetCurrentProcessPrivileges(privs);
197 :
198 0 : execve(argv_cstr[0], argv_cstr.get(), envp);
199 : // if we get here, we're in serious trouble and should complain loudly
200 : // NOTE: This is async signal unsafe; it could deadlock instead. (But
201 : // only on debug builds; otherwise it's a signal-safe no-op.)
202 0 : DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
203 0 : _exit(127);
204 : } else {
205 3 : gProcessLog.print("==> process %d launched child process %d\n",
206 3 : GetCurrentProcId(), pid);
207 3 : if (wait)
208 1 : HANDLE_EINTR(waitpid(pid, 0, 0));
209 :
210 3 : if (process_handle)
211 3 : *process_handle = pid;
212 : }
213 :
214 3 : return true;
215 : }
216 :
217 0 : bool LaunchApp(const CommandLine& cl,
218 : bool wait, bool start_hidden,
219 : ProcessHandle* process_handle) {
220 0 : file_handle_mapping_vector no_files;
221 0 : return LaunchApp(cl.argv(), no_files, wait, process_handle);
222 : }
223 :
224 0 : void SetCurrentProcessPrivileges(ChildPrivileges privs) {
225 0 : if (privs == PRIVILEGES_INHERIT) {
226 0 : return;
227 : }
228 :
229 0 : gid_t gid = CHILD_UNPRIVILEGED_GID;
230 0 : uid_t uid = CHILD_UNPRIVILEGED_UID;
231 : #ifdef MOZ_WIDGET_GONK
232 : {
233 : static bool checked_pix_max, pix_max_ok;
234 : if (!checked_pix_max) {
235 : checked_pix_max = true;
236 : int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY);
237 : if (fd < 0) {
238 : DLOG(ERROR) << "Failed to open pid_max";
239 : _exit(127);
240 : }
241 : char buf[PATH_MAX];
242 : ssize_t len = read(fd, buf, sizeof(buf) - 1);
243 : close(fd);
244 : if (len < 0) {
245 : DLOG(ERROR) << "Failed to read pid_max";
246 : _exit(127);
247 : }
248 : buf[len] = '\0';
249 : int pid_max = atoi(buf);
250 : pix_max_ok =
251 : (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID);
252 : }
253 : if (!pix_max_ok) {
254 : DLOG(ERROR) << "Can't safely get unique uid/gid";
255 : _exit(127);
256 : }
257 : gid += getpid();
258 : uid += getpid();
259 : }
260 : #endif
261 0 : if (setgid(gid) != 0) {
262 0 : DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS";
263 0 : _exit(127);
264 : }
265 0 : if (setuid(uid) != 0) {
266 0 : DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS";
267 0 : _exit(127);
268 : }
269 0 : if (chdir("/") != 0)
270 0 : gProcessLog.print("==> could not chdir()\n");
271 : }
272 :
273 9 : } // namespace base
|