| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 "ipc/unix_domain_socket_util.h" | |
| 6 | |
| 7 #include <errno.h> | |
| 8 #include <sys/socket.h> | |
| 9 #include <sys/un.h> | |
| 10 #include <unistd.h> | |
| 11 | |
| 12 #include "base/files/file_path.h" | |
| 13 #include "base/files/file_util.h" | |
| 14 #include "base/files/scoped_file.h" | |
| 15 #include "base/logging.h" | |
| 16 #include "base/posix/eintr_wrapper.h" | |
| 17 #include "build/build_config.h" | |
| 18 | |
| 19 namespace IPC { | |
| 20 | |
| 21 // Verify that kMaxSocketNameLength is a decent size. | |
| 22 static_assert(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxSocketNameLength, | |
| 23 "sun_path is too long."); | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 // This function fills in |unix_addr| with the appropriate data for the socket, | |
| 28 // and sets |unix_addr_len| to the length of the data therein. | |
| 29 // Returns true on success, or false on failure (typically because |socket_name| | |
| 30 // violated the naming rules). | |
| 31 bool MakeUnixAddrForPath(const std::string& socket_name, | |
| 32 struct sockaddr_un* unix_addr, | |
| 33 size_t* unix_addr_len) { | |
| 34 DCHECK(unix_addr); | |
| 35 DCHECK(unix_addr_len); | |
| 36 | |
| 37 if (socket_name.length() == 0) { | |
| 38 LOG(ERROR) << "Empty socket name provided for unix socket address."; | |
| 39 return false; | |
| 40 } | |
| 41 // We reject socket_name.length() == kMaxSocketNameLength to make room for | |
| 42 // the NUL terminator at the end of the string. | |
| 43 if (socket_name.length() >= kMaxSocketNameLength) { | |
| 44 LOG(ERROR) << "Socket name too long: " << socket_name; | |
| 45 return false; | |
| 46 } | |
| 47 | |
| 48 // Create unix_addr structure. | |
| 49 memset(unix_addr, 0, sizeof(struct sockaddr_un)); | |
| 50 unix_addr->sun_family = AF_UNIX; | |
| 51 strncpy(unix_addr->sun_path, socket_name.c_str(), kMaxSocketNameLength); | |
| 52 *unix_addr_len = | |
| 53 offsetof(struct sockaddr_un, sun_path) + socket_name.length(); | |
| 54 return true; | |
| 55 } | |
| 56 | |
| 57 // This functions creates a unix domain socket, and set it as non-blocking. | |
| 58 // If successful, |out_fd| will be set to the new file descriptor, and the | |
| 59 // function will return true. Otherwise returns false. | |
| 60 bool CreateUnixDomainSocket(base::ScopedFD* out_fd) { | |
| 61 DCHECK(out_fd); | |
| 62 | |
| 63 // Create the unix domain socket. | |
| 64 base::ScopedFD fd(socket(AF_UNIX, SOCK_STREAM, 0)); | |
| 65 if (!fd.is_valid()) { | |
| 66 PLOG(ERROR) << "Failed to create AF_UNIX socket."; | |
| 67 return false; | |
| 68 } | |
| 69 | |
| 70 // Now set it as non-blocking. | |
| 71 if (!base::SetNonBlocking(fd.get())) { | |
| 72 PLOG(ERROR) << "base::SetNonBlocking() failed " << fd.get(); | |
| 73 return false; | |
| 74 } | |
| 75 | |
| 76 fd.swap(*out_fd); | |
| 77 | |
| 78 return true; | |
| 79 } | |
| 80 | |
| 81 bool IsRecoverableError() { | |
| 82 return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE || | |
| 83 errno == ENOMEM || errno == ENOBUFS; | |
| 84 } | |
| 85 | |
| 86 } // namespace | |
| 87 | |
| 88 bool CreateServerUnixDomainSocket(const base::FilePath& socket_path, | |
| 89 int* server_listen_fd) { | |
| 90 DCHECK(server_listen_fd); | |
| 91 | |
| 92 // Make sure the path we need exists. | |
| 93 base::FilePath socket_dir = socket_path.DirName(); | |
| 94 if (!base::CreateDirectory(socket_dir)) { | |
| 95 LOG(ERROR) << "Couldn't create directory: " << socket_dir.value(); | |
| 96 return false; | |
| 97 } | |
| 98 | |
| 99 const std::string socket_name = socket_path.value(); | |
| 100 | |
| 101 // Delete any old FS instances. | |
| 102 if (unlink(socket_name.c_str()) < 0 && errno != ENOENT) { | |
| 103 PLOG(ERROR) << "unlink " << socket_name; | |
| 104 return false; | |
| 105 } | |
| 106 | |
| 107 struct sockaddr_un unix_addr; | |
| 108 size_t unix_addr_len; | |
| 109 if (!MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len)) | |
| 110 return false; | |
| 111 | |
| 112 base::ScopedFD fd; | |
| 113 if (!CreateUnixDomainSocket(&fd)) | |
| 114 return false; | |
| 115 | |
| 116 // Bind the socket. | |
| 117 if (bind(fd.get(), reinterpret_cast<const sockaddr*>(&unix_addr), | |
| 118 unix_addr_len) < 0) { | |
| 119 PLOG(ERROR) << "bind " << socket_name; | |
| 120 return false; | |
| 121 } | |
| 122 | |
| 123 // Start listening on the socket. | |
| 124 if (listen(fd.get(), SOMAXCONN) < 0) { | |
| 125 PLOG(ERROR) << "listen " << socket_name; | |
| 126 unlink(socket_name.c_str()); | |
| 127 return false; | |
| 128 } | |
| 129 | |
| 130 *server_listen_fd = fd.release(); | |
| 131 return true; | |
| 132 } | |
| 133 | |
| 134 bool CreateClientUnixDomainSocket(const base::FilePath& socket_path, | |
| 135 int* client_socket) { | |
| 136 DCHECK(client_socket); | |
| 137 | |
| 138 struct sockaddr_un unix_addr; | |
| 139 size_t unix_addr_len; | |
| 140 if (!MakeUnixAddrForPath(socket_path.value(), &unix_addr, &unix_addr_len)) | |
| 141 return false; | |
| 142 | |
| 143 base::ScopedFD fd; | |
| 144 if (!CreateUnixDomainSocket(&fd)) | |
| 145 return false; | |
| 146 | |
| 147 if (HANDLE_EINTR(connect(fd.get(), reinterpret_cast<sockaddr*>(&unix_addr), | |
| 148 unix_addr_len)) < 0) { | |
| 149 PLOG(ERROR) << "connect " << socket_path.value(); | |
| 150 return false; | |
| 151 } | |
| 152 | |
| 153 *client_socket = fd.release(); | |
| 154 return true; | |
| 155 } | |
| 156 | |
| 157 bool GetPeerEuid(int fd, uid_t* peer_euid) { | |
| 158 DCHECK(peer_euid); | |
| 159 #if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) | |
| 160 uid_t socket_euid; | |
| 161 gid_t socket_gid; | |
| 162 if (getpeereid(fd, &socket_euid, &socket_gid) < 0) { | |
| 163 PLOG(ERROR) << "getpeereid " << fd; | |
| 164 return false; | |
| 165 } | |
| 166 *peer_euid = socket_euid; | |
| 167 return true; | |
| 168 #else | |
| 169 struct ucred cred; | |
| 170 socklen_t cred_len = sizeof(cred); | |
| 171 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0) { | |
| 172 PLOG(ERROR) << "getsockopt " << fd; | |
| 173 return false; | |
| 174 } | |
| 175 if (static_cast<unsigned>(cred_len) < sizeof(cred)) { | |
| 176 NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; | |
| 177 return false; | |
| 178 } | |
| 179 *peer_euid = cred.uid; | |
| 180 return true; | |
| 181 #endif | |
| 182 } | |
| 183 | |
| 184 bool IsPeerAuthorized(int peer_fd) { | |
| 185 uid_t peer_euid; | |
| 186 if (!GetPeerEuid(peer_fd, &peer_euid)) | |
| 187 return false; | |
| 188 if (peer_euid != geteuid()) { | |
| 189 DLOG(ERROR) << "Client euid is not authorised"; | |
| 190 return false; | |
| 191 } | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 bool ServerOnConnect(int server_listen_fd, int* server_socket) { | |
| 196 DCHECK(server_socket); | |
| 197 *server_socket = -1; | |
| 198 | |
| 199 base::ScopedFD accept_fd(HANDLE_EINTR(accept(server_listen_fd, NULL, 0))); | |
| 200 if (!accept_fd.is_valid()) | |
| 201 return IsRecoverableError(); | |
| 202 | |
| 203 if (!base::SetNonBlocking(accept_fd.get())) { | |
| 204 PLOG(ERROR) << "base::SetNonBlocking() failed " << accept_fd.get(); | |
| 205 // It's safe to keep listening on |server_listen_fd| even if the attempt to | |
| 206 // set O_NONBLOCK failed on the client fd. | |
| 207 return true; | |
| 208 } | |
| 209 | |
| 210 *server_socket = accept_fd.release(); | |
| 211 return true; | |
| 212 } | |
| 213 | |
| 214 } // namespace IPC | |
| OLD | NEW |