1 // Copyright 2018 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/public/cpp/platform/socket_utils_posix.h" 6 7 #include <stddef.h> 8 #include <sys/socket.h> 9 #include <unistd.h> 10 11 #if !defined(OS_NACL) 12 #include <sys/uio.h> 13 #endif 14 15 #include "base/files/file_util.h" 16 #include "base/logging.h" 17 #include "base/posix/eintr_wrapper.h" 18 #include "build/build_config.h" 19 20 namespace mojo { 21 22 namespace { 23 24 #if !defined(OS_NACL) 25 bool IsRecoverableError() { 26 return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE || 27 errno == ENOMEM || errno == ENOBUFS; 28 } 29 30 bool GetPeerEuid(base::PlatformFile fd, uid_t* peer_euid) { 31 #if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) 32 uid_t socket_euid; 33 gid_t socket_gid; 34 if (getpeereid(fd, &socket_euid, &socket_gid) < 0) { 35 PLOG(ERROR) << "getpeereid " << fd; 36 return false; 37 } 38 *peer_euid = socket_euid; 39 return true; 40 #else 41 struct ucred cred; 42 socklen_t cred_len = sizeof(cred); 43 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) { 44 PLOG(ERROR) << "getsockopt " << fd; 45 return false; 46 } 47 if (static_cast<unsigned>(cred_len) < sizeof(cred)) { 48 NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; 49 return false; 50 } 51 *peer_euid = cred.uid; 52 return true; 53 #endif 54 } 55 56 bool IsPeerAuthorized(base::PlatformFile fd) { 57 uid_t peer_euid; 58 if (!GetPeerEuid(fd, &peer_euid)) 59 return false; 60 if (peer_euid != geteuid()) { 61 DLOG(ERROR) << "Client euid is not authorized"; 62 return false; 63 } 64 return true; 65 } 66 #endif // !defined(OS_NACL) 67 68 // NOTE: On Linux |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to 69 // |sendmsg()|. On Mac we instead set |SO_NOSIGPIPE| on the socket itself. 70 #if defined(OS_MACOSX) 71 constexpr int kSendmsgFlags = 0; 72 #else 73 constexpr int kSendmsgFlags = MSG_NOSIGNAL; 74 #endif 75 76 constexpr size_t kMaxSendmsgHandles = 128; 77 78 } // namespace 79 80 ssize_t SocketWrite(base::PlatformFile socket, 81 const void* bytes, 82 size_t num_bytes) { 83 #if defined(OS_MACOSX) || defined(OS_NACL_NONSFI) 84 return HANDLE_EINTR(write(socket, bytes, num_bytes)); 85 #else 86 return send(socket, bytes, num_bytes, kSendmsgFlags); 87 #endif 88 } 89 90 ssize_t SocketWritev(base::PlatformFile socket, 91 struct iovec* iov, 92 size_t num_iov) { 93 #if defined(OS_MACOSX) 94 return HANDLE_EINTR(writev(socket, iov, static_cast<int>(num_iov))); 95 #else 96 struct msghdr msg = {}; 97 msg.msg_iov = iov; 98 msg.msg_iovlen = num_iov; 99 return HANDLE_EINTR(sendmsg(socket, &msg, kSendmsgFlags)); 100 #endif 101 } 102 103 ssize_t SendmsgWithHandles(base::PlatformFile socket, 104 struct iovec* iov, 105 size_t num_iov, 106 const std::vector<base::ScopedFD>& descriptors) { 107 DCHECK(iov); 108 DCHECK_GT(num_iov, 0u); 109 DCHECK(!descriptors.empty()); 110 DCHECK_LE(descriptors.size(), kMaxSendmsgHandles); 111 112 char cmsg_buf[CMSG_SPACE(kMaxSendmsgHandles * sizeof(int))]; 113 struct msghdr msg = {}; 114 msg.msg_iov = iov; 115 msg.msg_iovlen = num_iov; 116 msg.msg_control = cmsg_buf; 117 msg.msg_controllen = CMSG_LEN(descriptors.size() * sizeof(int)); 118 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); 119 cmsg->cmsg_level = SOL_SOCKET; 120 cmsg->cmsg_type = SCM_RIGHTS; 121 cmsg->cmsg_len = CMSG_LEN(descriptors.size() * sizeof(int)); 122 for (size_t i = 0; i < descriptors.size(); ++i) { 123 DCHECK_GE(descriptors[i].get(), 0); 124 reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = descriptors[i].get(); 125 } 126 return HANDLE_EINTR(sendmsg(socket, &msg, kSendmsgFlags)); 127 } 128 129 ssize_t SocketRecvmsg(base::PlatformFile socket, 130 void* buf, 131 size_t num_bytes, 132 std::vector<base::ScopedFD>* descriptors, 133 bool block) { 134 struct iovec iov = {buf, num_bytes}; 135 char cmsg_buf[CMSG_SPACE(kMaxSendmsgHandles * sizeof(int))]; 136 struct msghdr msg = {}; 137 msg.msg_iov = &iov; 138 msg.msg_iovlen = 1; 139 msg.msg_control = cmsg_buf; 140 msg.msg_controllen = sizeof(cmsg_buf); 141 ssize_t result = 142 HANDLE_EINTR(recvmsg(socket, &msg, block ? 0 : MSG_DONTWAIT)); 143 if (result < 0) 144 return result; 145 146 if (msg.msg_controllen == 0) 147 return result; 148 149 DCHECK(!(msg.msg_flags & MSG_CTRUNC)); 150 151 descriptors->clear(); 152 for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; 153 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 154 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { 155 size_t payload_length = cmsg->cmsg_len - CMSG_LEN(0); 156 DCHECK_EQ(payload_length % sizeof(int), 0u); 157 size_t num_fds = payload_length / sizeof(int); 158 const int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); 159 for (size_t i = 0; i < num_fds; ++i) { 160 base::ScopedFD fd(fds[i]); 161 DCHECK(fd.is_valid()); 162 descriptors->emplace_back(std::move(fd)); 163 } 164 } 165 } 166 167 return result; 168 } 169 170 bool AcceptSocketConnection(base::PlatformFile server_fd, 171 base::ScopedFD* connection_fd, 172 bool check_peer_user) { 173 DCHECK_GE(server_fd, 0); 174 connection_fd->reset(); 175 #if defined(OS_NACL) 176 NOTREACHED(); 177 return false; 178 #else 179 base::ScopedFD accepted_handle(HANDLE_EINTR(accept(server_fd, nullptr, 0))); 180 if (!accepted_handle.is_valid()) 181 return IsRecoverableError(); 182 if (check_peer_user && !IsPeerAuthorized(accepted_handle.get())) 183 return true; 184 if (!base::SetNonBlocking(accepted_handle.get())) { 185 PLOG(ERROR) << "base::SetNonBlocking() failed " << accepted_handle.get(); 186 return true; 187 } 188 189 *connection_fd = std::move(accepted_handle); 190 return true; 191 #endif // defined(OS_NACL) 192 } 193 194 } // namespace mojo 195