Skip to content

Commit

Permalink
[Cast][Mojo] Support UDS abstract namespace for NamedPlatformChannel
Browse files Browse the repository at this point in the history
Cast on Android uses a NamedPlatformChannel to interconnect the Cast
Service and Cast Browser components. To avoid filesystem permission
issues, Cast uses a Unix Domain Socket in the Linux abstract namespace.
Filesystem permissions are not necessary since the socket is already
guarded by the MediaShell security domain on SELinux.

Bug: b/229029761
Test: Install cast_service_bundle, launch HBO Now.

Change-Id: Ie703174508b5d94fff0a4dc987af7c138c7f7cc3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3595264
Reviewed-by: Joe Downing <joedow@chromium.org>
Reviewed-by: David Benjamin <davidben@chromium.org>
Reviewed-by: Junbo Ke <juke@chromium.org>
Reviewed-by: Ken Rockot <rockot@google.com>
Commit-Queue: Sean Topping <seantopping@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1001145}
  • Loading branch information
Sean Topping authored and Chromium LUCI CQ committed May 9, 2022
1 parent 0a94135 commit 07616d4
Show file tree
Hide file tree
Showing 18 changed files with 269 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,14 @@ class ExternalConnectorImpl::BrokerConnection
}

void AttemptBrokerConnection() {
mojo::NamedPlatformChannel::Options channel_options;
channel_options.server_name = broker_path_;
#if BUILDFLAG(IS_ANDROID)
// On Android, use the abstract namespace to avoid filesystem access.
channel_options.use_abstract_namespace = true;
#endif
mojo::PlatformChannelEndpoint endpoint =
mojo::NamedPlatformChannel::ConnectToServer(broker_path_);
mojo::NamedPlatformChannel::ConnectToServer(channel_options);
if (!endpoint.is_valid()) {
task_runner_->PostDelayedTask(
FROM_HERE,
Expand Down
4 changes: 4 additions & 0 deletions chromecast/external_mojo/public/cpp/external_mojo_broker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,10 @@ ExternalMojoBroker::ExternalMojoBroker(const std::string& broker_path) {

mojo::NamedPlatformChannel::Options channel_options;
channel_options.server_name = broker_path;
#if BUILDFLAG(IS_ANDROID)
// On Android, use the abstract namespace to avoid filesystem access.
channel_options.use_abstract_namespace = true;
#endif
mojo::NamedPlatformChannel named_channel(channel_options);

mojo::PlatformChannelServerEndpoint server_endpoint =
Expand Down
3 changes: 3 additions & 0 deletions mojo/public/cpp/platform/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ component("platform") {
"//mojo/public/c/system:headers",
]

deps = []

if (is_posix && !is_nacl && !is_mac) {
sources += [ "named_platform_channel_posix.cc" ]
deps += [ "//net" ]
}

if (is_mac) {
Expand Down
1 change: 1 addition & 0 deletions mojo/public/cpp/platform/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ include_rules = [
"-mojo",
"+mojo/public/c/system",
"+mojo/public/cpp/platform",
"+net/base",

# For some syscalls when building in NaCl toolchains.
"+native_client/src/public",
Expand Down
10 changes: 9 additions & 1 deletion mojo/public/cpp/platform/named_platform_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ void NamedPlatformChannel::PassServerNameOnCommandLine(
PlatformChannelEndpoint NamedPlatformChannel::ConnectToServer(
const ServerName& server_name) {
DCHECK(!server_name.empty());
return CreateClientEndpoint(server_name);
Options options = {.server_name = server_name};
return CreateClientEndpoint(options);
}

// static
PlatformChannelEndpoint NamedPlatformChannel::ConnectToServer(
const Options& options) {
DCHECK(!options.server_name.empty());
return CreateClientEndpoint(options);
}

// static
Expand Down
11 changes: 9 additions & 2 deletions mojo/public/cpp/platform/named_platform_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) NamedPlatformChannel {
// with a random name. This controls the directory where that happens.
// Ignored if |server_name| was set explicitly.
base::FilePath socket_dir;

// Use an abstract socket address instead of a filesystem path.
bool use_abstract_namespace = false;
#endif
};

Expand Down Expand Up @@ -103,6 +106,11 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) NamedPlatformChannel {
[[nodiscard]] static PlatformChannelEndpoint ConnectToServer(
const ServerName& server_name);

// Like above, but passing an Options struct instead. |options.server_name|
// must be a non-empty string.
[[nodiscard]] static PlatformChannelEndpoint ConnectToServer(
const Options& options);

// Like above, but extracts the server name from |command_line| using the
// common |kNamedHandleSwitch| flag.
[[nodiscard]] static PlatformChannelEndpoint ConnectToServer(
Expand All @@ -112,8 +120,7 @@ class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) NamedPlatformChannel {
static PlatformChannelServerEndpoint CreateServerEndpoint(
const Options& options,
ServerName* server_name);
static PlatformChannelEndpoint CreateClientEndpoint(
const ServerName& server_name);
static PlatformChannelEndpoint CreateClientEndpoint(const Options& options);

ServerName server_name_;
PlatformChannelServerEndpoint server_endpoint_;
Expand Down
6 changes: 3 additions & 3 deletions mojo/public/cpp/platform/named_platform_channel_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(

// static
PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
const NamedPlatformChannel::ServerName& server_name) {
const Options& options) {
base::mac::ScopedMachSendRight send_right;
kern_return_t kr = bootstrap_look_up(
bootstrap_port, server_name.c_str(),
bootstrap_port, options.server_name.c_str(),
base::mac::ScopedMachSendRight::Receiver(send_right).get());
if (kr != KERN_SUCCESS) {
BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << server_name;
BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << options.server_name;
return PlatformChannelEndpoint();
}

Expand Down
56 changes: 26 additions & 30 deletions mojo/public/cpp/platform/named_platform_channel_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "net/base/sockaddr_storage.h"
#include "net/base/sockaddr_util_posix.h"

namespace mojo {

Expand All @@ -28,33 +30,30 @@ NamedPlatformChannel::ServerName GenerateRandomServerName(
.value();
}

// This function fills in |unix_addr| with the appropriate data for the socket,
// and sets |unix_addr_len| to the length of the data therein.
// Returns true on success, or false on failure (typically because |server_name|
// violated the naming rules).
// This function fills in |addr_storage| with the appropriate data for the
// socket as well as the data's length. Returns true on success, or false on
// failure (typically because |server_name| violated the naming rules). On
// Linux and Android, setting |use_abstract_namespace| to true will return a
// socket address for an abstract non-filesystem socket.
bool MakeUnixAddr(const NamedPlatformChannel::ServerName& server_name,
struct sockaddr_un* unix_addr,
size_t* unix_addr_len) {
DCHECK(unix_addr);
DCHECK(unix_addr_len);
bool use_abstract_namespace,
net::SockaddrStorage* addr_storage) {
DCHECK(addr_storage);
DCHECK(!server_name.empty());

constexpr size_t kMaxSocketNameLength = 104;

// We reject server_name.length() == kMaxSocketNameLength to make room for the
// NUL terminator at the end of the string.
// NUL terminator at the end of the string. For the Linux abstract namespace,
// the path has a leading NUL character instead (with no NUL terminator
// required). In both cases N+1 bytes are needed to fill the server name.
if (server_name.length() >= kMaxSocketNameLength) {
LOG(ERROR) << "Socket name too long: " << server_name;
return false;
}

// Create unix_addr structure.
memset(unix_addr, 0, sizeof(struct sockaddr_un));
unix_addr->sun_family = AF_UNIX;
strncpy(unix_addr->sun_path, server_name.c_str(), kMaxSocketNameLength);
*unix_addr_len =
offsetof(struct sockaddr_un, sun_path) + server_name.length();
return true;
return net::FillUnixAddress(server_name, use_abstract_namespace,
addr_storage);
}

// This function creates a unix domain socket, and set it as non-blocking.
Expand Down Expand Up @@ -99,18 +98,16 @@ PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(
return PlatformChannelServerEndpoint();
}

struct sockaddr_un unix_addr;
size_t unix_addr_len;
if (!MakeUnixAddr(name, &unix_addr, &unix_addr_len))
net::SockaddrStorage storage;
if (!MakeUnixAddr(name, options.use_abstract_namespace, &storage))
return PlatformChannelServerEndpoint();

PlatformHandle handle = CreateUnixDomainSocket();
if (!handle.is_valid())
return PlatformChannelServerEndpoint();

// Bind the socket.
if (bind(handle.GetFD().get(), reinterpret_cast<const sockaddr*>(&unix_addr),
unix_addr_len) < 0) {
if (bind(handle.GetFD().get(), storage.addr, storage.addr_len) < 0) {
PLOG(ERROR) << "bind " << name;
return PlatformChannelServerEndpoint();
}
Expand All @@ -128,22 +125,21 @@ PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(

// static
PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
const ServerName& server_name) {
DCHECK(!server_name.empty());
const Options& options) {
DCHECK(!options.server_name.empty());

struct sockaddr_un unix_addr;
size_t unix_addr_len;
if (!MakeUnixAddr(server_name, &unix_addr, &unix_addr_len))
net::SockaddrStorage storage;
if (!MakeUnixAddr(options.server_name, options.use_abstract_namespace,
&storage))
return PlatformChannelEndpoint();

PlatformHandle handle = CreateUnixDomainSocket();
if (!handle.is_valid())
return PlatformChannelEndpoint();

if (HANDLE_EINTR(connect(handle.GetFD().get(),
reinterpret_cast<sockaddr*>(&unix_addr),
unix_addr_len)) < 0) {
PLOG(ERROR) << "connect " << server_name;
if (HANDLE_EINTR(
connect(handle.GetFD().get(), storage.addr, storage.addr_len)) < 0) {
PLOG(ERROR) << "connect " << options.server_name;
return PlatformChannelEndpoint();
}
return PlatformChannelEndpoint(std::move(handle));
Expand Down
4 changes: 2 additions & 2 deletions mojo/public/cpp/platform/named_platform_channel_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(

// static
PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
const ServerName& server_name) {
std::wstring pipe_name = GetPipeNameFromServerName(server_name);
const Options& options) {
std::wstring pipe_name = GetPipeNameFromServerName(options.server_name);

// Note: This may block.
if (!::WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT))
Expand Down
6 changes: 6 additions & 0 deletions net/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,8 @@ component("net") {
"base/file_stream_context_posix.cc",
"base/network_interfaces_posix.cc",
"base/network_interfaces_posix.h",
"base/sockaddr_util_posix.cc",
"base/sockaddr_util_posix.h",
"disk_cache/cache_util_posix.cc",
"disk_cache/simple/simple_util_posix.cc",
"http/url_security_manager_posix.cc",
Expand Down Expand Up @@ -4447,6 +4449,10 @@ test("net_unittests") {
deps += [ "//net/server:tests" ]
}

if (is_posix) {
sources += [ "base/sockaddr_util_posix_unittest.cc" ]
}

if (is_posix || is_fuchsia) {
sources += [ "socket/udp_socket_posix_unittest.cc" ]
}
Expand Down
55 changes: 55 additions & 0 deletions net/base/sockaddr_util_posix.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/sockaddr_util_posix.h"

#include <string.h>

#include <sys/socket.h>
#include <sys/un.h>

#include "build/build_config.h"
#include "net/base/sockaddr_storage.h"

namespace net {

bool FillUnixAddress(const std::string& socket_path,
bool use_abstract_namespace,
SockaddrStorage* address) {
// Caller should provide a non-empty path for the socket address.
if (socket_path.empty())
return false;

size_t path_max = address->addr_len - offsetof(struct sockaddr_un, sun_path);
// Non abstract namespace pathname should be null-terminated. Abstract
// namespace pathname must start with '\0'. So, the size is always greater
// than socket_path size by 1.
size_t path_size = socket_path.size() + 1;
if (path_size > path_max)
return false;

struct sockaddr_un* socket_addr =
reinterpret_cast<struct sockaddr_un*>(address->addr);
memset(socket_addr, 0, address->addr_len);
socket_addr->sun_family = AF_UNIX;
address->addr_len = path_size + offsetof(struct sockaddr_un, sun_path);
if (!use_abstract_namespace) {
memcpy(socket_addr->sun_path, socket_path.c_str(), socket_path.size());
return true;
}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// Convert the path given into abstract socket name. It must start with
// the '\0' character, so we are adding it. |addr_len| must specify the
// length of the structure exactly, as potentially the socket name may
// have '\0' characters embedded (although we don't support this).
// Note that addr.sun_path is already zero initialized.
memcpy(socket_addr->sun_path + 1, socket_path.c_str(), socket_path.size());
return true;
#else
return false;
#endif
}

} // namespace net
24 changes: 24 additions & 0 deletions net/base/sockaddr_util_posix.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_BASE_SOCKADDR_UTIL_POSIX_H_
#define NET_BASE_SOCKADDR_UTIL_POSIX_H_

#include <string>

#include "net/base/net_export.h"

namespace net {

struct SockaddrStorage;

// Fills |address| with |socket_path| and its length. For Android or Linux
// platform, this supports abstract namespaces.
NET_EXPORT bool FillUnixAddress(const std::string& socket_path,
bool use_abstract_namespace,
SockaddrStorage* address);

} // namespace net

#endif // NET_BASE_SOCKADDR_UTIL_POSIX_H_

0 comments on commit 07616d4

Please sign in to comment.