OLD | NEW |
---|---|
(Empty) | |
1 #include "sandbox/linux/services/broker_process.h" | |
2 | |
3 #include <fcntl.h> | |
4 #include <sys/socket.h> | |
5 #include <sys/stat.h> | |
6 #include <sys/types.h> | |
7 #include <unistd.h> | |
8 | |
9 #include <algorithm> | |
10 #include <string> | |
11 #include <vector> | |
12 | |
13 #include "base/logging.h" | |
14 #include "base/pickle.h" | |
15 #include "base/posix/eintr_wrapper.h" | |
16 #include "base/posix/unix_domain_socket.h" | |
17 | |
18 namespace { | |
19 | |
20 // Check whether |requested_filename| is in |allowed_file_names|. | |
21 // For paranoia, if file_to_open is not NULL, we will return the matching | |
22 // string for the white list. | |
Jorge Lucangeli Obes
2012/12/13 02:14:54
from?
jln (very slow on Chromium)
2012/12/13 02:33:14
Done.
| |
23 // Even if an attacker manager to fool the string comparison mechanism, we | |
Jorge Lucangeli Obes
2012/12/13 02:14:54
managed?
jln (very slow on Chromium)
2012/12/13 02:33:14
Done.
| |
24 // would not open an attacker-controlled file name. | |
25 bool GetFileNameInWhitelist(const std::vector<std::string>& allowed_file_names, | |
26 const std::string& requested_filename, | |
27 std::string* file_to_open) { | |
28 std::vector<std::string>::const_iterator it; | |
29 it = std::find(allowed_file_names.begin(), allowed_file_names.end(), | |
30 requested_filename); | |
31 if (it < allowed_file_names.end()) { // requested_filename was found? | |
32 if (file_to_open) | |
33 *file_to_open = *it; | |
34 return true; | |
35 } | |
36 return false; | |
37 } | |
38 | |
39 bool HandleOpenRequest(int fd, | |
40 const std::vector<std::string>& allowed_file_names, | |
41 const Pickle& read_pickle, PickleIterator iter) { | |
42 std::string requested_filename; | |
43 int flags = 0; | |
44 if (!read_pickle.ReadString(&iter, &requested_filename) || | |
45 !read_pickle.ReadInt(&iter, &flags)) { | |
46 return -1; | |
47 } | |
48 | |
49 Pickle write_pickle; | |
50 std::vector<int> opened_files; | |
51 std::string file_to_open; | |
52 bool file_is_in_whitelist = GetFileNameInWhitelist(allowed_file_names, | |
53 requested_filename, | |
54 &file_to_open); | |
55 if (file_is_in_whitelist && // requested_filename is in the whitelist? | |
56 flags == O_RDONLY) { | |
57 int opened_fd = open(file_to_open.c_str(), O_RDONLY); | |
58 if (opened_fd < 0) { | |
59 write_pickle.WriteInt(-errno); | |
60 } else { | |
61 // Success. | |
62 opened_files.push_back(opened_fd); | |
63 write_pickle.WriteInt(0); | |
64 } | |
65 } else { | |
66 write_pickle.WriteInt(-EPERM); | |
67 } | |
68 | |
69 const size_t kMaxMessageLength = sandbox::BrokerProcess::kMaxMessageLength; | |
70 CHECK_LE(write_pickle.size(), kMaxMessageLength); | |
71 ssize_t sent = UnixDomainSocket::SendMsg(fd, write_pickle.data(), | |
72 write_pickle.size(), opened_files); | |
73 | |
74 // Close anything we have opened in this process. | |
75 for (std::vector<int>::iterator it = opened_files.begin(); | |
76 it < opened_files.end(); ++it) { | |
77 (void) HANDLE_EINTR(close(*it)); | |
78 } | |
79 | |
80 if (sent <= 0) { | |
81 LOG(ERROR) << "Could not send IPC reply"; | |
82 return false; | |
83 } | |
84 return true; | |
85 } | |
86 | |
87 // Handle a request on the IPC channel FD. | |
88 // A request should have a file descriptor attached on which we will reply and | |
89 // that we will then close. | |
90 // A request should start with an int that will be used as the command type. | |
91 bool HandleRequest(int fd, | |
92 const std::vector<std::string>& allowed_file_names) { | |
93 | |
94 std::vector<int> fds; | |
95 char buf[sandbox::BrokerProcess::kMaxMessageLength]; | |
96 errno = 0; | |
97 const ssize_t msg_len = UnixDomainSocket::RecvMsg(fd, buf, | |
98 sizeof(buf), &fds); | |
99 | |
100 if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) { | |
101 // EOF from our parent, or our parent died, we should die. | |
102 _exit(0); | |
103 } | |
104 | |
105 // The parent should send exactly one file descriptor, on which we | |
106 // will write the reply. | |
107 if (msg_len < 0 || fds.size() != 1 || fds.at(0) < 0) { | |
108 PLOG(ERROR) << "Error reading message from the parent."; | |
109 return false; | |
110 } | |
111 | |
112 Pickle pickle(buf, msg_len); | |
113 PickleIterator iter(pickle); | |
114 int command_type; | |
115 if (pickle.ReadInt(&iter, &command_type)) { | |
116 bool r = false; | |
117 // Go through all the possible IPC messages. | |
118 switch (command_type) { | |
119 case sandbox::BrokerProcess::kCommandOpen: | |
120 // We reply on the file descriptor sent to us via the IPC channel. | |
121 r = HandleOpenRequest(fds.at(0), allowed_file_names, | |
122 pickle, iter); | |
123 // Close the temporary reply channel. | |
124 (void) HANDLE_EINTR(close(fds.at(0))); | |
125 return r; | |
126 default: | |
127 NOTREACHED(); | |
128 return false; | |
129 } | |
130 } | |
131 | |
132 LOG(ERROR) << "Error parsing Open request"; | |
Jorge Lucangeli Obes
2012/12/13 02:14:54
This should be a generic error right? It lives out
jln (very slow on Chromium)
2012/12/13 02:33:14
Good point, done.
| |
133 return false; | |
134 } | |
135 | |
136 } // namespace | |
137 | |
138 namespace sandbox { | |
139 | |
140 BrokerProcess::BrokerProcess(const std::vector<std::string>& allowed_file_names) | |
141 : initialized_(false), | |
142 is_child_(false), | |
143 fast_check_in_client_(true), | |
144 broker_pid_(-1), | |
145 allowed_file_names_(allowed_file_names), | |
146 ipc_socketpair_(-1) { | |
147 } | |
148 | |
149 BrokerProcess::BrokerProcess(const std::vector<std::string>& allowed_file_names, | |
150 bool fast_check_in_client) | |
151 : initialized_(false), | |
152 is_child_(false), | |
153 fast_check_in_client_(fast_check_in_client), | |
154 broker_pid_(-1), | |
155 allowed_file_names_(allowed_file_names), | |
156 ipc_socketpair_(-1) { | |
157 } | |
158 | |
159 BrokerProcess::~BrokerProcess() { | |
160 if (initialized_ && ipc_socketpair_ != -1) { | |
161 void (HANDLE_EINTR(close(ipc_socketpair_))); | |
162 } | |
163 } | |
164 | |
165 bool BrokerProcess::Init(void* sandbox_callback) { | |
166 CHECK(!initialized_); | |
167 CHECK_EQ(sandbox_callback, (void*) NULL) << | |
168 "sandbox_callback is not implemented"; | |
169 int socket_pair[2]; | |
170 if (socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair)) { | |
171 LOG(ERROR) << "Failed to create socketpair"; | |
172 return false; | |
173 } | |
174 | |
175 int child_pid = fork(); | |
176 if (child_pid == -1) { | |
177 (void) HANDLE_EINTR(close(socket_pair[0])); | |
178 (void) HANDLE_EINTR(close(socket_pair[1])); | |
179 return false; | |
180 } | |
181 if (child_pid) { | |
182 // We are the parent and we have just forked our broker process. | |
183 (void) HANDLE_EINTR(close(socket_pair[1])); | |
184 // We should only be able to write to the IPC channel. We'll always send | |
185 // a new file descriptor to receive the reply on. | |
186 shutdown(socket_pair[0], SHUT_RD); | |
187 ipc_socketpair_ = socket_pair[0]; | |
188 initialized_ = true; | |
189 is_child_ = false; | |
190 broker_pid_ = child_pid; | |
191 | |
192 } else { | |
193 // We are the broker. | |
194 (void) HANDLE_EINTR(close(socket_pair[0])); | |
195 // We should only be able to read from this IPC channel. We will send our | |
196 // replies on a new file descriptor attached to the requests. | |
197 shutdown(socket_pair[1], SHUT_WR); | |
198 ipc_socketpair_ = socket_pair[1]; | |
199 initialized_ = true; | |
200 is_child_ = true; | |
201 // TODO(jln): activate a sandbox here. | |
202 for (;;) { | |
203 HandleRequest(ipc_socketpair_, allowed_file_names_); | |
204 } | |
205 } | |
206 | |
207 initialized_ = true; | |
208 return true; | |
209 } | |
210 | |
211 int BrokerProcess::Open(const char* pathname, int flags) const { | |
212 CHECK(initialized_); | |
213 // There is no point in forwarding a request that we know will be denied. | |
214 // Of course, the real security check needs to be on the other side of the | |
215 // IPC. | |
216 if (fast_check_in_client_) { | |
217 if(!GetFileNameInWhitelist(allowed_file_names_, pathname, NULL) || | |
218 flags != O_RDONLY) { | |
219 return -EPERM; | |
220 } | |
221 } | |
222 | |
223 Pickle write_pickle; | |
224 write_pickle.WriteInt(kCommandOpen); | |
225 write_pickle.WriteString(pathname); | |
226 write_pickle.WriteInt(flags); | |
227 const size_t kMaxMessageLength = sandbox::BrokerProcess::kMaxMessageLength; | |
228 CHECK_LE(write_pickle.size(), kMaxMessageLength); | |
229 | |
230 int returned_fd = -1; | |
231 uint8_t reply_buf[kMaxMessageLength]; | |
232 // Send a request (in write_pickle) as well as a new temporary socketpair. | |
Jorge Lucangeli Obes
2012/12/13 02:14:54
Maybe clarify that SendRecvMsg takes care of creat
jln (very slow on Chromium)
2012/12/13 02:33:14
Done.
| |
233 // Then read the reply on this new socketpair in reply_buf and put an | |
234 // eventual attached file descriptor in returned_fd. | |
235 // TODO(jln): this API needs some rewriting and documentation. | |
236 ssize_t msg_len = UnixDomainSocket::SendRecvMsg(ipc_socketpair_, | |
237 reply_buf, | |
238 sizeof(reply_buf), | |
239 &returned_fd, | |
240 write_pickle); | |
241 if (msg_len <= 0) { | |
242 LOG(ERROR) << "Could not make request to broker process"; | |
243 return -ENOMEM; | |
244 } | |
245 | |
246 Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len); | |
247 PickleIterator iter(read_pickle); | |
248 int return_value = -1; | |
249 // Now deserialize the return value and eventually return the file | |
250 // descriptor. | |
251 if (read_pickle.ReadInt(&iter, &return_value)) { | |
252 if (return_value < 0) { | |
253 CHECK_EQ(returned_fd, -1); | |
254 return return_value; | |
255 } else { | |
256 // We have a real file descriptor to return. | |
257 CHECK_GE(returned_fd, 0); | |
258 return returned_fd; | |
259 } | |
260 } else { | |
261 LOG(ERROR) << "Could not read pickle"; | |
262 return -1; | |
263 } | |
264 } | |
265 | |
266 } // namespace sandbox. | |
OLD | NEW |