Index: mojo/edk/embedder/platform_channel_utils_posix.cc |
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.cc b/mojo/edk/embedder/platform_channel_utils_posix.cc |
index 3171844f67c7efd3b85bfc6fc55ab91d67b9c292..9fda89ebb65c54920c63619b63333feb72f12ff4 100644 |
--- a/mojo/edk/embedder/platform_channel_utils_posix.cc |
+++ b/mojo/edk/embedder/platform_channel_utils_posix.cc |
@@ -8,9 +8,13 @@ |
#include <sys/socket.h> |
#include <unistd.h> |
+#include <utility> |
+ |
+#include "base/files/file_util.h" |
#include "base/logging.h" |
#include "base/posix/eintr_wrapper.h" |
#include "build/build_config.h" |
+#include "mojo/edk/embedder/scoped_platform_handle.h" |
#if !defined(OS_NACL) |
#include <sys/uio.h> |
@@ -22,6 +26,55 @@ |
namespace mojo { |
namespace edk { |
+namespace { |
+ |
+#if !defined(OS_NACL) |
+bool IsRecoverableError() { |
+ return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE || |
+ errno == ENOMEM || errno == ENOBUFS; |
+} |
+ |
+bool GetPeerEuid(PlatformHandle handle, uid_t* peer_euid) { |
+ DCHECK(peer_euid); |
+#if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) |
+ uid_t socket_euid; |
+ gid_t socket_gid; |
+ if (getpeereid(handle.handle, &socket_euid, &socket_gid) < 0) { |
+ PLOG(ERROR) << "getpeereid " << handle.handle; |
+ return false; |
+ } |
+ *peer_euid = socket_euid; |
+ return true; |
+#else |
+ struct ucred cred; |
+ socklen_t cred_len = sizeof(cred); |
+ if (getsockopt(handle.handle, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < |
+ 0) { |
+ PLOG(ERROR) << "getsockopt " << handle.handle; |
+ return false; |
+ } |
+ if (static_cast<unsigned>(cred_len) < sizeof(cred)) { |
+ NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; |
+ return false; |
+ } |
+ *peer_euid = cred.uid; |
+ return true; |
+#endif |
+} |
+ |
+bool IsPeerAuthorized(PlatformHandle peer_handle) { |
+ uid_t peer_euid; |
+ if (!GetPeerEuid(peer_handle, &peer_euid)) |
+ return false; |
+ if (peer_euid != geteuid()) { |
+ DLOG(ERROR) << "Client euid is not authorised"; |
+ return false; |
+ } |
+ return true; |
+} |
+#endif // !defined(OS_NACL) |
+ |
+} // namespace |
// On Linux, |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to |
// |send()|/|sendmsg()|. (There is no way of suppressing |SIGPIPE| on |
@@ -193,5 +246,36 @@ ssize_t PlatformChannelRecvmsg(PlatformHandle h, |
return result; |
} |
+bool ServerAcceptConnection(PlatformHandle server_handle, |
+ ScopedPlatformHandle* connection_handle) { |
+ DCHECK(server_handle.is_valid()); |
+ connection_handle->reset(); |
+#if defined(OS_NACL) |
+ NOTREACHED(); |
+ return false; |
+#else |
+ ScopedPlatformHandle accept_handle( |
+ PlatformHandle(HANDLE_EINTR(accept(server_handle.handle, NULL, 0)))); |
+ if (!accept_handle.is_valid()) |
+ return IsRecoverableError(); |
+ |
+ // Verify that the IPC channel peer is running as the same user. |
+ if (!IsPeerAuthorized(accept_handle.get())) { |
+ return true; |
+ } |
+ |
+ if (!base::SetNonBlocking(accept_handle.get().handle)) { |
+ PLOG(ERROR) << "base::SetNonBlocking() failed " |
+ << accept_handle.get().handle; |
+ // It's safe to keep listening on |server_handle| even if the attempt to set |
+ // O_NONBLOCK failed on the client fd. |
+ return true; |
+ } |
+ |
+ *connection_handle = std::move(accept_handle); |
+ return true; |
+#endif // defined(OS_NACL) |
+} |
+ |
} // namespace edk |
} // namespace mojo |