OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ipc/ipc_channel_posix.h" | 5 #include "ipc/ipc_channel_posix.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <fcntl.h> | 8 #include <fcntl.h> |
9 #include <stddef.h> | 9 #include <stddef.h> |
10 #include <sys/socket.h> | 10 #include <sys/socket.h> |
(...skipping 22 matching lines...) Expand all Loading... |
33 #include "base/rand_util.h" | 33 #include "base/rand_util.h" |
34 #include "base/stl_util.h" | 34 #include "base/stl_util.h" |
35 #include "base/string_util.h" | 35 #include "base/string_util.h" |
36 #include "base/synchronization/lock.h" | 36 #include "base/synchronization/lock.h" |
37 #include "ipc/file_descriptor_set_posix.h" | 37 #include "ipc/file_descriptor_set_posix.h" |
38 #include "ipc/ipc_descriptors.h" | 38 #include "ipc/ipc_descriptors.h" |
39 #include "ipc/ipc_listener.h" | 39 #include "ipc/ipc_listener.h" |
40 #include "ipc/ipc_logging.h" | 40 #include "ipc/ipc_logging.h" |
41 #include "ipc/ipc_message_utils.h" | 41 #include "ipc/ipc_message_utils.h" |
42 #include "ipc/ipc_switches.h" | 42 #include "ipc/ipc_switches.h" |
| 43 #include "ipc/unix_domain_socket_util.h" |
43 | 44 |
44 namespace IPC { | 45 namespace IPC { |
45 | 46 |
46 // IPC channels on Windows use named pipes (CreateNamedPipe()) with | 47 // IPC channels on Windows use named pipes (CreateNamedPipe()) with |
47 // channel ids as the pipe names. Channels on POSIX use sockets as | 48 // channel ids as the pipe names. Channels on POSIX use sockets as |
48 // pipes These don't quite line up. | 49 // pipes These don't quite line up. |
49 // | 50 // |
50 // When creating a child subprocess we use a socket pair and the parent side of | 51 // When creating a child subprocess we use a socket pair and the parent side of |
51 // the fork arranges it such that the initial control channel ends up on the | 52 // the fork arranges it such that the initial control channel ends up on the |
52 // magic file descriptor kPrimaryIPCChannel in the child. Future | 53 // magic file descriptor kPrimaryIPCChannel in the child. Future |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 | 131 |
131 private: | 132 private: |
132 base::Lock lock_; | 133 base::Lock lock_; |
133 typedef std::map<std::string, int> ChannelToFDMap; | 134 typedef std::map<std::string, int> ChannelToFDMap; |
134 ChannelToFDMap map_; | 135 ChannelToFDMap map_; |
135 | 136 |
136 friend struct DefaultSingletonTraits<PipeMap>; | 137 friend struct DefaultSingletonTraits<PipeMap>; |
137 }; | 138 }; |
138 | 139 |
139 //------------------------------------------------------------------------------ | 140 //------------------------------------------------------------------------------ |
140 // Verify that kMaxPipeNameLength is a decent size. | |
141 COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxPipeNameLength, | |
142 BAD_SUN_PATH_LENGTH); | |
143 | |
144 // Creates a unix domain socket bound to the specified name that is listening | |
145 // for connections. | |
146 bool CreateServerUnixDomainSocket(const std::string& pipe_name, | |
147 int* server_listen_fd) { | |
148 DCHECK(server_listen_fd); | |
149 | |
150 if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { | |
151 DLOG(ERROR) << "pipe_name.length() == " << pipe_name.length(); | |
152 return false; | |
153 } | |
154 | |
155 // Create socket. | |
156 int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
157 if (fd < 0) { | |
158 return false; | |
159 } | |
160 | |
161 // Make socket non-blocking | |
162 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { | |
163 PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; | |
164 if (HANDLE_EINTR(close(fd)) < 0) | |
165 PLOG(ERROR) << "close " << pipe_name; | |
166 return false; | |
167 } | |
168 | |
169 // Delete any old FS instances. | |
170 unlink(pipe_name.c_str()); | |
171 | |
172 // Make sure the path we need exists. | |
173 base::FilePath path(pipe_name); | |
174 base::FilePath dir_path = path.DirName(); | |
175 if (!file_util::CreateDirectory(dir_path)) { | |
176 if (HANDLE_EINTR(close(fd)) < 0) | |
177 PLOG(ERROR) << "close " << pipe_name; | |
178 return false; | |
179 } | |
180 | |
181 // Create unix_addr structure. | |
182 struct sockaddr_un unix_addr; | |
183 memset(&unix_addr, 0, sizeof(unix_addr)); | |
184 unix_addr.sun_family = AF_UNIX; | |
185 int path_len = snprintf(unix_addr.sun_path, IPC::kMaxPipeNameLength, | |
186 "%s", pipe_name.c_str()); | |
187 DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); | |
188 size_t unix_addr_len = offsetof(struct sockaddr_un, | |
189 sun_path) + path_len + 1; | |
190 | |
191 // Bind the socket. | |
192 if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr), | |
193 unix_addr_len) != 0) { | |
194 PLOG(ERROR) << "bind " << pipe_name; | |
195 if (HANDLE_EINTR(close(fd)) < 0) | |
196 PLOG(ERROR) << "close " << pipe_name; | |
197 return false; | |
198 } | |
199 | |
200 // Start listening on the socket. | |
201 const int listen_queue_length = 1; | |
202 if (listen(fd, listen_queue_length) != 0) { | |
203 PLOG(ERROR) << "listen " << pipe_name; | |
204 if (HANDLE_EINTR(close(fd)) < 0) | |
205 PLOG(ERROR) << "close " << pipe_name; | |
206 return false; | |
207 } | |
208 | |
209 *server_listen_fd = fd; | |
210 return true; | |
211 } | |
212 | |
213 // Accept a connection on a socket we are listening to. | |
214 bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { | |
215 DCHECK(server_socket); | |
216 | |
217 int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); | |
218 if (accept_fd < 0) | |
219 return false; | |
220 if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) { | |
221 PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; | |
222 if (HANDLE_EINTR(close(accept_fd)) < 0) | |
223 PLOG(ERROR) << "close " << accept_fd; | |
224 return false; | |
225 } | |
226 | |
227 *server_socket = accept_fd; | |
228 return true; | |
229 } | |
230 | |
231 bool CreateClientUnixDomainSocket(const std::string& pipe_name, | |
232 int* client_socket) { | |
233 DCHECK(client_socket); | |
234 DCHECK_GT(pipe_name.length(), 0u); | |
235 DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); | |
236 | |
237 if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { | |
238 return false; | |
239 } | |
240 | |
241 // Create socket. | |
242 int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
243 if (fd < 0) { | |
244 PLOG(ERROR) << "socket " << pipe_name; | |
245 return false; | |
246 } | |
247 | |
248 // Make socket non-blocking | |
249 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { | |
250 PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; | |
251 if (HANDLE_EINTR(close(fd)) < 0) | |
252 PLOG(ERROR) << "close " << pipe_name; | |
253 return false; | |
254 } | |
255 | |
256 // Create server side of socket. | |
257 struct sockaddr_un server_unix_addr; | |
258 memset(&server_unix_addr, 0, sizeof(server_unix_addr)); | |
259 server_unix_addr.sun_family = AF_UNIX; | |
260 int path_len = snprintf(server_unix_addr.sun_path, IPC::kMaxPipeNameLength, | |
261 "%s", pipe_name.c_str()); | |
262 DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); | |
263 size_t server_unix_addr_len = offsetof(struct sockaddr_un, | |
264 sun_path) + path_len + 1; | |
265 | |
266 if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr), | |
267 server_unix_addr_len)) != 0) { | |
268 PLOG(ERROR) << "connect " << pipe_name; | |
269 if (HANDLE_EINTR(close(fd)) < 0) | |
270 PLOG(ERROR) << "close " << pipe_name; | |
271 return false; | |
272 } | |
273 | |
274 *client_socket = fd; | |
275 return true; | |
276 } | |
277 | 141 |
278 bool SocketWriteErrorIsRecoverable() { | 142 bool SocketWriteErrorIsRecoverable() { |
279 #if defined(OS_MACOSX) | 143 #if defined(OS_MACOSX) |
280 // On OS X if sendmsg() is trying to send fds between processes and there | 144 // On OS X if sendmsg() is trying to send fds between processes and there |
281 // isn't enough room in the output buffer to send the fd structure over | 145 // isn't enough room in the output buffer to send the fd structure over |
282 // atomically then EMSGSIZE is returned. | 146 // atomically then EMSGSIZE is returned. |
283 // | 147 // |
284 // EMSGSIZE presents a problem since the system APIs can only call us when | 148 // EMSGSIZE presents a problem since the system APIs can only call us when |
285 // there's room in the socket buffer and not when there is "enough" room. | 149 // there's room in the socket buffer and not when there is "enough" room. |
286 // | 150 // |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 return false; | 245 return false; |
382 } | 246 } |
383 if (!(value & O_NONBLOCK)) { | 247 if (!(value & O_NONBLOCK)) { |
384 LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; | 248 LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; |
385 return false; | 249 return false; |
386 } | 250 } |
387 #endif // IPC_USES_READWRITE | 251 #endif // IPC_USES_READWRITE |
388 } else if (mode_ & MODE_NAMED_FLAG) { | 252 } else if (mode_ & MODE_NAMED_FLAG) { |
389 // Case 2 from comment above. | 253 // Case 2 from comment above. |
390 if (mode_ & MODE_SERVER_FLAG) { | 254 if (mode_ & MODE_SERVER_FLAG) { |
391 if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) { | 255 if (!CreateServerUnixDomainSocket(base::FilePath(pipe_name_), |
| 256 &local_pipe)) { |
392 return false; | 257 return false; |
393 } | 258 } |
394 must_unlink_ = true; | 259 must_unlink_ = true; |
395 } else if (mode_ & MODE_CLIENT_FLAG) { | 260 } else if (mode_ & MODE_CLIENT_FLAG) { |
396 if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) { | 261 if (!CreateClientUnixDomainSocket(base::FilePath(pipe_name_), |
| 262 &local_pipe)) { |
397 return false; | 263 return false; |
398 } | 264 } |
399 } else { | 265 } else { |
400 LOG(ERROR) << "Bad mode: " << mode_; | 266 LOG(ERROR) << "Bad mode: " << mode_; |
401 return false; | 267 return false; |
402 } | 268 } |
403 } else { | 269 } else { |
404 local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); | 270 local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); |
405 if (mode_ & MODE_CLIENT_FLAG) { | 271 if (mode_ & MODE_CLIENT_FLAG) { |
406 if (local_pipe != -1) { | 272 if (local_pipe != -1) { |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
667 } | 533 } |
668 | 534 |
669 bool Channel::ChannelImpl::AcceptsConnections() const { | 535 bool Channel::ChannelImpl::AcceptsConnections() const { |
670 return server_listen_pipe_ != -1; | 536 return server_listen_pipe_ != -1; |
671 } | 537 } |
672 | 538 |
673 bool Channel::ChannelImpl::HasAcceptedConnection() const { | 539 bool Channel::ChannelImpl::HasAcceptedConnection() const { |
674 return AcceptsConnections() && pipe_ != -1; | 540 return AcceptsConnections() && pipe_ != -1; |
675 } | 541 } |
676 | 542 |
677 bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { | 543 bool Channel::ChannelImpl::GetPeerEuid(uid_t* peer_euid) const { |
678 DCHECK(HasAcceptedConnection()); | 544 DCHECK(!(mode_ & MODE_SERVER) || HasAcceptedConnection()); |
679 #if defined(OS_MACOSX) || defined(OS_OPENBSD) | 545 return IPC::GetPeerEuid(pipe_, peer_euid); |
680 uid_t peer_euid; | |
681 gid_t peer_gid; | |
682 if (getpeereid(pipe_, &peer_euid, &peer_gid) != 0) { | |
683 PLOG(ERROR) << "getpeereid " << pipe_; | |
684 return false; | |
685 } | |
686 *client_euid = peer_euid; | |
687 return true; | |
688 #elif defined(OS_SOLARIS) | |
689 return false; | |
690 #else | |
691 struct ucred cred; | |
692 socklen_t cred_len = sizeof(cred); | |
693 if (getsockopt(pipe_, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) != 0) { | |
694 PLOG(ERROR) << "getsockopt " << pipe_; | |
695 return false; | |
696 } | |
697 if (static_cast<unsigned>(cred_len) < sizeof(cred)) { | |
698 NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; | |
699 return false; | |
700 } | |
701 *client_euid = cred.uid; | |
702 return true; | |
703 #endif | |
704 } | 546 } |
705 | 547 |
706 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { | 548 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { |
707 // Unregister libevent for the unix domain socket and close it. | 549 // Unregister libevent for the unix domain socket and close it. |
708 read_watcher_.StopWatchingFileDescriptor(); | 550 read_watcher_.StopWatchingFileDescriptor(); |
709 write_watcher_.StopWatchingFileDescriptor(); | 551 write_watcher_.StopWatchingFileDescriptor(); |
710 if (pipe_ != -1) { | 552 if (pipe_ != -1) { |
711 if (HANDLE_EINTR(close(pipe_)) < 0) | 553 if (HANDLE_EINTR(close(pipe_)) < 0) |
712 PLOG(ERROR) << "close pipe_ " << pipe_name_; | 554 PLOG(ERROR) << "close pipe_ " << pipe_name_; |
713 pipe_ = -1; | 555 pipe_ = -1; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
746 void Channel::ChannelImpl::SetGlobalPid(int pid) { | 588 void Channel::ChannelImpl::SetGlobalPid(int pid) { |
747 global_pid_ = pid; | 589 global_pid_ = pid; |
748 } | 590 } |
749 #endif // OS_LINUX | 591 #endif // OS_LINUX |
750 | 592 |
751 // Called by libevent when we can read from the pipe without blocking. | 593 // Called by libevent when we can read from the pipe without blocking. |
752 void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { | 594 void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { |
753 bool send_server_hello_msg = false; | 595 bool send_server_hello_msg = false; |
754 if (fd == server_listen_pipe_) { | 596 if (fd == server_listen_pipe_) { |
755 int new_pipe = 0; | 597 int new_pipe = 0; |
756 if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) { | 598 if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe) || |
| 599 new_pipe < 0) { |
757 Close(); | 600 Close(); |
758 listener()->OnChannelListenError(); | 601 listener()->OnChannelListenError(); |
759 } | 602 } |
760 | 603 |
761 if (pipe_ != -1) { | 604 if (pipe_ != -1) { |
762 // We already have a connection. We only handle one at a time. | 605 // We already have a connection. We only handle one at a time. |
763 // close our new descriptor. | 606 // close our new descriptor. |
764 if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0) | 607 if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0) |
765 DPLOG(ERROR) << "shutdown " << pipe_name_; | 608 DPLOG(ERROR) << "shutdown " << pipe_name_; |
766 if (HANDLE_EINTR(close(new_pipe)) < 0) | 609 if (HANDLE_EINTR(close(new_pipe)) < 0) |
767 DPLOG(ERROR) << "close " << pipe_name_; | 610 DPLOG(ERROR) << "close " << pipe_name_; |
768 listener()->OnChannelDenied(); | 611 listener()->OnChannelDenied(); |
769 return; | 612 return; |
770 } | 613 } |
771 pipe_ = new_pipe; | 614 pipe_ = new_pipe; |
772 | 615 |
773 if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) { | 616 if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) { |
774 // Verify that the IPC channel peer is running as the same user. | 617 // Verify that the IPC channel peer is running as the same user. |
775 uid_t client_euid; | 618 uid_t client_euid; |
776 if (!GetClientEuid(&client_euid)) { | 619 if (!GetPeerEuid(&client_euid)) { |
777 DLOG(ERROR) << "Unable to query client euid"; | 620 DLOG(ERROR) << "Unable to query client euid"; |
778 ResetToAcceptingConnectionState(); | 621 ResetToAcceptingConnectionState(); |
779 return; | 622 return; |
780 } | 623 } |
781 if (client_euid != geteuid()) { | 624 if (client_euid != geteuid()) { |
782 DLOG(WARNING) << "Client euid is not authorised"; | 625 DLOG(WARNING) << "Client euid is not authorised"; |
783 ResetToAcceptingConnectionState(); | 626 ResetToAcceptingConnectionState(); |
784 return; | 627 return; |
785 } | 628 } |
786 } | 629 } |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1150 } | 993 } |
1151 | 994 |
1152 bool Channel::AcceptsConnections() const { | 995 bool Channel::AcceptsConnections() const { |
1153 return channel_impl_->AcceptsConnections(); | 996 return channel_impl_->AcceptsConnections(); |
1154 } | 997 } |
1155 | 998 |
1156 bool Channel::HasAcceptedConnection() const { | 999 bool Channel::HasAcceptedConnection() const { |
1157 return channel_impl_->HasAcceptedConnection(); | 1000 return channel_impl_->HasAcceptedConnection(); |
1158 } | 1001 } |
1159 | 1002 |
1160 bool Channel::GetClientEuid(uid_t* client_euid) const { | 1003 bool Channel::GetPeerEuid(uid_t* peer_euid) const { |
1161 return channel_impl_->GetClientEuid(client_euid); | 1004 return channel_impl_->GetPeerEuid(peer_euid); |
1162 } | 1005 } |
1163 | 1006 |
1164 void Channel::ResetToAcceptingConnectionState() { | 1007 void Channel::ResetToAcceptingConnectionState() { |
1165 channel_impl_->ResetToAcceptingConnectionState(); | 1008 channel_impl_->ResetToAcceptingConnectionState(); |
1166 } | 1009 } |
1167 | 1010 |
1168 // static | 1011 // static |
1169 bool Channel::IsNamedServerInitialized(const std::string& channel_id) { | 1012 bool Channel::IsNamedServerInitialized(const std::string& channel_id) { |
1170 return ChannelImpl::IsNamedServerInitialized(channel_id); | 1013 return ChannelImpl::IsNamedServerInitialized(channel_id); |
1171 } | 1014 } |
(...skipping 12 matching lines...) Expand all Loading... |
1184 | 1027 |
1185 | 1028 |
1186 #if defined(OS_LINUX) | 1029 #if defined(OS_LINUX) |
1187 // static | 1030 // static |
1188 void Channel::SetGlobalPid(int pid) { | 1031 void Channel::SetGlobalPid(int pid) { |
1189 ChannelImpl::SetGlobalPid(pid); | 1032 ChannelImpl::SetGlobalPid(pid); |
1190 } | 1033 } |
1191 #endif // OS_LINUX | 1034 #endif // OS_LINUX |
1192 | 1035 |
1193 } // namespace IPC | 1036 } // namespace IPC |
OLD | NEW |