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