OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 | |
5 #include "sandbox/linux/syscall_broker/broker_host.h" | |
6 | |
7 #include <fcntl.h> | |
8 #include <sys/socket.h> | |
9 #include <sys/stat.h> | |
10 #include <sys/syscall.h> | |
11 #include <sys/types.h> | |
12 #include <unistd.h> | |
13 | |
14 #include <string> | |
15 #include <vector> | |
16 | |
17 #include "base/files/scoped_file.h" | |
18 #include "base/logging.h" | |
19 #include "base/pickle.h" | |
20 #include "base/posix/eintr_wrapper.h" | |
21 #include "base/posix/unix_domain_socket_linux.h" | |
22 #include "base/third_party/valgrind/valgrind.h" | |
23 #include "sandbox/linux/services/linux_syscalls.h" | |
24 #include "sandbox/linux/syscall_broker/broker_common.h" | |
25 #include "sandbox/linux/syscall_broker/broker_policy.h" | |
26 | |
27 namespace sandbox { | |
28 | |
29 namespace syscall_broker { | |
30 | |
31 namespace { | |
32 | |
33 bool IsRunningOnValgrind() { | |
34 return RUNNING_ON_VALGRIND; | |
35 } | |
36 | |
37 // A little open(2) wrapper to handle some oddities for us. In the general case | |
38 // make a direct system call since we want to keep in control of the broker | |
39 // process' system calls profile to be able to loosely sandbox it. | |
40 int sys_open(const char* pathname, int flags) { | |
41 // Always pass a defined |mode| in case flags mistakenly contains O_CREAT. | |
42 const int mode = 0; | |
43 if (IsRunningOnValgrind()) { | |
44 // Valgrind does not support AT_FDCWD, just use libc's open() in this case. | |
45 return open(pathname, flags, mode); | |
46 } else { | |
47 return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode); | |
48 } | |
49 } | |
50 | |
51 // Open |requested_filename| with |flags| if allowed by our policy. | |
52 // Write the syscall return value (-errno) to |write_pickle| and append | |
53 // a file descriptor to |opened_files| if relevant. | |
54 void OpenFileForIPC(const BrokerPolicy& policy, | |
55 const std::string& requested_filename, | |
56 int flags, | |
57 Pickle* write_pickle, | |
58 std::vector<int>* opened_files) { | |
59 DCHECK(write_pickle); | |
60 DCHECK(opened_files); | |
61 const char* file_to_open = NULL; | |
62 const bool safe_to_open_file = policy.GetFileNameIfAllowedToOpen( | |
63 requested_filename.c_str(), flags, &file_to_open); | |
64 | |
65 if (safe_to_open_file) { | |
66 CHECK(file_to_open); | |
67 int opened_fd = sys_open(file_to_open, flags); | |
68 if (opened_fd < 0) { | |
69 write_pickle->WriteInt(-errno); | |
70 } else { | |
71 // Success. | |
72 opened_files->push_back(opened_fd); | |
73 write_pickle->WriteInt(0); | |
74 } | |
75 } else { | |
76 write_pickle->WriteInt(-policy.denied_errno()); | |
77 } | |
78 } | |
79 | |
80 // Perform access(2) on |requested_filename| with mode |mode| if allowed by our | |
81 // policy. Write the syscall return value (-errno) to |write_pickle|. | |
82 void AccessFileForIPC(const BrokerPolicy& policy, | |
83 const std::string& requested_filename, | |
84 int mode, | |
85 Pickle* write_pickle) { | |
86 DCHECK(write_pickle); | |
87 const char* file_to_access = NULL; | |
88 const bool safe_to_access_file = policy.GetFileNameIfAllowedToAccess( | |
89 requested_filename.c_str(), mode, &file_to_access); | |
90 | |
91 if (safe_to_access_file) { | |
92 CHECK(file_to_access); | |
93 int access_ret = access(file_to_access, mode); | |
94 int access_errno = errno; | |
95 if (!access_ret) | |
96 write_pickle->WriteInt(0); | |
97 else | |
98 write_pickle->WriteInt(-access_errno); | |
leecam
2014/10/31 21:06:24
OpenFileForIPC just does -errno for consistency we
jln (very slow on Chromium)
2014/10/31 21:43:19
In OpenFileForIPC, we use errno immediately after
| |
99 } else { | |
100 write_pickle->WriteInt(-policy.denied_errno()); | |
101 } | |
102 } | |
103 | |
104 // Handle a |command_type| request contained in |read_pickle| and send the reply | |
105 // on |reply_ipc|. | |
106 // Currently kCommandOpen and kCommandAccess are supported. | |
107 bool HandleRemoteCommand(const BrokerPolicy& policy, | |
108 IPCCommands command_type, | |
109 int reply_ipc, | |
110 const Pickle& read_pickle, | |
111 PickleIterator iter) { | |
112 // Currently all commands have two arguments: filename and flags. | |
113 std::string requested_filename; | |
114 int flags = 0; | |
115 if (!read_pickle.ReadString(&iter, &requested_filename) || | |
116 !read_pickle.ReadInt(&iter, &flags)) { | |
117 return -1; | |
leecam
2014/10/31 21:06:25
return false
jln (very slow on Chromium)
2014/10/31 21:43:19
Done.
| |
118 } | |
119 | |
120 Pickle write_pickle; | |
121 std::vector<int> opened_files; | |
122 | |
123 switch (command_type) { | |
124 case kCommandAccess: | |
125 AccessFileForIPC(policy, requested_filename, flags, &write_pickle); | |
126 break; | |
127 case kCommandOpen: | |
128 OpenFileForIPC( | |
129 policy, requested_filename, flags, &write_pickle, &opened_files); | |
130 break; | |
131 default: | |
132 LOG(ERROR) << "Invalid IPC command"; | |
133 break; | |
leecam
2014/10/31 21:06:25
Should we return false here as we encountered a ba
jln (very slow on Chromium)
2014/10/31 21:43:19
May be worth considering, but I don't want to risk
| |
134 } | |
135 | |
136 CHECK_LE(write_pickle.size(), kMaxMessageLength); | |
137 ssize_t sent = UnixDomainSocket::SendMsg( | |
138 reply_ipc, write_pickle.data(), write_pickle.size(), opened_files); | |
139 | |
140 // Close anything we have opened in this process. | |
141 for (std::vector<int>::iterator it = opened_files.begin(); | |
142 it != opened_files.end(); | |
143 ++it) { | |
144 int ret = IGNORE_EINTR(close(*it)); | |
145 DCHECK(!ret) << "Could not close file descriptor"; | |
146 } | |
147 | |
148 if (sent <= 0) { | |
149 LOG(ERROR) << "Could not send IPC reply"; | |
150 return false; | |
151 } | |
152 return true; | |
153 } | |
154 | |
155 } // namespace | |
156 | |
157 BrokerHost::BrokerHost(const BrokerPolicy& broker_policy, int ipc_channel) | |
158 : broker_policy_(broker_policy), ipc_channel_(ipc_channel) { | |
159 } | |
160 | |
161 BrokerHost::~BrokerHost() { | |
162 } | |
163 | |
164 // Handle a request on the IPC channel ipc_channel_. | |
165 // A request should have a file descriptor attached on which we will reply and | |
166 // that we will then close. | |
167 // A request should start with an int that will be used as the command type. | |
168 bool BrokerHost::HandleRequest() const { | |
169 ScopedVector<base::ScopedFD> fds; | |
170 char buf[kMaxMessageLength]; | |
171 errno = 0; | |
172 const ssize_t msg_len = | |
173 UnixDomainSocket::RecvMsg(ipc_channel_, buf, sizeof(buf), &fds); | |
174 | |
175 if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) { | |
176 // EOF from our parent, or our parent died, we should die. | |
leecam
2014/10/31 21:06:25
Assuming the other end is our parent is an impleme
jln (very slow on Chromium)
2014/10/31 21:43:19
Done.
| |
177 _exit(0); | |
leecam
2014/10/31 21:06:25
Seems a little harsh :) Maybe return false or an e
jln (very slow on Chromium)
2014/10/31 21:43:19
Yep, for sure! Especially if we embed this in the
| |
178 } | |
179 | |
180 // The parent should send exactly one file descriptor, on which we | |
leecam
2014/10/31 21:06:25
parent=client
jln (very slow on Chromium)
2014/10/31 21:43:19
Done.
| |
181 // will write the reply. | |
182 // TODO(mdempsky): ScopedVector doesn't have 'at()', only 'operator[]'. | |
183 if (msg_len < 0 || fds.size() != 1 || fds[0]->get() < 0) { | |
184 PLOG(ERROR) << "Error reading message from the client"; | |
185 return false; | |
186 } | |
187 | |
188 base::ScopedFD temporary_ipc(fds[0]->Pass()); | |
189 | |
190 Pickle pickle(buf, msg_len); | |
191 PickleIterator iter(pickle); | |
192 int command_type; | |
193 if (pickle.ReadInt(&iter, &command_type)) { | |
194 bool r = false; | |
195 // Go through all the possible IPC messages. | |
196 switch (command_type) { | |
197 case kCommandAccess: | |
198 case kCommandOpen: | |
199 // We reply on the file descriptor sent to us via the IPC channel. | |
200 r = HandleRemoteCommand(broker_policy_, | |
201 static_cast<IPCCommands>(command_type), | |
202 temporary_ipc.get(), | |
203 pickle, | |
204 iter); | |
205 break; | |
206 default: | |
207 NOTREACHED(); | |
208 r = false; | |
209 break; | |
210 } | |
211 return r; | |
212 } | |
213 | |
214 LOG(ERROR) << "Error parsing IPC request"; | |
215 return false; | |
216 } | |
217 | |
218 } // namespace syscall_broker | |
219 | |
220 } // namespace sandbox | |
OLD | NEW |