Home | History | Annotate | Download | only in embedder
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "mojo/edk/embedder/platform_channel_utils_posix.h"
      6 
      7 #include <stddef.h>
      8 #include <sys/socket.h>
      9 #include <unistd.h>
     10 
     11 #include <utility>
     12 
     13 #include "base/files/file_util.h"
     14 #include "base/logging.h"
     15 #include "base/posix/eintr_wrapper.h"
     16 #include "build/build_config.h"
     17 #include "mojo/edk/embedder/scoped_platform_handle.h"
     18 
     19 #if !defined(OS_NACL)
     20 #include <sys/uio.h>
     21 #endif
     22 
     23 #if !defined(SO_PEEK_OFF)
     24 #define SO_PEEK_OFF 42
     25 #endif
     26 
     27 namespace mojo {
     28 namespace edk {
     29 namespace {
     30 
     31 #if !defined(OS_NACL)
     32 bool IsRecoverableError() {
     33   return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE ||
     34          errno == ENOMEM || errno == ENOBUFS;
     35 }
     36 
     37 bool GetPeerEuid(PlatformHandle handle, uid_t* peer_euid) {
     38   DCHECK(peer_euid);
     39 #if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD)
     40   uid_t socket_euid;
     41   gid_t socket_gid;
     42   if (getpeereid(handle.handle, &socket_euid, &socket_gid) < 0) {
     43     PLOG(ERROR) << "getpeereid " << handle.handle;
     44     return false;
     45   }
     46   *peer_euid = socket_euid;
     47   return true;
     48 #else
     49   struct ucred cred;
     50   socklen_t cred_len = sizeof(cred);
     51   if (getsockopt(handle.handle, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) <
     52       0) {
     53     PLOG(ERROR) << "getsockopt " << handle.handle;
     54     return false;
     55   }
     56   if (static_cast<unsigned>(cred_len) < sizeof(cred)) {
     57     NOTREACHED() << "Truncated ucred from SO_PEERCRED?";
     58     return false;
     59   }
     60   *peer_euid = cred.uid;
     61   return true;
     62 #endif
     63 }
     64 
     65 bool IsPeerAuthorized(PlatformHandle peer_handle) {
     66   uid_t peer_euid;
     67   if (!GetPeerEuid(peer_handle, &peer_euid))
     68     return false;
     69   if (peer_euid != geteuid()) {
     70     DLOG(ERROR) << "Client euid is not authorised";
     71     return false;
     72   }
     73   return true;
     74 }
     75 #endif  // !defined(OS_NACL)
     76 
     77 }  // namespace
     78 
     79 // On Linux, |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to
     80 // |send()|/|sendmsg()|. (There is no way of suppressing |SIGPIPE| on
     81 // |write()|/|writev().) On Mac, |SIGPIPE| is suppressed by setting the
     82 // |SO_NOSIGPIPE| option on the socket.
     83 //
     84 // Performance notes:
     85 //  - On Linux, we have to use |send()|/|sendmsg()| rather than
     86 //    |write()|/|writev()| in order to suppress |SIGPIPE|. This is okay, since
     87 //    |send()| is (slightly) faster than |write()| (!), while |sendmsg()| is
     88 //    quite comparable to |writev()|.
     89 //  - On Mac, we may use |write()|/|writev()|. Here, |write()| is considerably
     90 //    faster than |send()|, whereas |sendmsg()| is quite comparable to
     91 //    |writev()|.
     92 //  - On both platforms, an appropriate |sendmsg()|/|writev()| is considerably
     93 //    faster than two |send()|s/|write()|s.
     94 //  - Relative numbers (minimum real times from 10 runs) for one |write()| of
     95 //    1032 bytes, one |send()| of 1032 bytes, one |writev()| of 32+1000 bytes,
     96 //    one |sendmsg()| of 32+1000 bytes, two |write()|s of 32 and 1000 bytes, two
     97 //    |send()|s of 32 and 1000 bytes:
     98 //    - Linux: 0.81 s, 0.77 s, 0.87 s, 0.89 s, 1.31 s, 1.22 s
     99 //    - Mac: 2.21 s, 2.91 s, 2.98 s, 3.08 s, 3.59 s, 4.74 s
    100 
    101 // Flags to use with calling |send()| or |sendmsg()| (see above).
    102 #if defined(OS_MACOSX)
    103 const int kSendFlags = 0;
    104 #else
    105 const int kSendFlags = MSG_NOSIGNAL;
    106 #endif
    107 
    108 ssize_t PlatformChannelWrite(PlatformHandle h,
    109                              const void* bytes,
    110                              size_t num_bytes) {
    111   DCHECK(h.is_valid());
    112   DCHECK(bytes);
    113   DCHECK_GT(num_bytes, 0u);
    114 
    115 #if defined(OS_MACOSX) || defined(OS_NACL_NONSFI)
    116   // send() is not available under NaCl-nonsfi.
    117   return HANDLE_EINTR(write(h.handle, bytes, num_bytes));
    118 #else
    119   return send(h.handle, bytes, num_bytes, kSendFlags);
    120 #endif
    121 }
    122 
    123 ssize_t PlatformChannelWritev(PlatformHandle h,
    124                               struct iovec* iov,
    125                               size_t num_iov) {
    126   DCHECK(h.is_valid());
    127   DCHECK(iov);
    128   DCHECK_GT(num_iov, 0u);
    129 
    130 #if defined(OS_MACOSX)
    131   return HANDLE_EINTR(writev(h.handle, iov, static_cast<int>(num_iov)));
    132 #else
    133   struct msghdr msg = {};
    134   msg.msg_iov = iov;
    135   msg.msg_iovlen = num_iov;
    136   return HANDLE_EINTR(sendmsg(h.handle, &msg, kSendFlags));
    137 #endif
    138 }
    139 
    140 ssize_t PlatformChannelSendmsgWithHandles(PlatformHandle h,
    141                                           struct iovec* iov,
    142                                           size_t num_iov,
    143                                           PlatformHandle* platform_handles,
    144                                           size_t num_platform_handles) {
    145   DCHECK(iov);
    146   DCHECK_GT(num_iov, 0u);
    147   DCHECK(platform_handles);
    148   DCHECK_GT(num_platform_handles, 0u);
    149   DCHECK_LE(num_platform_handles, kPlatformChannelMaxNumHandles);
    150 
    151   char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
    152   struct msghdr msg = {};
    153   msg.msg_iov = iov;
    154   msg.msg_iovlen = num_iov;
    155   msg.msg_control = cmsg_buf;
    156   msg.msg_controllen = CMSG_LEN(num_platform_handles * sizeof(int));
    157   struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
    158   cmsg->cmsg_level = SOL_SOCKET;
    159   cmsg->cmsg_type = SCM_RIGHTS;
    160   cmsg->cmsg_len = CMSG_LEN(num_platform_handles * sizeof(int));
    161   for (size_t i = 0; i < num_platform_handles; i++) {
    162     DCHECK(platform_handles[i].is_valid());
    163     reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = platform_handles[i].handle;
    164   }
    165 
    166   return HANDLE_EINTR(sendmsg(h.handle, &msg, kSendFlags));
    167 }
    168 
    169 bool PlatformChannelSendHandles(PlatformHandle h,
    170                                 PlatformHandle* handles,
    171                                 size_t num_handles) {
    172   DCHECK(handles);
    173   DCHECK_GT(num_handles, 0u);
    174   DCHECK_LE(num_handles, kPlatformChannelMaxNumHandles);
    175 
    176   // Note: |sendmsg()| fails on Mac if we don't write at least one character.
    177   struct iovec iov = {const_cast<char*>(""), 1};
    178   char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
    179   struct msghdr msg = {};
    180   msg.msg_iov = &iov;
    181   msg.msg_iovlen = 1;
    182   msg.msg_control = cmsg_buf;
    183   msg.msg_controllen = CMSG_LEN(num_handles * sizeof(int));
    184   struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
    185   cmsg->cmsg_level = SOL_SOCKET;
    186   cmsg->cmsg_type = SCM_RIGHTS;
    187   cmsg->cmsg_len = CMSG_LEN(num_handles * sizeof(int));
    188   for (size_t i = 0; i < num_handles; i++) {
    189     DCHECK(handles[i].is_valid());
    190     reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = handles[i].handle;
    191   }
    192 
    193   ssize_t result = HANDLE_EINTR(sendmsg(h.handle, &msg, kSendFlags));
    194   if (result < 1) {
    195     DCHECK_EQ(result, -1);
    196     return false;
    197   }
    198 
    199   for (size_t i = 0; i < num_handles; i++)
    200     handles[i].CloseIfNecessary();
    201   return true;
    202 }
    203 
    204 ssize_t PlatformChannelRecvmsg(PlatformHandle h,
    205                                void* buf,
    206                                size_t num_bytes,
    207                                std::deque<PlatformHandle>* platform_handles,
    208                                bool block) {
    209   DCHECK(buf);
    210   DCHECK_GT(num_bytes, 0u);
    211   DCHECK(platform_handles);
    212 
    213   struct iovec iov = {buf, num_bytes};
    214   char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
    215   struct msghdr msg = {};
    216   msg.msg_iov = &iov;
    217   msg.msg_iovlen = 1;
    218   msg.msg_control = cmsg_buf;
    219   msg.msg_controllen = sizeof(cmsg_buf);
    220 
    221   ssize_t result =
    222       HANDLE_EINTR(recvmsg(h.handle, &msg, block ? 0 : MSG_DONTWAIT));
    223   if (result < 0)
    224     return result;
    225 
    226   // Success; no control messages.
    227   if (msg.msg_controllen == 0)
    228     return result;
    229 
    230   DCHECK(!(msg.msg_flags & MSG_CTRUNC));
    231 
    232   for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg;
    233        cmsg = CMSG_NXTHDR(&msg, cmsg)) {
    234     if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
    235       size_t payload_length = cmsg->cmsg_len - CMSG_LEN(0);
    236       DCHECK_EQ(payload_length % sizeof(int), 0u);
    237       size_t num_fds = payload_length / sizeof(int);
    238       const int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
    239       for (size_t i = 0; i < num_fds; i++) {
    240         platform_handles->push_back(PlatformHandle(fds[i]));
    241         DCHECK(platform_handles->back().is_valid());
    242       }
    243     }
    244   }
    245 
    246   return result;
    247 }
    248 
    249 bool ServerAcceptConnection(PlatformHandle server_handle,
    250                             ScopedPlatformHandle* connection_handle,
    251                             bool check_peer_user) {
    252   DCHECK(server_handle.is_valid());
    253   connection_handle->reset();
    254 #if defined(OS_NACL)
    255   NOTREACHED();
    256   return false;
    257 #else
    258   ScopedPlatformHandle accept_handle(
    259       PlatformHandle(HANDLE_EINTR(accept(server_handle.handle, NULL, 0))));
    260   if (!accept_handle.is_valid())
    261     return IsRecoverableError();
    262 
    263   // Verify that the IPC channel peer is running as the same user.
    264   if (check_peer_user && !IsPeerAuthorized(accept_handle.get())) {
    265     return true;
    266   }
    267 
    268   if (!base::SetNonBlocking(accept_handle.get().handle)) {
    269     PLOG(ERROR) << "base::SetNonBlocking() failed "
    270                 << accept_handle.get().handle;
    271     // It's safe to keep listening on |server_handle| even if the attempt to set
    272     // O_NONBLOCK failed on the client fd.
    273     return true;
    274   }
    275 
    276   *connection_handle = std::move(accept_handle);
    277   return true;
    278 #endif  // defined(OS_NACL)
    279 }
    280 
    281 }  // namespace edk
    282 }  // namespace mojo
    283