1// Copyright (c) 2012 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 "net/socket/unix_domain_socket_posix.h" 6 7#include <cstring> 8#include <string> 9 10#include <errno.h> 11#include <sys/socket.h> 12#include <sys/stat.h> 13#include <sys/types.h> 14#include <sys/un.h> 15#include <unistd.h> 16 17#include "base/bind.h" 18#include "base/callback.h" 19#include "base/posix/eintr_wrapper.h" 20#include "base/threading/platform_thread.h" 21#include "build/build_config.h" 22#include "net/base/net_errors.h" 23#include "net/base/net_util.h" 24 25namespace net { 26 27namespace { 28 29bool NoAuthenticationCallback(uid_t, gid_t) { 30 return true; 31} 32 33bool GetPeerIds(int socket, uid_t* user_id, gid_t* group_id) { 34#if defined(OS_LINUX) || defined(OS_ANDROID) 35 struct ucred user_cred; 36 socklen_t len = sizeof(user_cred); 37 if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) == -1) 38 return false; 39 *user_id = user_cred.uid; 40 *group_id = user_cred.gid; 41#else 42 if (getpeereid(socket, user_id, group_id) == -1) 43 return false; 44#endif 45 return true; 46} 47 48} // namespace 49 50// static 51UnixDomainSocket::AuthCallback NoAuthentication() { 52 return base::Bind(NoAuthenticationCallback); 53} 54 55// static 56UnixDomainSocket* UnixDomainSocket::CreateAndListenInternal( 57 const std::string& path, 58 const std::string& fallback_path, 59 StreamListenSocket::Delegate* del, 60 const AuthCallback& auth_callback, 61 bool use_abstract_namespace) { 62 SocketDescriptor s = CreateAndBind(path, use_abstract_namespace); 63 if (s == kInvalidSocket && !fallback_path.empty()) 64 s = CreateAndBind(fallback_path, use_abstract_namespace); 65 if (s == kInvalidSocket) 66 return NULL; 67 UnixDomainSocket* sock = new UnixDomainSocket(s, del, auth_callback); 68 sock->Listen(); 69 return sock; 70} 71 72// static 73scoped_refptr<UnixDomainSocket> UnixDomainSocket::CreateAndListen( 74 const std::string& path, 75 StreamListenSocket::Delegate* del, 76 const AuthCallback& auth_callback) { 77 return CreateAndListenInternal(path, "", del, auth_callback, false); 78} 79 80#if defined(SOCKET_ABSTRACT_NAMESPACE_SUPPORTED) 81// static 82scoped_refptr<UnixDomainSocket> 83UnixDomainSocket::CreateAndListenWithAbstractNamespace( 84 const std::string& path, 85 const std::string& fallback_path, 86 StreamListenSocket::Delegate* del, 87 const AuthCallback& auth_callback) { 88 return make_scoped_refptr( 89 CreateAndListenInternal(path, fallback_path, del, auth_callback, true)); 90} 91#endif 92 93UnixDomainSocket::UnixDomainSocket( 94 SocketDescriptor s, 95 StreamListenSocket::Delegate* del, 96 const AuthCallback& auth_callback) 97 : StreamListenSocket(s, del), 98 auth_callback_(auth_callback) {} 99 100UnixDomainSocket::~UnixDomainSocket() {} 101 102// static 103SocketDescriptor UnixDomainSocket::CreateAndBind(const std::string& path, 104 bool use_abstract_namespace) { 105 sockaddr_un addr; 106 static const size_t kPathMax = sizeof(addr.sun_path); 107 if (use_abstract_namespace + path.size() + 1 /* '\0' */ > kPathMax) 108 return kInvalidSocket; 109 const SocketDescriptor s = socket(PF_UNIX, SOCK_STREAM, 0); 110 if (s == kInvalidSocket) 111 return kInvalidSocket; 112 memset(&addr, 0, sizeof(addr)); 113 addr.sun_family = AF_UNIX; 114 socklen_t addr_len; 115 if (use_abstract_namespace) { 116 // Convert the path given into abstract socket name. It must start with 117 // the '\0' character, so we are adding it. |addr_len| must specify the 118 // length of the structure exactly, as potentially the socket name may 119 // have '\0' characters embedded (although we don't support this). 120 // Note that addr.sun_path is already zero initialized. 121 memcpy(addr.sun_path + 1, path.c_str(), path.size()); 122 addr_len = path.size() + offsetof(struct sockaddr_un, sun_path) + 1; 123 } else { 124 memcpy(addr.sun_path, path.c_str(), path.size()); 125 addr_len = sizeof(sockaddr_un); 126 } 127 if (bind(s, reinterpret_cast<sockaddr*>(&addr), addr_len)) { 128 LOG(ERROR) << "Could not bind unix domain socket to " << path; 129 if (use_abstract_namespace) 130 LOG(ERROR) << " (with abstract namespace enabled)"; 131 if (HANDLE_EINTR(close(s)) < 0) 132 LOG(ERROR) << "close() error"; 133 return kInvalidSocket; 134 } 135 return s; 136} 137 138void UnixDomainSocket::Accept() { 139 SocketDescriptor conn = StreamListenSocket::AcceptSocket(); 140 if (conn == kInvalidSocket) 141 return; 142 uid_t user_id; 143 gid_t group_id; 144 if (!GetPeerIds(conn, &user_id, &group_id) || 145 !auth_callback_.Run(user_id, group_id)) { 146 if (HANDLE_EINTR(close(conn)) < 0) 147 LOG(ERROR) << "close() error"; 148 return; 149 } 150 scoped_refptr<UnixDomainSocket> sock( 151 new UnixDomainSocket(conn, socket_delegate_, auth_callback_)); 152 // It's up to the delegate to AddRef if it wants to keep it around. 153 sock->WatchSocket(WAITING_READ); 154 socket_delegate_->DidAccept(this, sock.get()); 155} 156 157UnixDomainSocketFactory::UnixDomainSocketFactory( 158 const std::string& path, 159 const UnixDomainSocket::AuthCallback& auth_callback) 160 : path_(path), 161 auth_callback_(auth_callback) {} 162 163UnixDomainSocketFactory::~UnixDomainSocketFactory() {} 164 165scoped_refptr<StreamListenSocket> UnixDomainSocketFactory::CreateAndListen( 166 StreamListenSocket::Delegate* delegate) const { 167 return UnixDomainSocket::CreateAndListen( 168 path_, delegate, auth_callback_); 169} 170 171#if defined(SOCKET_ABSTRACT_NAMESPACE_SUPPORTED) 172 173UnixDomainSocketWithAbstractNamespaceFactory:: 174UnixDomainSocketWithAbstractNamespaceFactory( 175 const std::string& path, 176 const std::string& fallback_path, 177 const UnixDomainSocket::AuthCallback& auth_callback) 178 : UnixDomainSocketFactory(path, auth_callback), 179 fallback_path_(fallback_path) {} 180 181UnixDomainSocketWithAbstractNamespaceFactory:: 182~UnixDomainSocketWithAbstractNamespaceFactory() {} 183 184scoped_refptr<StreamListenSocket> 185UnixDomainSocketWithAbstractNamespaceFactory::CreateAndListen( 186 StreamListenSocket::Delegate* delegate) const { 187 return UnixDomainSocket::CreateAndListenWithAbstractNamespace( 188 path_, fallback_path_, delegate, auth_callback_); 189} 190 191#endif 192 193} // namespace net 194