Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(588)

Side by Side Diff: ipc/ipc_channel_posix.cc

Issue 12386010: Implement IPC::ChannelFactory, a class that accept()s on a UNIX socket. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: un-revert fix to tests Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ipc/ipc_channel_posix.h ('k') | ipc/ipc_channel_posix_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « ipc/ipc_channel_posix.h ('k') | ipc/ipc_channel_posix_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698