| 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 |