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