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" | |
44 | 43 |
45 namespace IPC { | 44 namespace IPC { |
46 | 45 |
47 // IPC channels on Windows use named pipes (CreateNamedPipe()) with | 46 // IPC channels on Windows use named pipes (CreateNamedPipe()) with |
48 // channel ids as the pipe names. Channels on POSIX use sockets as | 47 // channel ids as the pipe names. Channels on POSIX use sockets as |
49 // pipes These don't quite line up. | 48 // pipes These don't quite line up. |
50 // | 49 // |
51 // When creating a child subprocess we use a socket pair and the parent side of | 50 // When creating a child subprocess we use a socket pair and the parent side of |
52 // the fork arranges it such that the initial control channel ends up on the | 51 // the fork arranges it such that the initial control channel ends up on the |
53 // magic file descriptor kPrimaryIPCChannel in the child. Future | 52 // magic file descriptor kPrimaryIPCChannel in the child. Future |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 | 130 |
132 private: | 131 private: |
133 base::Lock lock_; | 132 base::Lock lock_; |
134 typedef std::map<std::string, int> ChannelToFDMap; | 133 typedef std::map<std::string, int> ChannelToFDMap; |
135 ChannelToFDMap map_; | 134 ChannelToFDMap map_; |
136 | 135 |
137 friend struct DefaultSingletonTraits<PipeMap>; | 136 friend struct DefaultSingletonTraits<PipeMap>; |
138 }; | 137 }; |
139 | 138 |
140 //------------------------------------------------------------------------------ | 139 //------------------------------------------------------------------------------ |
| 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 } |
141 | 277 |
142 bool SocketWriteErrorIsRecoverable() { | 278 bool SocketWriteErrorIsRecoverable() { |
143 #if defined(OS_MACOSX) | 279 #if defined(OS_MACOSX) |
144 // On OS X if sendmsg() is trying to send fds between processes and there | 280 // On OS X if sendmsg() is trying to send fds between processes and there |
145 // isn't enough room in the output buffer to send the fd structure over | 281 // isn't enough room in the output buffer to send the fd structure over |
146 // atomically then EMSGSIZE is returned. | 282 // atomically then EMSGSIZE is returned. |
147 // | 283 // |
148 // EMSGSIZE presents a problem since the system APIs can only call us when | 284 // EMSGSIZE presents a problem since the system APIs can only call us when |
149 // there's room in the socket buffer and not when there is "enough" room. | 285 // there's room in the socket buffer and not when there is "enough" room. |
150 // | 286 // |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 return false; | 381 return false; |
246 } | 382 } |
247 if (!(value & O_NONBLOCK)) { | 383 if (!(value & O_NONBLOCK)) { |
248 LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; | 384 LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; |
249 return false; | 385 return false; |
250 } | 386 } |
251 #endif // IPC_USES_READWRITE | 387 #endif // IPC_USES_READWRITE |
252 } else if (mode_ & MODE_NAMED_FLAG) { | 388 } else if (mode_ & MODE_NAMED_FLAG) { |
253 // Case 2 from comment above. | 389 // Case 2 from comment above. |
254 if (mode_ & MODE_SERVER_FLAG) { | 390 if (mode_ & MODE_SERVER_FLAG) { |
255 if (!CreateServerUnixDomainSocket(base::FilePath(pipe_name_), | 391 if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) { |
256 &local_pipe)) { | |
257 return false; | 392 return false; |
258 } | 393 } |
259 must_unlink_ = true; | 394 must_unlink_ = true; |
260 } else if (mode_ & MODE_CLIENT_FLAG) { | 395 } else if (mode_ & MODE_CLIENT_FLAG) { |
261 if (!CreateClientUnixDomainSocket(base::FilePath(pipe_name_), | 396 if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) { |
262 &local_pipe)) { | |
263 return false; | |
264 } | |
265 // Verify that the server has the same euid as the client. | |
266 if (!IPC::IsPeerAuthorized(local_pipe)) { | |
267 if (HANDLE_EINTR(close(local_pipe)) < 0) | |
268 PLOG(ERROR) << "close " << pipe_name_; | |
269 return false; | 397 return false; |
270 } | 398 } |
271 } else { | 399 } else { |
272 LOG(ERROR) << "Bad mode: " << mode_; | 400 LOG(ERROR) << "Bad mode: " << mode_; |
273 return false; | 401 return false; |
274 } | 402 } |
275 } else { | 403 } else { |
276 local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); | 404 local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); |
277 if (mode_ & MODE_CLIENT_FLAG) { | 405 if (mode_ & MODE_CLIENT_FLAG) { |
278 if (local_pipe != -1) { | 406 if (local_pipe != -1) { |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
539 } | 667 } |
540 | 668 |
541 bool Channel::ChannelImpl::AcceptsConnections() const { | 669 bool Channel::ChannelImpl::AcceptsConnections() const { |
542 return server_listen_pipe_ != -1; | 670 return server_listen_pipe_ != -1; |
543 } | 671 } |
544 | 672 |
545 bool Channel::ChannelImpl::HasAcceptedConnection() const { | 673 bool Channel::ChannelImpl::HasAcceptedConnection() const { |
546 return AcceptsConnections() && pipe_ != -1; | 674 return AcceptsConnections() && pipe_ != -1; |
547 } | 675 } |
548 | 676 |
549 bool Channel::ChannelImpl::GetPeerEuid(uid_t* peer_euid) const { | 677 bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { |
550 DCHECK(!(mode_ & MODE_SERVER) || HasAcceptedConnection()); | 678 DCHECK(HasAcceptedConnection()); |
551 return IPC::GetPeerEuid(pipe_, peer_euid); | 679 #if defined(OS_MACOSX) || defined(OS_OPENBSD) |
| 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 |
552 } | 704 } |
553 | 705 |
554 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { | 706 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { |
555 // Unregister libevent for the unix domain socket and close it. | 707 // Unregister libevent for the unix domain socket and close it. |
556 read_watcher_.StopWatchingFileDescriptor(); | 708 read_watcher_.StopWatchingFileDescriptor(); |
557 write_watcher_.StopWatchingFileDescriptor(); | 709 write_watcher_.StopWatchingFileDescriptor(); |
558 if (pipe_ != -1) { | 710 if (pipe_ != -1) { |
559 if (HANDLE_EINTR(close(pipe_)) < 0) | 711 if (HANDLE_EINTR(close(pipe_)) < 0) |
560 PLOG(ERROR) << "close pipe_ " << pipe_name_; | 712 PLOG(ERROR) << "close pipe_ " << pipe_name_; |
561 pipe_ = -1; | 713 pipe_ = -1; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
594 void Channel::ChannelImpl::SetGlobalPid(int pid) { | 746 void Channel::ChannelImpl::SetGlobalPid(int pid) { |
595 global_pid_ = pid; | 747 global_pid_ = pid; |
596 } | 748 } |
597 #endif // OS_LINUX | 749 #endif // OS_LINUX |
598 | 750 |
599 // Called by libevent when we can read from the pipe without blocking. | 751 // Called by libevent when we can read from the pipe without blocking. |
600 void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { | 752 void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { |
601 bool send_server_hello_msg = false; | 753 bool send_server_hello_msg = false; |
602 if (fd == server_listen_pipe_) { | 754 if (fd == server_listen_pipe_) { |
603 int new_pipe = 0; | 755 int new_pipe = 0; |
604 if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe) || | 756 if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) { |
605 new_pipe < 0) { | |
606 Close(); | 757 Close(); |
607 listener()->OnChannelListenError(); | 758 listener()->OnChannelListenError(); |
608 } | 759 } |
609 | 760 |
610 if (pipe_ != -1) { | 761 if (pipe_ != -1) { |
611 // We already have a connection. We only handle one at a time. | 762 // We already have a connection. We only handle one at a time. |
612 // close our new descriptor. | 763 // close our new descriptor. |
613 if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0) | 764 if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0) |
614 DPLOG(ERROR) << "shutdown " << pipe_name_; | 765 DPLOG(ERROR) << "shutdown " << pipe_name_; |
615 if (HANDLE_EINTR(close(new_pipe)) < 0) | 766 if (HANDLE_EINTR(close(new_pipe)) < 0) |
616 DPLOG(ERROR) << "close " << pipe_name_; | 767 DPLOG(ERROR) << "close " << pipe_name_; |
617 listener()->OnChannelDenied(); | 768 listener()->OnChannelDenied(); |
618 return; | 769 return; |
619 } | 770 } |
620 pipe_ = new_pipe; | 771 pipe_ = new_pipe; |
621 | 772 |
622 if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) { | 773 if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) { |
623 // Verify that the IPC channel peer is running as the same user. | 774 // Verify that the IPC channel peer is running as the same user. |
624 uid_t client_euid; | 775 uid_t client_euid; |
625 if (!GetPeerEuid(&client_euid)) { | 776 if (!GetClientEuid(&client_euid)) { |
626 DLOG(ERROR) << "Unable to query client euid"; | 777 DLOG(ERROR) << "Unable to query client euid"; |
627 ResetToAcceptingConnectionState(); | 778 ResetToAcceptingConnectionState(); |
628 return; | 779 return; |
629 } | 780 } |
630 if (client_euid != geteuid()) { | 781 if (client_euid != geteuid()) { |
631 DLOG(WARNING) << "Client euid is not authorised"; | 782 DLOG(WARNING) << "Client euid is not authorised"; |
632 ResetToAcceptingConnectionState(); | 783 ResetToAcceptingConnectionState(); |
633 return; | 784 return; |
634 } | 785 } |
635 } | 786 } |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
999 } | 1150 } |
1000 | 1151 |
1001 bool Channel::AcceptsConnections() const { | 1152 bool Channel::AcceptsConnections() const { |
1002 return channel_impl_->AcceptsConnections(); | 1153 return channel_impl_->AcceptsConnections(); |
1003 } | 1154 } |
1004 | 1155 |
1005 bool Channel::HasAcceptedConnection() const { | 1156 bool Channel::HasAcceptedConnection() const { |
1006 return channel_impl_->HasAcceptedConnection(); | 1157 return channel_impl_->HasAcceptedConnection(); |
1007 } | 1158 } |
1008 | 1159 |
1009 bool Channel::GetPeerEuid(uid_t* peer_euid) const { | 1160 bool Channel::GetClientEuid(uid_t* client_euid) const { |
1010 return channel_impl_->GetPeerEuid(peer_euid); | 1161 return channel_impl_->GetClientEuid(client_euid); |
1011 } | 1162 } |
1012 | 1163 |
1013 void Channel::ResetToAcceptingConnectionState() { | 1164 void Channel::ResetToAcceptingConnectionState() { |
1014 channel_impl_->ResetToAcceptingConnectionState(); | 1165 channel_impl_->ResetToAcceptingConnectionState(); |
1015 } | 1166 } |
1016 | 1167 |
1017 // static | 1168 // static |
1018 bool Channel::IsNamedServerInitialized(const std::string& channel_id) { | 1169 bool Channel::IsNamedServerInitialized(const std::string& channel_id) { |
1019 return ChannelImpl::IsNamedServerInitialized(channel_id); | 1170 return ChannelImpl::IsNamedServerInitialized(channel_id); |
1020 } | 1171 } |
(...skipping 12 matching lines...) Expand all Loading... |
1033 | 1184 |
1034 | 1185 |
1035 #if defined(OS_LINUX) | 1186 #if defined(OS_LINUX) |
1036 // static | 1187 // static |
1037 void Channel::SetGlobalPid(int pid) { | 1188 void Channel::SetGlobalPid(int pid) { |
1038 ChannelImpl::SetGlobalPid(pid); | 1189 ChannelImpl::SetGlobalPid(pid); |
1039 } | 1190 } |
1040 #endif // OS_LINUX | 1191 #endif // OS_LINUX |
1041 | 1192 |
1042 } // namespace IPC | 1193 } // namespace IPC |
OLD | NEW |