mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Now the network proxy client can automatically redirect connect syscalls to a handler that will send the data (syscall arguments) to the proxy server automatically and will return the obtained socket from the proxy server, in the future rules like allowed IP, protocols, etc. will be added
PiperOrigin-RevId: 259512665 Change-Id: I2747c7548ab24c7d2c90abb303fd783c11fed6f4
This commit is contained in:
parent
ae9836e6bf
commit
ef7592cfdd
|
@ -148,6 +148,7 @@ cc_library(
|
||||||
":comms",
|
":comms",
|
||||||
"//sandboxed_api/sandbox2/util:strerror",
|
"//sandboxed_api/sandbox2/util:strerror",
|
||||||
"//sandboxed_api/util:status",
|
"//sandboxed_api/util:status",
|
||||||
|
"@com_google_absl//absl/memory",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
"@com_google_absl//absl/synchronization",
|
"@com_google_absl//absl/synchronization",
|
||||||
"@com_google_glog//:glog",
|
"@com_google_glog//:glog",
|
||||||
|
@ -366,6 +367,7 @@ cc_library(
|
||||||
"//sandboxed_api/sandbox2/util:fileops",
|
"//sandboxed_api/sandbox2/util:fileops",
|
||||||
"//sandboxed_api/sandbox2/util:strerror",
|
"//sandboxed_api/sandbox2/util:strerror",
|
||||||
"//sandboxed_api/util:raw_logging",
|
"//sandboxed_api/util:raw_logging",
|
||||||
|
"//sandboxed_api/util:status",
|
||||||
"@com_google_absl//absl/base:core_headers",
|
"@com_google_absl//absl/base:core_headers",
|
||||||
"@com_google_absl//absl/memory",
|
"@com_google_absl//absl/memory",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
|
|
|
@ -40,9 +40,11 @@
|
||||||
#include "absl/strings/str_join.h"
|
#include "absl/strings/str_join.h"
|
||||||
#include "absl/strings/str_split.h"
|
#include "absl/strings/str_split.h"
|
||||||
#include "sandboxed_api/sandbox2/comms.h"
|
#include "sandboxed_api/sandbox2/comms.h"
|
||||||
|
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||||
#include "sandboxed_api/sandbox2/sanitizer.h"
|
#include "sandboxed_api/sandbox2/sanitizer.h"
|
||||||
#include "sandboxed_api/sandbox2/util/strerror.h"
|
#include "sandboxed_api/sandbox2/util/strerror.h"
|
||||||
#include "sandboxed_api/util/raw_logging.h"
|
#include "sandboxed_api/util/raw_logging.h"
|
||||||
|
#include "sandboxed_api/util/canonical_errors.h"
|
||||||
|
|
||||||
namespace sandbox2 {
|
namespace sandbox2 {
|
||||||
|
|
||||||
|
@ -256,4 +258,15 @@ NetworkProxyClient* Client::GetNetworkProxyClient() {
|
||||||
return proxy_client_.get();
|
return proxy_client_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sapi::Status Client::InstallNetworkProxyHandler() {
|
||||||
|
if (fd_map_.find(NetworkProxyClient::kFDName) == fd_map_.end()) {
|
||||||
|
return sapi::FailedPreconditionError(
|
||||||
|
"InstallNetworkProxyHandler() must be called at most once after the "
|
||||||
|
"sandbox is installed. Also, the NetworkProxyServer needs to be "
|
||||||
|
"enabled.");
|
||||||
|
}
|
||||||
|
return NetworkProxyHandler::InstallNetworkProxyHandler(
|
||||||
|
GetNetworkProxyClient());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
|
@ -58,6 +58,10 @@ class Client {
|
||||||
// for the first time.
|
// for the first time.
|
||||||
NetworkProxyClient* GetNetworkProxyClient();
|
NetworkProxyClient* GetNetworkProxyClient();
|
||||||
|
|
||||||
|
// Redirects the connect() syscall to the ConnectHandler() method in
|
||||||
|
// the NetworkProxyClient class.
|
||||||
|
sapi::Status InstallNetworkProxyHandler();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Comms used for synchronization with the monitor, not owned by the object.
|
// Comms used for synchronization with the monitor, not owned by the object.
|
||||||
Comms* comms_;
|
Comms* comms_;
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
if(SAPI_ENABLE_EXAMPLES)
|
if(SAPI_ENABLE_EXAMPLES)
|
||||||
add_subdirectory(crc4)
|
add_subdirectory(crc4)
|
||||||
add_subdirectory(custom_fork)
|
add_subdirectory(custom_fork)
|
||||||
|
add_subdirectory(network)
|
||||||
|
add_subdirectory(network_proxy)
|
||||||
add_subdirectory(static)
|
add_subdirectory(static)
|
||||||
add_subdirectory(tool)
|
add_subdirectory(tool)
|
||||||
add_subdirectory(zlib)
|
add_subdirectory(zlib)
|
||||||
|
|
52
sandboxed_api/sandbox2/examples/network/BUILD.bazel
Normal file
52
sandboxed_api/sandbox2/examples/network/BUILD.bazel
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# Copyright 2019 Google LLC. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# The 'network' example demonstrates:
|
||||||
|
# - separate executor and sandboxee
|
||||||
|
# - sandboxee enables sandboxing by calling SandboxMeHere()
|
||||||
|
# - strict syscall policy
|
||||||
|
# - sandbox2::Comms for data exchange (IPC)
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts")
|
||||||
|
|
||||||
|
# Executor
|
||||||
|
cc_binary(
|
||||||
|
name = "network_sandbox",
|
||||||
|
srcs = ["network_sandbox.cc"],
|
||||||
|
copts = sapi_platform_copts(),
|
||||||
|
data = [":network_bin"],
|
||||||
|
deps = [
|
||||||
|
"//sandboxed_api/sandbox2",
|
||||||
|
"//sandboxed_api/sandbox2:comms",
|
||||||
|
"//sandboxed_api/sandbox2/util:bpf_helper",
|
||||||
|
"//sandboxed_api/sandbox2/util:fileops",
|
||||||
|
"//sandboxed_api/sandbox2/util:runfiles",
|
||||||
|
"//sandboxed_api/util:flags",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Sandboxee
|
||||||
|
cc_binary(
|
||||||
|
name = "network_bin",
|
||||||
|
srcs = ["network_bin.cc"],
|
||||||
|
copts = sapi_platform_copts(),
|
||||||
|
deps = [
|
||||||
|
"//sandboxed_api/sandbox2:client",
|
||||||
|
"//sandboxed_api/sandbox2:comms",
|
||||||
|
"//sandboxed_api/sandbox2:util",
|
||||||
|
"@com_google_absl//absl/strings:str_format",
|
||||||
|
],
|
||||||
|
)
|
44
sandboxed_api/sandbox2/examples/network/CMakeLists.txt
Normal file
44
sandboxed_api/sandbox2/examples/network/CMakeLists.txt
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Copyright 2019 Google LLC. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# sandboxed_api/sandbox2/examples/network:network_sandbox
|
||||||
|
add_executable(sandbox2_network_sandbox
|
||||||
|
network_sandbox.cc
|
||||||
|
)
|
||||||
|
add_executable(sandbox2::network_sandbox ALIAS sandbox2_network_sandbox)
|
||||||
|
add_dependencies(sandbox2_network_sandbox
|
||||||
|
sandbox2::network_bin
|
||||||
|
)
|
||||||
|
target_link_libraries(sandbox2_network_sandbox PRIVATE
|
||||||
|
glog::glog
|
||||||
|
sandbox2::bpf_helper
|
||||||
|
sandbox2::comms
|
||||||
|
sandbox2::fileops
|
||||||
|
sandbox2::runfiles
|
||||||
|
sandbox2::sandbox2
|
||||||
|
sapi::base
|
||||||
|
)
|
||||||
|
|
||||||
|
# sandboxed_api/sandbox2/examples/network_bin:network_bin
|
||||||
|
add_executable(network_bin
|
||||||
|
network_bin.cc
|
||||||
|
)
|
||||||
|
add_executable(sandbox2::network_bin ALIAS network_bin)
|
||||||
|
target_link_libraries(network_bin PRIVATE
|
||||||
|
absl::str_format
|
||||||
|
sandbox2::client
|
||||||
|
sandbox2::comms
|
||||||
|
sandbox2::fileops
|
||||||
|
sapi::base
|
||||||
|
)
|
78
sandboxed_api/sandbox2/examples/network/network_bin.cc
Normal file
78
sandboxed_api/sandbox2/examples/network/network_bin.cc
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright 2019 Google LLC. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// This file is an example of a network sandboxed binary inside a network
|
||||||
|
// namespace. It can't connect with the server directly, but the executor can
|
||||||
|
// establish a connection and pass the connected socket to the sandboxee.
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <syscall.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "sandboxed_api/sandbox2/client.h"
|
||||||
|
#include "sandboxed_api/sandbox2/comms.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util.h"
|
||||||
|
|
||||||
|
static ssize_t ReadFromFd(int fd, uint8_t* buf, size_t size) {
|
||||||
|
ssize_t received = 0;
|
||||||
|
while (received < size) {
|
||||||
|
ssize_t read_status =
|
||||||
|
TEMP_FAILURE_RETRY(read(fd, &buf[received], size - received));
|
||||||
|
if (read_status == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (read_status < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
received += read_status;
|
||||||
|
}
|
||||||
|
return received;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CommunicationTest(int sock) {
|
||||||
|
char received[1025] = {0};
|
||||||
|
|
||||||
|
if (ReadFromFd(sock, reinterpret_cast<uint8_t*>(received),
|
||||||
|
sizeof(received) - 1) <= 0) {
|
||||||
|
LOG(ERROR) << "Data receiving error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
absl::PrintF("Sandboxee received data from the server:\n\n%s\n", received);
|
||||||
|
if (strcmp(received, "Hello World\n")) {
|
||||||
|
LOG(ERROR) << "Data receiving error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
// Set-up the sandbox2::Client object, using a file descriptor (1023).
|
||||||
|
sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
|
||||||
|
sandbox2::Client sandbox2_client(&comms);
|
||||||
|
// Enable sandboxing from here.
|
||||||
|
sandbox2_client.SandboxMeHere();
|
||||||
|
|
||||||
|
int client;
|
||||||
|
if (!comms.RecvFD(&client)) {
|
||||||
|
fputs("sandboxee: !comms.RecvFD(&client) failed\n", stderr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CommunicationTest(client)) return 2;
|
||||||
|
return 0;
|
||||||
|
}
|
237
sandboxed_api/sandbox2/examples/network/network_sandbox.cc
Normal file
237
sandboxed_api/sandbox2/examples/network/network_sandbox.cc
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
// Copyright 2019 Google LLC. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// A demo sandbox for the network binary.
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <syscall.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
#include "sandboxed_api/util/flag.h"
|
||||||
|
#include "sandboxed_api/sandbox2/comms.h"
|
||||||
|
#include "sandboxed_api/sandbox2/executor.h"
|
||||||
|
#include "sandboxed_api/sandbox2/ipc.h"
|
||||||
|
#include "sandboxed_api/sandbox2/policy.h"
|
||||||
|
#include "sandboxed_api/sandbox2/policybuilder.h"
|
||||||
|
#include "sandboxed_api/sandbox2/sandbox2.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/fileops.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/runfiles.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::unique_ptr<sandbox2::Policy> GetPolicy(absl::string_view sandboxee_path) {
|
||||||
|
return sandbox2::PolicyBuilder()
|
||||||
|
.EnableNamespaces()
|
||||||
|
.AllowExit()
|
||||||
|
.AllowMmap()
|
||||||
|
.AllowRead()
|
||||||
|
.AllowWrite()
|
||||||
|
.AllowSyscall(__NR_close)
|
||||||
|
.AllowSyscall(__NR_recvmsg) // RecvFD
|
||||||
|
.AllowSyscall(__NR_sendto) // send
|
||||||
|
.AllowStat() // printf,puts
|
||||||
|
.AddLibrariesForBinary(sandboxee_path)
|
||||||
|
.BuildOrDie();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server(int port) {
|
||||||
|
sandbox2::file_util::fileops::FDCloser s{
|
||||||
|
socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0)};
|
||||||
|
if (s.get() < 0) {
|
||||||
|
PLOG(ERROR) << "socket() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int enable = 1;
|
||||||
|
if (setsockopt(s.get(), SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) <
|
||||||
|
0) {
|
||||||
|
PLOG(ERROR) << "setsockopt(SO_REUSEADDR) failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to localhost only.
|
||||||
|
struct sockaddr_in6 addr = {};
|
||||||
|
addr.sin6_family = AF_INET6;
|
||||||
|
addr.sin6_port = htons(port);
|
||||||
|
|
||||||
|
int err = inet_pton(AF_INET6, "::1", &addr.sin6_addr.s6_addr);
|
||||||
|
if (err == 0) {
|
||||||
|
LOG(ERROR) << "inet_pton() failed";
|
||||||
|
return;
|
||||||
|
} else if (err == -1) {
|
||||||
|
PLOG(ERROR) << "inet_pton() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(s.get(), (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||||
|
PLOG(ERROR) << "bind() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(s.get(), 1) < 0) {
|
||||||
|
PLOG(ERROR) << "listen() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sandbox2::file_util::fileops::FDCloser client{accept(s.get(), 0, 0)};
|
||||||
|
if (client.get() < 0) {
|
||||||
|
PLOG(ERROR) << "accept() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char msg[] = "Hello World\n";
|
||||||
|
write(client.get(), msg, strlen(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectToServer(int port) {
|
||||||
|
int s = socket(AF_INET6, SOCK_STREAM, 0);
|
||||||
|
if (s < 0) {
|
||||||
|
PLOG(ERROR) << "socket() failed";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in6 saddr {};
|
||||||
|
saddr.sin6_family = AF_INET6;
|
||||||
|
saddr.sin6_port = htons(port);
|
||||||
|
|
||||||
|
int err = inet_pton(AF_INET6, "::1", &saddr.sin6_addr);
|
||||||
|
if (err == 0) {
|
||||||
|
LOG(ERROR) << "inet_pton() failed";
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
} else if (err == -1) {
|
||||||
|
PLOG(ERROR) << "inet_pton() failed";
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = connect(s, reinterpret_cast<const struct sockaddr*>(&saddr),
|
||||||
|
sizeof(saddr));
|
||||||
|
if (err != 0) {
|
||||||
|
LOG(ERROR) << "connect() failed";
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Connected to the server";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HandleSandboxee(sandbox2::Comms* comms, int port) {
|
||||||
|
// connect to the server and pass the file descriptor to sandboxee.
|
||||||
|
int client = ConnectToServer(port);
|
||||||
|
if (client <= 0) {
|
||||||
|
LOG(ERROR) << "connect_to_server() failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!comms->SendFD(client)) {
|
||||||
|
LOG(ERROR) << "sandboxee_comms->SendFD(client) failed";
|
||||||
|
close(client);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close(client);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
// This test is incompatible with sanitizers.
|
||||||
|
// The `SKIP_SANITIZERS_AND_COVERAGE` macro won't work for us here since we
|
||||||
|
// need to return something.
|
||||||
|
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
|
||||||
|
defined(THREAD_SANITIZER)
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
int port = 8085;
|
||||||
|
std::thread server_thread{Server,port};
|
||||||
|
server_thread.detach();
|
||||||
|
|
||||||
|
std::string path = sandbox2::GetInternalDataDependencyFilePath(
|
||||||
|
"sandbox2/examples/network/network_bin");
|
||||||
|
std::vector<std::string> args = {path};
|
||||||
|
std::vector<std::string> envs = {};
|
||||||
|
|
||||||
|
auto executor = absl::make_unique<sandbox2::Executor>(path, args, envs);
|
||||||
|
executor
|
||||||
|
// Sandboxing is enabled by the binary itself (i.e. the crc4bin is capable
|
||||||
|
// of enabling sandboxing on its own).
|
||||||
|
->set_enable_sandbox_before_exec(false)
|
||||||
|
// Set cwd to / to get rids of warnings connected with file namespace.
|
||||||
|
.set_cwd("/");
|
||||||
|
|
||||||
|
executor
|
||||||
|
->limits()
|
||||||
|
// Remove restrictions on the size of address-space of sandboxed
|
||||||
|
// processes.
|
||||||
|
->set_rlimit_as(RLIM64_INFINITY)
|
||||||
|
// Kill sandboxed processes with a signal (SIGXFSZ) if it writes more than
|
||||||
|
// these many bytes to the file-system.
|
||||||
|
.set_rlimit_fsize(10000)
|
||||||
|
.set_rlimit_cpu(100) // The CPU time limit in seconds
|
||||||
|
.set_walltime_limit(absl::Seconds(100));
|
||||||
|
|
||||||
|
auto policy = GetPolicy(path);
|
||||||
|
sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
|
||||||
|
auto* comms = s2.comms();
|
||||||
|
|
||||||
|
// Let the sandboxee run.
|
||||||
|
if (!s2.RunAsync()) {
|
||||||
|
auto result = s2.AwaitResult();
|
||||||
|
LOG(ERROR) << "RunAsync failed: " << result.ToString();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HandleSandboxee(comms, port)) {
|
||||||
|
if (!s2.IsTerminated()) {
|
||||||
|
// Kill the sandboxee, because failure to receive the data over the Comms
|
||||||
|
// channel doesn't automatically mean that the sandboxee itself had
|
||||||
|
// already finished. The final reason will not be overwritten, so if
|
||||||
|
// sandboxee finished because of e.g. timeout, the TIMEOUT reason will be
|
||||||
|
// reported.
|
||||||
|
LOG(INFO) << "Killing sandboxee";
|
||||||
|
s2.Kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = s2.AwaitResult();
|
||||||
|
if (result.final_status() != sandbox2::Result::OK) {
|
||||||
|
LOG(ERROR) << "Sandbox error: " << result.ToString();
|
||||||
|
return 3; // e.g. sandbox violation, signal (sigsegv).
|
||||||
|
}
|
||||||
|
auto code = result.reason_code();
|
||||||
|
if (code) {
|
||||||
|
LOG(ERROR) << "Sandboxee exited with non-zero: " << code;
|
||||||
|
return 4; // e.g. normal child error.
|
||||||
|
}
|
||||||
|
LOG(INFO) << "Sandboxee finished: " << result.ToString();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
49
sandboxed_api/sandbox2/examples/network_proxy/BUILD.bazel
Normal file
49
sandboxed_api/sandbox2/examples/network_proxy/BUILD.bazel
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Copyright 2019 Google LLC. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# The 'network proxy' example demonstrates how to use network proxy server.
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts")
|
||||||
|
|
||||||
|
# Executor
|
||||||
|
cc_binary(
|
||||||
|
name = "networkproxy_sandbox",
|
||||||
|
srcs = ["networkproxy_sandbox.cc"],
|
||||||
|
copts = sapi_platform_copts(),
|
||||||
|
data = [":networkproxy_bin"],
|
||||||
|
deps = [
|
||||||
|
"//sandboxed_api/sandbox2",
|
||||||
|
"//sandboxed_api/sandbox2:comms",
|
||||||
|
"//sandboxed_api/sandbox2/util:bpf_helper",
|
||||||
|
"//sandboxed_api/sandbox2/util:fileops",
|
||||||
|
"//sandboxed_api/sandbox2/util:runfiles",
|
||||||
|
"//sandboxed_api/util:flags",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Sandboxee
|
||||||
|
cc_binary(
|
||||||
|
name = "networkproxy_bin",
|
||||||
|
srcs = ["networkproxy_bin.cc"],
|
||||||
|
copts = sapi_platform_copts(),
|
||||||
|
deps = [
|
||||||
|
"//sandboxed_api/sandbox2:client",
|
||||||
|
"//sandboxed_api/sandbox2:comms",
|
||||||
|
"//sandboxed_api/sandbox2:network_proxy_client",
|
||||||
|
"//sandboxed_api/sandbox2/util:fileops",
|
||||||
|
"@com_google_absl//absl/strings:str_format",
|
||||||
|
],
|
||||||
|
)
|
45
sandboxed_api/sandbox2/examples/network_proxy/CMakeLists.txt
Normal file
45
sandboxed_api/sandbox2/examples/network_proxy/CMakeLists.txt
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Copyright 2019 Google LLC. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# sandboxed_api/sandbox2/examples/network_proxy:networkproxy_sandbox
|
||||||
|
add_executable(sandbox2_networkproxy_sandbox
|
||||||
|
networkproxy_sandbox.cc
|
||||||
|
)
|
||||||
|
add_executable(sandbox2::networkproxy_sandbox ALIAS sandbox2_networkproxy_sandbox)
|
||||||
|
add_dependencies(sandbox2_networkproxy_sandbox
|
||||||
|
sandbox2::networkproxy_bin
|
||||||
|
)
|
||||||
|
target_link_libraries(sandbox2_networkproxy_sandbox PRIVATE
|
||||||
|
glog::glog
|
||||||
|
sandbox2::bpf_helper
|
||||||
|
sandbox2::comms
|
||||||
|
sandbox2::fileops
|
||||||
|
sandbox2::runfiles
|
||||||
|
sandbox2::sandbox2
|
||||||
|
sapi::base
|
||||||
|
)
|
||||||
|
|
||||||
|
# sandboxed_api/sandbox2/examples/networkproxy_bin:networkproxy_bin
|
||||||
|
add_executable(networkproxy_bin
|
||||||
|
networkproxy_bin.cc
|
||||||
|
)
|
||||||
|
add_executable(sandbox2::networkproxy_bin ALIAS networkproxy_bin)
|
||||||
|
target_link_libraries(networkproxy_bin PRIVATE
|
||||||
|
absl::str_format
|
||||||
|
sandbox2::client
|
||||||
|
sandbox2::comms
|
||||||
|
sandbox2::fileops
|
||||||
|
sandbox2::network_proxy_client
|
||||||
|
sapi::base
|
||||||
|
)
|
|
@ -0,0 +1,117 @@
|
||||||
|
// This file is an example of a network sandboxed binary inside a network
|
||||||
|
// namespace. It can't connect with the server directly, but the executor can
|
||||||
|
// establish a connection and pass the connected socket to the sandboxee.
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <syscall.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "sandboxed_api/sandbox2/client.h"
|
||||||
|
#include "sandboxed_api/sandbox2/comms.h"
|
||||||
|
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/fileops.h"
|
||||||
|
|
||||||
|
static ssize_t ReadFromFd(int fd, uint8_t* buf, size_t size) {
|
||||||
|
ssize_t received = 0;
|
||||||
|
while (received < size) {
|
||||||
|
ssize_t read_status =
|
||||||
|
TEMP_FAILURE_RETRY(read(fd, &buf[received], size - received));
|
||||||
|
if (read_status == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (read_status < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
received += read_status;
|
||||||
|
}
|
||||||
|
return received;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CommunicationTest(int sock) {
|
||||||
|
char received[1025] = {0};
|
||||||
|
|
||||||
|
if (ReadFromFd(sock, reinterpret_cast<uint8_t*>(received),
|
||||||
|
sizeof(received) - 1) <= 0) {
|
||||||
|
LOG(ERROR) << "Data receiving error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
absl::PrintF("Sandboxee received data from the server:\n\n%s\n", received);
|
||||||
|
if (strcmp(received, "Hello World\n")) {
|
||||||
|
LOG(ERROR) << "Data receiving error";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ConnectToServer(int port) {
|
||||||
|
int s = socket(AF_INET6, SOCK_STREAM, 0);
|
||||||
|
if (s < 0) {
|
||||||
|
PLOG(ERROR) << "socket() failed";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in6 saddr {};
|
||||||
|
saddr.sin6_family = AF_INET6;
|
||||||
|
saddr.sin6_port = htons(port);
|
||||||
|
|
||||||
|
int err = inet_pton(AF_INET6, "::1", &saddr.sin6_addr);
|
||||||
|
if (err == 0) {
|
||||||
|
LOG(ERROR) << "inet_pton() failed";
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
} else if (err == -1) {
|
||||||
|
PLOG(ERROR) << "inet_pton() failed";
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = connect(s, reinterpret_cast<const struct sockaddr*>(&saddr),
|
||||||
|
sizeof(saddr));
|
||||||
|
if (err != 0) {
|
||||||
|
LOG(ERROR) << "connect() failed";
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Connected to the server";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
// Set-up the sandbox2::Client object, using a file descriptor (1023).
|
||||||
|
sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
|
||||||
|
sandbox2::Client sandbox2_client(&comms);
|
||||||
|
|
||||||
|
// Enable sandboxing from here.
|
||||||
|
sandbox2_client.SandboxMeHere();
|
||||||
|
|
||||||
|
sapi::Status status = sandbox2_client.InstallNetworkProxyHandler();
|
||||||
|
if (!status.ok()) {
|
||||||
|
LOG(ERROR) << "InstallNetworkProxyHandler() failed: " << status.message();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive port number of the server
|
||||||
|
int port;
|
||||||
|
if (!comms.RecvInt32(&port)) {
|
||||||
|
LOG(ERROR) << "sandboxee_comms->RecvUint32(&crc4) failed";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sandbox2::file_util::fileops::FDCloser client{ConnectToServer(port)};
|
||||||
|
if (client.get() == -1) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CommunicationTest(client.get())) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
// A demo sandbox for the network binary.
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <syscall.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
#include "sandboxed_api/util/flag.h"
|
||||||
|
#include "sandboxed_api/sandbox2/comms.h"
|
||||||
|
#include "sandboxed_api/sandbox2/executor.h"
|
||||||
|
#include "sandboxed_api/sandbox2/ipc.h"
|
||||||
|
#include "sandboxed_api/sandbox2/policy.h"
|
||||||
|
#include "sandboxed_api/sandbox2/policybuilder.h"
|
||||||
|
#include "sandboxed_api/sandbox2/sandbox2.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/fileops.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/runfiles.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::unique_ptr<sandbox2::Policy> GetPolicy(absl::string_view sandboxee_path) {
|
||||||
|
return sandbox2::PolicyBuilder()
|
||||||
|
.AllowExit()
|
||||||
|
.EnableNamespaces()
|
||||||
|
.AllowMmap()
|
||||||
|
.AllowRead()
|
||||||
|
.AllowWrite()
|
||||||
|
.AllowStat() // printf, puts
|
||||||
|
.AllowOpen()
|
||||||
|
.AllowSyscall(__NR_sendto) // send
|
||||||
|
.AllowSyscall(__NR_lseek)
|
||||||
|
.AllowSyscall(__NR_munmap)
|
||||||
|
.AllowSyscall(__NR_getpid)
|
||||||
|
.AddNetworkProxyHandlerPolicy()
|
||||||
|
.AddLibrariesForBinary(sandboxee_path)
|
||||||
|
.BuildOrDie();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server(int port) {
|
||||||
|
sandbox2::file_util::fileops::FDCloser s{
|
||||||
|
socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0)};
|
||||||
|
if (s.get() < 0) {
|
||||||
|
PLOG(ERROR) << "socket() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int enable = 1;
|
||||||
|
if (setsockopt(s.get(), SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) <
|
||||||
|
0) {
|
||||||
|
PLOG(ERROR) << "setsockopt(SO_REUSEADDR) failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to localhost only.
|
||||||
|
struct sockaddr_in6 addr = {};
|
||||||
|
addr.sin6_family = AF_INET6;
|
||||||
|
addr.sin6_port = htons(port);
|
||||||
|
|
||||||
|
int err = inet_pton(AF_INET6, "::1", &addr.sin6_addr.s6_addr);
|
||||||
|
if (err == 0) {
|
||||||
|
LOG(ERROR) << "inet_pton() failed";
|
||||||
|
return;
|
||||||
|
} else if (err == -1) {
|
||||||
|
PLOG(ERROR) << "inet_pton() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(s.get(), (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||||
|
PLOG(ERROR) << "bind() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(s.get(), 1) < 0) {
|
||||||
|
PLOG(ERROR) << "listen() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sandbox2::file_util::fileops::FDCloser client{accept(s.get(), 0, 0)};
|
||||||
|
if (client.get() < 0) {
|
||||||
|
PLOG(ERROR) << "accept() failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char kMsg[] = "Hello World\n";
|
||||||
|
write(client.get(), kMsg, ABSL_ARRAYSIZE(kMsg));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
// This test is incompatible with sanitizers.
|
||||||
|
// The `SKIP_SANITIZERS_AND_COVERAGE` macro won't work for us here since we
|
||||||
|
// need to return something.
|
||||||
|
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
|
||||||
|
defined(THREAD_SANITIZER)
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
int port = 8085;
|
||||||
|
std::thread server_thread{Server,port};
|
||||||
|
server_thread.detach();
|
||||||
|
|
||||||
|
std::string path = sandbox2::GetInternalDataDependencyFilePath(
|
||||||
|
"sandbox2/examples/network_proxy/networkproxy_bin");
|
||||||
|
std::vector<std::string> args = {path};
|
||||||
|
std::vector<std::string> envs = {};
|
||||||
|
|
||||||
|
auto executor = absl::make_unique<sandbox2::Executor>(path, args, envs);
|
||||||
|
|
||||||
|
executor
|
||||||
|
// Sandboxing is enabled by the binary itself (i.e. the networkproxy_bin
|
||||||
|
// is capable of enabling sandboxing on its own).
|
||||||
|
->set_enable_sandbox_before_exec(false)
|
||||||
|
// Set cwd to / to get rids of warnings connected with file namespace.
|
||||||
|
.set_cwd("/")
|
||||||
|
// Enable built-in network proxy.
|
||||||
|
.ipc()
|
||||||
|
->EnableNetworkProxyServer();
|
||||||
|
executor
|
||||||
|
->limits()
|
||||||
|
// Remove restrictions on the size of address-space of sandboxed
|
||||||
|
// processes.
|
||||||
|
->set_rlimit_as(RLIM64_INFINITY)
|
||||||
|
// Kill sandboxed processes with a signal (SIGXFSZ) if it writes more than
|
||||||
|
// these many bytes to the file-system.
|
||||||
|
.set_rlimit_fsize(10000)
|
||||||
|
// The CPU time limit in seconds.
|
||||||
|
.set_rlimit_cpu(100)
|
||||||
|
.set_walltime_limit(absl::Seconds(100));
|
||||||
|
|
||||||
|
auto policy = GetPolicy(path);
|
||||||
|
sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
|
||||||
|
auto* comms = s2.comms();
|
||||||
|
|
||||||
|
// Let the sandboxee run.
|
||||||
|
if (!s2.RunAsync()) {
|
||||||
|
auto result = s2.AwaitResult();
|
||||||
|
LOG(ERROR) << "RunAsync failed: " << result.ToString();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the port number via comms
|
||||||
|
if (!comms->SendInt32(port)) {
|
||||||
|
LOG(ERROR) << "sandboxee_comms->SendInt32() failed";
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = s2.AwaitResult();
|
||||||
|
if (result.final_status() != sandbox2::Result::OK) {
|
||||||
|
LOG(ERROR) << "Sandbox error: " << result.ToString();
|
||||||
|
return 4; // e.g. sandbox violation, signal (sigsegv)
|
||||||
|
}
|
||||||
|
auto code = result.reason_code();
|
||||||
|
if (code) {
|
||||||
|
LOG(ERROR) << "Sandboxee exited with non-zero: " << code;
|
||||||
|
return 5; // e.g. normal child error
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Sandboxee finished: " << result.ToString();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -14,10 +14,17 @@
|
||||||
|
|
||||||
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||||
|
|
||||||
|
#include <linux/net.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <syscall.h>
|
||||||
|
#include <ucontext.h>
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <glog/logging.h>
|
#include <glog/logging.h>
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "sandboxed_api/sandbox2/util/strerror.h"
|
#include "sandboxed_api/sandbox2/util/strerror.h"
|
||||||
#include "sandboxed_api/util/canonical_errors.h"
|
#include "sandboxed_api/util/canonical_errors.h"
|
||||||
|
@ -26,6 +33,25 @@
|
||||||
|
|
||||||
namespace sandbox2 {
|
namespace sandbox2 {
|
||||||
|
|
||||||
|
#ifndef SYS_SECCOMP
|
||||||
|
constexpr int SYS_SECCOMP = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
constexpr int kRegResult = REG_RAX;
|
||||||
|
constexpr int kRegSyscall = REG_RAX;
|
||||||
|
constexpr int kRegArg0 = REG_RDI;
|
||||||
|
constexpr int kRegArg1 = REG_RSI;
|
||||||
|
constexpr int kRegArg2 = REG_RDX;
|
||||||
|
#endif
|
||||||
|
#if defined(__powerpc64__)
|
||||||
|
constexpr int kRegResult = 3;
|
||||||
|
constexpr int kRegSyscall = 0;
|
||||||
|
constexpr int kRegArg0 = 3;
|
||||||
|
constexpr int kRegArg1 = 4;
|
||||||
|
constexpr int kRegArg2 = 5;
|
||||||
|
#endif
|
||||||
|
|
||||||
constexpr char NetworkProxyClient::kFDName[];
|
constexpr char NetworkProxyClient::kFDName[];
|
||||||
|
|
||||||
int NetworkProxyClient::ConnectHandler(int sockfd, const struct sockaddr* addr,
|
int NetworkProxyClient::ConnectHandler(int sockfd, const struct sockaddr* addr,
|
||||||
|
@ -34,7 +60,7 @@ int NetworkProxyClient::ConnectHandler(int sockfd, const struct sockaddr* addr,
|
||||||
if (status.ok()) {
|
if (status.ok()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
PLOG(ERROR) << "ConnectHandler() failed: " << status.message();
|
LOG(ERROR) << "ConnectHandler() failed: " << status.message();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,9 +112,106 @@ sapi::Status NetworkProxyClient::ReceiveRemoteResult() {
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
errno = result;
|
errno = result;
|
||||||
return sapi::InternalError(
|
return sapi::InternalError(
|
||||||
absl::StrCat("Error in network proxy: ", StrError(errno)));
|
absl::StrCat("Error in network proxy server: ", StrError(errno)));
|
||||||
}
|
}
|
||||||
return sapi::OkStatus();
|
return sapi::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static NetworkProxyHandler* g_network_proxy_handler = nullptr;
|
||||||
|
|
||||||
|
void SignalHandler(int nr, siginfo_t* info, void* void_context) {
|
||||||
|
g_network_proxy_handler->ProcessSeccompTrap(nr, info, void_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
sapi::Status NetworkProxyHandler::InstallNetworkProxyHandler(
|
||||||
|
NetworkProxyClient* npc) {
|
||||||
|
if (g_network_proxy_handler) {
|
||||||
|
return sapi::AlreadyExistsError(
|
||||||
|
"Network proxy handler is already installed");
|
||||||
|
}
|
||||||
|
g_network_proxy_handler = new NetworkProxyHandler(npc);
|
||||||
|
return sapi::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkProxyHandler::InvokeOldAct(int nr, siginfo_t* info,
|
||||||
|
void* void_context) {
|
||||||
|
if (oldact_.sa_flags & SA_SIGINFO) {
|
||||||
|
if (oldact_.sa_sigaction) {
|
||||||
|
oldact_.sa_sigaction(nr, info, void_context);
|
||||||
|
}
|
||||||
|
} else if (oldact_.sa_handler == SIG_IGN) {
|
||||||
|
return;
|
||||||
|
} else if (oldact_.sa_handler == SIG_DFL) {
|
||||||
|
sigaction(SIGSYS, &oldact_, nullptr);
|
||||||
|
raise(SIGSYS);
|
||||||
|
} else if (oldact_.sa_handler) {
|
||||||
|
oldact_.sa_handler(nr);
|
||||||
|
}
|
||||||
|
} // namespace sandbox2
|
||||||
|
|
||||||
|
void NetworkProxyHandler::ProcessSeccompTrap(int nr, siginfo_t* info,
|
||||||
|
void* void_context) {
|
||||||
|
ucontext_t* ctx = (ucontext_t*)(void_context);
|
||||||
|
if (info->si_code != SYS_SECCOMP) {
|
||||||
|
InvokeOldAct(nr, info, void_context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
auto* registers = ctx->uc_mcontext.gregs;
|
||||||
|
#elif defined(__powerpc64__)
|
||||||
|
auto* registers = ctx->uc_mcontext.gp_regs;
|
||||||
|
using ppc_gpreg_t = std::decay<decltype(registers[0])>::type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int syscall = registers[kRegSyscall];
|
||||||
|
|
||||||
|
int sockfd;
|
||||||
|
const struct sockaddr* addr;
|
||||||
|
socklen_t addrlen;
|
||||||
|
|
||||||
|
if (syscall == __NR_connect) {
|
||||||
|
sockfd = static_cast<int>(registers[kRegArg0]);
|
||||||
|
addr = reinterpret_cast<const struct sockaddr*>(registers[kRegArg1]);
|
||||||
|
addrlen = static_cast<socklen_t>(registers[kRegArg2]);
|
||||||
|
#if defined(__powerpc64__)
|
||||||
|
} else if (syscall == __NR_socketcall &&
|
||||||
|
static_cast<int>(registers[kRegArg0]) == SYS_CONNECT) {
|
||||||
|
ppc_gpreg_t* args = reinterpret_cast<ppc_gpreg_t*>(registers[kRegArg1]);
|
||||||
|
|
||||||
|
sockfd = static_cast<int>(args[0]);
|
||||||
|
addr = reinterpret_cast<const struct sockaddr*>(args[1]);
|
||||||
|
addrlen = static_cast<socklen_t>(args[2]);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
InvokeOldAct(nr, info, void_context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sapi::Status result = network_proxy_client_->Connect(sockfd, addr, addrlen);
|
||||||
|
if (result.ok()) {
|
||||||
|
registers[kRegResult] = 0;
|
||||||
|
} else {
|
||||||
|
registers[kRegResult] = -errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkProxyHandler::InstallSeccompTrap() {
|
||||||
|
sigset_t mask;
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, SIGSYS);
|
||||||
|
|
||||||
|
struct sigaction act = {};
|
||||||
|
act.sa_sigaction = &SignalHandler;
|
||||||
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
|
||||||
|
CHECK_EQ(sigaction(SIGSYS, &act, &oldact_), 0);
|
||||||
|
CHECK_EQ(sigprocmask(SIG_UNBLOCK, &mask, nullptr), 0);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
|
@ -50,6 +50,26 @@ class NetworkProxyClient {
|
||||||
absl::Mutex mutex_;
|
absl::Mutex mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NetworkProxyHandler {
|
||||||
|
public:
|
||||||
|
// Installs the handler that redirects connect() syscalls to the trap
|
||||||
|
// function. This function exchange data with NetworkProxyServer that checks
|
||||||
|
// if this connection is allowed and sends the connected socket to us.
|
||||||
|
// In other words, this function just use NetworkProxyClient class.
|
||||||
|
static sapi::Status InstallNetworkProxyHandler(NetworkProxyClient* npc);
|
||||||
|
void ProcessSeccompTrap(int nr, siginfo_t* info, void* void_context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkProxyHandler(NetworkProxyClient* npc) : network_proxy_client_(npc) {
|
||||||
|
InstallSeccompTrap();
|
||||||
|
}
|
||||||
|
void InvokeOldAct(int nr, siginfo_t* info, void* void_context);
|
||||||
|
void InstallSeccompTrap();
|
||||||
|
|
||||||
|
struct sigaction oldact_;
|
||||||
|
NetworkProxyClient* network_proxy_client_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
||||||
#endif // SANDBOXED_API_SANDBOX2_NETWORK_PROXY_CLIENT_H_
|
#endif // SANDBOXED_API_SANDBOX2_NETWORK_PROXY_CLIENT_H_
|
||||||
|
|
|
@ -39,35 +39,38 @@ void NetworkProxyServer::ProcessConnectRequest() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only sockaddr_in is supported.
|
|
||||||
if (addr.size() != sizeof(sockaddr_in)) {
|
|
||||||
SendResult(-1, EINVAL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const struct sockaddr_in* saddr =
|
const struct sockaddr_in* saddr =
|
||||||
reinterpret_cast<const sockaddr_in*>(addr.data());
|
reinterpret_cast<const sockaddr_in*>(addr.data());
|
||||||
|
|
||||||
// Only IPv4 TCP and IPv6 TCP are supported.
|
// Only IPv4 TCP and IPv6 TCP are supported.
|
||||||
if (saddr->sin_family != AF_INET && saddr->sin_family != AF_INET6) {
|
if (!((addr.size() == sizeof(sockaddr_in) && saddr->sin_family == AF_INET) ||
|
||||||
SendResult(-1, EINVAL);
|
(addr.size() == sizeof(sockaddr_in6) &&
|
||||||
|
saddr->sin_family == AF_INET6))) {
|
||||||
|
SendError(EINVAL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int new_socket = socket(saddr->sin_family, SOCK_STREAM, 0);
|
int new_socket = socket(saddr->sin_family, SOCK_STREAM, 0);
|
||||||
|
if (new_socket < 0) {
|
||||||
|
SendError(errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
file_util::fileops::FDCloser new_socket_closer(new_socket);
|
file_util::fileops::FDCloser new_socket_closer(new_socket);
|
||||||
|
|
||||||
int result = connect(
|
int result = connect(
|
||||||
new_socket, reinterpret_cast<const sockaddr*>(addr.data()), addr.size());
|
new_socket, reinterpret_cast<const sockaddr*>(addr.data()), addr.size());
|
||||||
|
|
||||||
SendResult(result, errno);
|
if (result == 0) {
|
||||||
|
NotifySuccess();
|
||||||
if (result == 0 && !fatal_error_) {
|
if (!fatal_error_) {
|
||||||
if (!comms_->SendFD(new_socket)) {
|
if (!comms_->SendFD(new_socket)) {
|
||||||
fatal_error_ = true;
|
fatal_error_ = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkProxyServer::Run() {
|
void NetworkProxyServer::Run() {
|
||||||
while (!fatal_error_) {
|
while (!fatal_error_) {
|
||||||
|
@ -77,8 +80,14 @@ void NetworkProxyServer::Run() {
|
||||||
<< "Clean shutdown or error occurred, shutting down NetworkProxyServer";
|
<< "Clean shutdown or error occurred, shutting down NetworkProxyServer";
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkProxyServer::SendResult(int result, int saved_errno) {
|
void NetworkProxyServer::SendError(int saved_errno) {
|
||||||
if (!comms_->SendInt32(result == 0 ? result : saved_errno)) {
|
if (!comms_->SendInt32(saved_errno)) {
|
||||||
|
fatal_error_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkProxyServer::NotifySuccess() {
|
||||||
|
if (!comms_->SendInt32(0)) {
|
||||||
fatal_error_ = true;
|
fatal_error_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,13 @@ class NetworkProxyServer {
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Sends the result of internal functions to the sandboxee. It sends errno in
|
// Notifies the network proxy client about the error and sends its code.
|
||||||
// case of error and 0 if no error occurred. On error, it sets fatal_error_ to
|
void SendError(int saved_errno);
|
||||||
// true, which terminates the processing loop in ProcessConnectRequest().
|
|
||||||
void SendResult(int result, int saved_errno);
|
|
||||||
|
|
||||||
|
// Notifies the network proxy client that no error occurred.
|
||||||
|
void NotifySuccess();
|
||||||
|
|
||||||
|
// Serves connection requests from the network proxy client.
|
||||||
void ProcessConnectRequest();
|
void ProcessConnectRequest();
|
||||||
|
|
||||||
std::unique_ptr<Comms> comms_;
|
std::unique_ptr<Comms> comms_;
|
||||||
|
|
|
@ -23,14 +23,17 @@
|
||||||
#endif
|
#endif
|
||||||
#include <fcntl.h> // For the fcntl flags
|
#include <fcntl.h> // For the fcntl flags
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
|
#include <linux/net.h> // For SYS_CONNECT
|
||||||
#include <linux/random.h> // For GRND_NONBLOCK
|
#include <linux/random.h> // For GRND_NONBLOCK
|
||||||
#include <sys/mman.h> // For mmap arguments
|
#include <sys/mman.h> // For mmap arguments
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "absl/memory/memory.h"
|
||||||
#include "absl/strings/escaping.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "absl/strings/match.h"
|
#include "absl/strings/match.h"
|
||||||
#include "sandboxed_api/sandbox2/namespace.h"
|
#include "sandboxed_api/sandbox2/namespace.h"
|
||||||
|
@ -557,21 +560,23 @@ PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(unsigned int num, BpfFunc f) {
|
||||||
|
|
||||||
PolicyBuilder& PolicyBuilder::AddPolicyOnSyscalls(
|
PolicyBuilder& PolicyBuilder::AddPolicyOnSyscalls(
|
||||||
SyscallInitializer nums, const std::vector<sock_filter>& policy) {
|
SyscallInitializer nums, const std::vector<sock_filter>& policy) {
|
||||||
auto resolved_policy = ResolveBpfFunc([nums, policy](bpf_labels& labels)
|
auto resolved_policy =
|
||||||
-> std::vector<sock_filter> {
|
ResolveBpfFunc(
|
||||||
|
[nums, policy](bpf_labels& labels) -> std::vector<sock_filter> {
|
||||||
std::vector<sock_filter> out;
|
std::vector<sock_filter> out;
|
||||||
out.reserve(nums.size() + policy.size());
|
out.reserve(nums.size() + policy.size());
|
||||||
for (auto num : nums) {
|
for (auto num : nums) {
|
||||||
out.insert(out.end(), {SYSCALL(num, JUMP(&labels, do_policy_l))});
|
out.insert(out.end(), {SYSCALL(num, JUMP(&labels, do_policy_l))});
|
||||||
}
|
}
|
||||||
out.insert(out.end(),
|
out.insert(out.end(), {JUMP(&labels, dont_do_policy_l),
|
||||||
{JUMP(&labels, dont_do_policy_l), LABEL(&labels, do_policy_l)});
|
LABEL(&labels, do_policy_l)});
|
||||||
for (const auto& filter : policy) {
|
for (const auto& filter : policy) {
|
||||||
// Syscall arch is expected as TRACE value
|
// Syscall arch is expected as TRACE value
|
||||||
if (filter.code == (BPF_RET | BPF_K) &&
|
if (filter.code == (BPF_RET | BPF_K) &&
|
||||||
(filter.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE &&
|
(filter.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE &&
|
||||||
(filter.k & SECCOMP_RET_DATA) != Syscall::GetHostArch()) {
|
(filter.k & SECCOMP_RET_DATA) != Syscall::GetHostArch()) {
|
||||||
LOG(WARNING) << "SANDBOX2_TRACE should be used in policy instead of "
|
LOG(WARNING)
|
||||||
|
<< "SANDBOX2_TRACE should be used in policy instead of "
|
||||||
"TRACE(value)";
|
"TRACE(value)";
|
||||||
out.push_back(SANDBOX2_TRACE);
|
out.push_back(SANDBOX2_TRACE);
|
||||||
} else {
|
} else {
|
||||||
|
@ -840,6 +845,66 @@ PolicyBuilder& PolicyBuilder::CollectStacktracesOnKill(bool enable) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PolicyBuilder& PolicyBuilder::AddNetworkProxyPolicy() {
|
||||||
|
AllowFutexOp(FUTEX_WAKE);
|
||||||
|
AllowFutexOp(FUTEX_WAIT);
|
||||||
|
AllowFutexOp(FUTEX_WAIT_BITSET);
|
||||||
|
AllowSyscalls({
|
||||||
|
__NR_dup2,
|
||||||
|
__NR_recvmsg,
|
||||||
|
__NR_close,
|
||||||
|
__NR_gettid,
|
||||||
|
});
|
||||||
|
AddPolicyOnSyscall(__NR_socket, {
|
||||||
|
ARG_32(0),
|
||||||
|
JEQ32(AF_INET, ALLOW),
|
||||||
|
JEQ32(AF_INET6, ALLOW),
|
||||||
|
});
|
||||||
|
AddPolicyOnSyscall(__NR_getsockopt,
|
||||||
|
[](bpf_labels& labels) -> std::vector<sock_filter> {
|
||||||
|
return {
|
||||||
|
ARG_32(1),
|
||||||
|
JNE32(SOL_SOCKET, JUMP(&labels, getsockopt_end)),
|
||||||
|
ARG_32(2),
|
||||||
|
JEQ32(SO_TYPE, ALLOW),
|
||||||
|
LABEL(&labels, getsockopt_end),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
#if defined(__powerpc64__)
|
||||||
|
AddPolicyOnSyscall(__NR_socketcall, {
|
||||||
|
ARG_32(0),
|
||||||
|
JEQ32(SYS_SOCKET, ALLOW),
|
||||||
|
JEQ32(SYS_GETSOCKOPT, ALLOW),
|
||||||
|
JEQ32(SYS_RECVMSG, ALLOW),
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PolicyBuilder& PolicyBuilder::AddNetworkProxyHandlerPolicy() {
|
||||||
|
AddNetworkProxyPolicy();
|
||||||
|
AllowSyscall(__NR_rt_sigreturn);
|
||||||
|
|
||||||
|
AddPolicyOnSyscall(__NR_rt_sigaction, {
|
||||||
|
ARG_32(0),
|
||||||
|
JEQ32(SIGSYS, ALLOW),
|
||||||
|
});
|
||||||
|
|
||||||
|
AddPolicyOnSyscall(__NR_rt_sigprocmask, {
|
||||||
|
ARG_32(0),
|
||||||
|
JEQ32(SIG_UNBLOCK, ALLOW),
|
||||||
|
});
|
||||||
|
|
||||||
|
AddPolicyOnSyscall(__NR_connect, {TRAP(0)});
|
||||||
|
#if defined(__powerpc64__)
|
||||||
|
AddPolicyOnSyscall(__NR_socketcall, {
|
||||||
|
ARG_32(0),
|
||||||
|
JEQ32(SYS_CONNECT, TRAP(0)),
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void PolicyBuilder::StoreDescription(PolicyBuilderDescription* pb_description) {
|
void PolicyBuilder::StoreDescription(PolicyBuilderDescription* pb_description) {
|
||||||
for (const auto& handled_syscall : handled_syscalls_) {
|
for (const auto& handled_syscall : handled_syscalls_) {
|
||||||
pb_description->add_handled_syscalls(handled_syscall);
|
pb_description->add_handled_syscalls(handled_syscall);
|
||||||
|
|
|
@ -497,6 +497,13 @@ class PolicyBuilder final {
|
||||||
// sandbox-team@ first if unsure.
|
// sandbox-team@ first if unsure.
|
||||||
PolicyBuilder& DangerDefaultAllowAll();
|
PolicyBuilder& DangerDefaultAllowAll();
|
||||||
|
|
||||||
|
// Allows syscalls that are necessary for the NetworkProxyClient
|
||||||
|
PolicyBuilder& AddNetworkProxyPolicy();
|
||||||
|
|
||||||
|
// Allows syscalls that are necessary for the NetworkProxyClient and
|
||||||
|
// the NetworkProxyHandler
|
||||||
|
PolicyBuilder& AddNetworkProxyHandlerPolicy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class PolicyBuilderPeer; // For testing
|
friend class PolicyBuilderPeer; // For testing
|
||||||
friend class StackTracePeer;
|
friend class StackTracePeer;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user