Now network proxy server supports IP filtering. API to policybuilder is added to make a list of allowed pairs of allowed IP, mask and port where mask and port are optional.

PiperOrigin-RevId: 294640297
Change-Id: I4c6520685a658f8b7762af238588830f71b3f54a
This commit is contained in:
bielec 2020-02-12 13:43:05 +01:00 committed by Christian Blichmann
parent f1ce6fcb87
commit 800339d672
20 changed files with 146 additions and 547 deletions

View File

@ -89,7 +89,7 @@ cc_library(
":syscall", ":syscall",
":util", ":util",
"//sandboxed_api/util:status", "//sandboxed_api/util:status",
"@com_google_absl//absl/base", "//sandboxed_api/util:statusor",
"@com_google_absl//absl/memory", "@com_google_absl//absl/memory",
"@com_google_absl//absl/strings", "@com_google_absl//absl/strings",
], ],
@ -127,36 +127,6 @@ cc_library(
], ],
) )
cc_library(
name = "network_proxy_server",
srcs = ["network_proxy_server.cc"],
hdrs = ["network_proxy_server.h"],
copts = sapi_platform_copts(),
deps = [
":comms",
"//sandboxed_api/sandbox2/util:fileops",
"@com_google_absl//absl/memory",
"@com_google_glog//:glog",
],
)
cc_library(
name = "network_proxy_client",
srcs = ["network_proxy_client.cc"],
hdrs = ["network_proxy_client.h"],
copts = sapi_platform_copts(),
visibility = ["//visibility:public"],
deps = [
":comms",
"//sandboxed_api/sandbox2/util:strerror",
"//sandboxed_api/util:status",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_glog//:glog",
],
)
cc_library( cc_library(
name = "ipc", name = "ipc",
srcs = ["ipc.cc"], srcs = ["ipc.cc"],
@ -166,8 +136,6 @@ cc_library(
":comms", ":comms",
":logserver", ":logserver",
":logsink", ":logsink",
":network_proxy_client",
":network_proxy_server",
"@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",
@ -186,6 +154,7 @@ cc_library(
":regs", ":regs",
":syscall", ":syscall",
":violation_cc_proto", ":violation_cc_proto",
"//sandboxed_api/sandbox2/network_proxy:filtering",
"//sandboxed_api/sandbox2/util:bpf_helper", "//sandboxed_api/sandbox2/util:bpf_helper",
"//sandboxed_api/util:flags", "//sandboxed_api/util:flags",
"@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:core_headers",
@ -306,10 +275,10 @@ cc_library(
":client", ":client",
":executor", ":executor",
":comms", ":comms",
":violation_cc_proto",
":forkserver",
":forkserver_cc_proto", ":forkserver_cc_proto",
":global_forkserver", ":global_forkserver",
":violation_cc_proto",
":forkserver",
":ipc", ":ipc",
":limits", ":limits",
":logsink", ":logsink",
@ -321,7 +290,6 @@ cc_library(
":result", ":result",
":syscall", ":syscall",
":util", ":util",
":network_proxy_client",
"@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/container:flat_hash_set",
@ -333,12 +301,17 @@ cc_library(
"@com_google_absl//absl/time", "@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:optional",
"@org_kernel_libcap//:libcap", "@org_kernel_libcap//:libcap",
"//sandboxed_api/sandbox2/network_proxy:client",
"//sandboxed_api/sandbox2/network_proxy:filtering",
"//sandboxed_api/sandbox2/network_proxy:server",
"//sandboxed_api/sandbox2/unwind", "//sandboxed_api/sandbox2/unwind",
"//sandboxed_api/sandbox2/unwind:unwind_cc_proto", "//sandboxed_api/sandbox2/unwind:unwind_cc_proto",
"//sandboxed_api/sandbox2/util:bpf_helper", "//sandboxed_api/sandbox2/util:bpf_helper",
"//sandboxed_api/sandbox2/util:file_base", "//sandboxed_api/sandbox2/util:file_base",
"//sandboxed_api/sandbox2/util:fileops", "//sandboxed_api/sandbox2/util:fileops",
"//sandboxed_api/sandbox2/util:strerror",
"//sandboxed_api/util:raw_logging", "//sandboxed_api/util:raw_logging",
"//util/functional",
"//sandboxed_api/util:status", "//sandboxed_api/util:status",
"//sandboxed_api/util:statusor", "//sandboxed_api/util:statusor",
@ -364,7 +337,7 @@ cc_library(
deps = [ deps = [
":comms", ":comms",
":logsink", ":logsink",
":network_proxy_client", "//sandboxed_api/sandbox2/network_proxy:client",
"//sandboxed_api/sandbox2/util:file_helpers", "//sandboxed_api/sandbox2/util:file_helpers",
"//sandboxed_api/sandbox2/util:fileops", "//sandboxed_api/sandbox2/util:fileops",
"//sandboxed_api/sandbox2/util:strerror", "//sandboxed_api/sandbox2/util:strerror",

View File

@ -15,6 +15,7 @@
add_subdirectory(examples) add_subdirectory(examples)
add_subdirectory(unwind) add_subdirectory(unwind)
add_subdirectory(util) add_subdirectory(util)
add_subdirectory(network_proxy)
# sandboxed_api/sandbox2:bpfdisassembler # sandboxed_api/sandbox2:bpfdisassembler
add_library(sandbox2_bpfdisassembler STATIC add_library(sandbox2_bpfdisassembler STATIC
@ -121,36 +122,6 @@ target_link_libraries(sandbox2_logsink
PUBLIC glog::glog PUBLIC glog::glog
) )
# sandboxed_api/sandbox2:network_proxy_server
add_library(sandbox2_network_proxy_server STATIC
network_proxy_server.cc
network_proxy_server.h
)
add_library(sandbox2::network_proxy_server ALIAS sandbox2_network_proxy_server)
target_link_libraries(sandbox2_network_proxy_server PRIVATE
absl::memory
glog::glog
sandbox2::comms
sandbox2::fileops
sapi::base
)
# sandboxed_api/sandbox2:network_proxy_client
add_library(sandbox2_network_proxy_client STATIC
network_proxy_client.cc
network_proxy_client.h
)
add_library(sandbox2::network_proxy_client ALIAS sandbox2_network_proxy_client)
target_link_libraries(sandbox2_network_proxy_client PRIVATE
absl::strings
absl::synchronization
glog::glog
sandbox2::comms
sandbox2::strerror
sapi::base
sapi::status
)
# sandboxed_api/sandbox2:ipc # sandboxed_api/sandbox2:ipc
add_library(sandbox2_ipc STATIC add_library(sandbox2_ipc STATIC
ipc.cc ipc.cc

View File

@ -40,7 +40,6 @@
#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"

View File

@ -25,7 +25,7 @@
#include "sandboxed_api/sandbox2/comms.h" #include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/sandbox2/logsink.h" #include "sandboxed_api/sandbox2/logsink.h"
#include "sandboxed_api/sandbox2/network_proxy_client.h" #include "sandboxed_api/sandbox2/network_proxy/client.h"
namespace sandbox2 { namespace sandbox2 {

View File

@ -14,10 +14,10 @@
# The 'network proxy' example demonstrates how to use network proxy server. # The 'network proxy' example demonstrates how to use network proxy server.
licenses(["notice"])
load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts") load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts")
licenses(["notice"])
# Executor # Executor
cc_binary( cc_binary(
name = "networkproxy_sandbox", name = "networkproxy_sandbox",
@ -27,6 +27,7 @@ cc_binary(
deps = [ deps = [
"//sandboxed_api/sandbox2", "//sandboxed_api/sandbox2",
"//sandboxed_api/sandbox2:comms", "//sandboxed_api/sandbox2:comms",
"//sandboxed_api/sandbox2/network_proxy:filtering",
"//sandboxed_api/sandbox2/util:bpf_helper", "//sandboxed_api/sandbox2/util:bpf_helper",
"//sandboxed_api/sandbox2/util:fileops", "//sandboxed_api/sandbox2/util:fileops",
"//sandboxed_api/sandbox2/util:runfiles", "//sandboxed_api/sandbox2/util:runfiles",
@ -42,7 +43,7 @@ cc_binary(
deps = [ deps = [
"//sandboxed_api/sandbox2:client", "//sandboxed_api/sandbox2:client",
"//sandboxed_api/sandbox2:comms", "//sandboxed_api/sandbox2:comms",
"//sandboxed_api/sandbox2:network_proxy_client", "//sandboxed_api/sandbox2/network_proxy:client",
"//sandboxed_api/sandbox2/util:fileops", "//sandboxed_api/sandbox2/util:fileops",
"@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/strings:str_format",
], ],

View File

@ -14,7 +14,7 @@
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
#include "sandboxed_api/sandbox2/client.h" #include "sandboxed_api/sandbox2/client.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/network_proxy/client.h"
#include "sandboxed_api/sandbox2/util/fileops.h" #include "sandboxed_api/sandbox2/util/fileops.h"
static ssize_t ReadFromFd(int fd, uint8_t* buf, size_t size) { static ssize_t ReadFromFd(int fd, uint8_t* buf, size_t size) {
@ -113,5 +113,6 @@ int main(int argc, char** argv) {
if (!CommunicationTest(client.get())) { if (!CommunicationTest(client.get())) {
return 4; return 4;
} }
return 0; return 0;
} }

View File

@ -42,6 +42,8 @@ std::unique_ptr<sandbox2::Policy> GetPolicy(absl::string_view sandboxee_path) {
.AllowSyscall(__NR_munmap) .AllowSyscall(__NR_munmap)
.AllowSyscall(__NR_getpid) .AllowSyscall(__NR_getpid)
.AddNetworkProxyHandlerPolicy() .AddNetworkProxyHandlerPolicy()
.AllowTcMalloc()
.AllowIPv6("::1")
.AddLibrariesForBinary(sandboxee_path) .AddLibrariesForBinary(sandboxee_path)
.BuildOrDie(); .BuildOrDie();
} }
@ -129,10 +131,7 @@ int main(int argc, char** argv) {
// is capable of enabling sandboxing on its own). // is capable of enabling sandboxing on its own).
->set_enable_sandbox_before_exec(false) ->set_enable_sandbox_before_exec(false)
// Set cwd to / to get rids of warnings connected with file namespace. // Set cwd to / to get rids of warnings connected with file namespace.
.set_cwd("/") .set_cwd("/");
// Enable built-in network proxy.
.ipc()
->EnableNetworkProxyServer();
executor executor
->limits() ->limits()
// Remove restrictions on the size of address-space of sandboxed // Remove restrictions on the size of address-space of sandboxed

View File

@ -17,16 +17,12 @@
#include "sandboxed_api/sandbox2/ipc.h" #include "sandboxed_api/sandbox2/ipc.h"
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <thread>
#include <thread> // NOLINT(build/c++11)
#include <glog/logging.h> #include <glog/logging.h>
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "sandboxed_api/sandbox2/logserver.h" #include "sandboxed_api/sandbox2/logserver.h"
#include "sandboxed_api/sandbox2/logsink.h" #include "sandboxed_api/sandbox2/logsink.h"
#include "sandboxed_api/sandbox2/network_proxy_client.h"
#include "sandboxed_api/sandbox2/network_proxy_server.h"
namespace sandbox2 { namespace sandbox2 {
@ -103,16 +99,4 @@ void IPC::EnableLogServer() {
log_thread.detach(); log_thread.detach();
} }
void IPC::EnableNetworkProxyServer() {
int fd = ReceiveFd(NetworkProxyClient::kFDName);
auto proxy_server = [fd]() {
NetworkProxyServer network_proxy_server(fd);
network_proxy_server.Run();
};
std::thread proxy_thread{proxy_server};
proxy_thread.detach();
}
} // namespace sandbox2 } // namespace sandbox2

View File

@ -23,7 +23,6 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include "absl/base/macros.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "sandboxed_api/sandbox2/comms.h" #include "sandboxed_api/sandbox2/comms.h"
@ -60,10 +59,6 @@ class IPC final {
// Client::SendLogsToSupervisor in the sandboxee. // Client::SendLogsToSupervisor in the sandboxee.
void EnableLogServer(); void EnableLogServer();
// Enable network proxy server, this will start a thread in the sandbox
// that waits for connection requests from the sandboxee.
void EnableNetworkProxyServer();
private: private:
friend class Executor; friend class Executor;
friend class Monitor; friend class Monitor;

View File

@ -53,6 +53,7 @@
#include "sandboxed_api/sandbox2/limits.h" #include "sandboxed_api/sandbox2/limits.h"
#include "sandboxed_api/sandbox2/mounts.h" #include "sandboxed_api/sandbox2/mounts.h"
#include "sandboxed_api/sandbox2/namespace.h" #include "sandboxed_api/sandbox2/namespace.h"
#include "sandboxed_api/sandbox2/network_proxy/server.h"
#include "sandboxed_api/sandbox2/policy.h" #include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/regs.h" #include "sandboxed_api/sandbox2/regs.h"
#include "sandboxed_api/sandbox2/result.h" #include "sandboxed_api/sandbox2/result.h"
@ -140,6 +141,9 @@ Monitor::~Monitor() {
if (log_file_) { if (log_file_) {
std::fclose(log_file_); std::fclose(log_file_);
} }
if (network_proxy_server_) {
network_proxy_thread_.join();
}
} }
namespace { namespace {
@ -198,6 +202,10 @@ void Monitor::Run() {
// sandbox master/monitor, which ptrace_attach'es to the child. // sandbox master/monitor, which ptrace_attach'es to the child.
int clone_flags = CLONE_UNTRACED; int clone_flags = CLONE_UNTRACED;
if (policy_->allowed_hosts_) {
EnableNetworkProxyServer();
}
// Get PID of the sandboxee. // Get PID of the sandboxee.
pid_t init_pid = 0; pid_t init_pid = 0;
Namespace* ns = policy_->GetNamespace(); Namespace* ns = policy_->GetNamespace();
@ -349,6 +357,14 @@ void Monitor::MainLoop(sigset_t* sset) {
KillSandboxee(); KillSandboxee();
} }
if (network_proxy_server_ &&
network_proxy_server_->violation_occurred_.load(
std::memory_order_acquire) &&
!network_violation_) {
network_violation_ = true;
KillSandboxee();
}
// It should be a non-blocking operation (hence WNOHANG), so this function // It should be a non-blocking operation (hence WNOHANG), so this function
// returns quickly if there are no events to be processed. // returns quickly if there are no events to be processed.
// Prioritize main pid to avoid resource starvation // Prioritize main pid to avoid resource starvation
@ -401,7 +417,10 @@ void Monitor::MainLoop(sigset_t* sset) {
VLOG(1) << "PID: " << ret << " terminated with signal: " VLOG(1) << "PID: " << ret << " terminated with signal: "
<< util::GetSignalName(WTERMSIG(status)); << util::GetSignalName(WTERMSIG(status));
if (ret == pid_) { if (ret == pid_) {
if (external_kill_) { if (network_violation_) {
SetExitStatusCode(Result::NETWORK_VIOLATION, 0);
result_.SetNetworkViolation(network_proxy_server_->violation_msg_);
} else if (external_kill_) {
SetExitStatusCode(Result::EXTERNAL_KILL, 0); SetExitStatusCode(Result::EXTERNAL_KILL, 0);
} else if (timed_out_) { } else if (timed_out_) {
SetExitStatusCode(Result::TIMEOUT, 0); SetExitStatusCode(Result::TIMEOUT, 0);
@ -794,7 +813,10 @@ void Monitor::EventPtraceExit(pid_t pid, int event_msg) {
// 3) Regular signal/other exit cause. // 3) Regular signal/other exit cause.
if (pid == pid_) { if (pid == pid_) {
VLOG(1) << "PID: " << pid << " main special exit"; VLOG(1) << "PID: " << pid << " main special exit";
if (external_kill_) { if (network_violation_) {
SetExitStatusCode(Result::NETWORK_VIOLATION, 0);
result_.SetNetworkViolation(network_proxy_server_->violation_msg_);
} else if (external_kill_) {
SetExitStatusCode(Result::EXTERNAL_KILL, 0); SetExitStatusCode(Result::EXTERNAL_KILL, 0);
} else if (timed_out_) { } else if (timed_out_) {
SetExitStatusCode(Result::TIMEOUT, 0); SetExitStatusCode(Result::TIMEOUT, 0);
@ -926,4 +948,14 @@ void Monitor::LogSyscallViolationExplanation(const Syscall& syscall) const {
} }
} }
void Monitor::EnableNetworkProxyServer() {
int fd = ipc_->ReceiveFd(NetworkProxyClient::kFDName);
network_proxy_server_ = absl::make_unique<NetworkProxyServer>(
fd, &policy_->allowed_hosts_.value(), pthread_self());
network_proxy_thread_ = std::thread(&NetworkProxyServer::Run,
network_proxy_server_.get());
}
} // namespace sandbox2 } // namespace sandbox2

View File

@ -26,11 +26,13 @@
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#include <memory> #include <memory>
#include <thread>
#include "absl/synchronization/notification.h" #include "absl/synchronization/notification.h"
#include "sandboxed_api/sandbox2/comms.h" #include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/sandbox2/executor.h" #include "sandboxed_api/sandbox2/executor.h"
#include "sandboxed_api/sandbox2/ipc.h" #include "sandboxed_api/sandbox2/ipc.h"
#include "sandboxed_api/sandbox2/network_proxy/server.h"
#include "sandboxed_api/sandbox2/notify.h" #include "sandboxed_api/sandbox2/notify.h"
#include "sandboxed_api/sandbox2/policy.h" #include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/regs.h" #include "sandboxed_api/sandbox2/regs.h"
@ -139,6 +141,10 @@ class Monitor final {
// Processes stop path. // Processes stop path.
void EventPtraceStop(pid_t pid, int stopsig); void EventPtraceStop(pid_t pid, int stopsig);
// Enable network proxy server, this will start a thread in the sandbox
// that waits for connection requests from the sandboxee.
void EnableNetworkProxyServer();
// Internal objects, owned by the Sandbox2 object. // Internal objects, owned by the Sandbox2 object.
Executor* executor_; Executor* executor_;
Notify* notify_; Notify* notify_;
@ -168,6 +174,8 @@ class Monitor final {
std::atomic<int64_t> deadline_millis_{0}; std::atomic<int64_t> deadline_millis_{0};
// Was external kill sent to the sandboxee // Was external kill sent to the sandboxee
bool external_kill_ = false; bool external_kill_ = false;
// Network violation occurred and process of killing sandboxee started
bool network_violation_ = false;
// Is the sandboxee timed out // Is the sandboxee timed out
bool timed_out_ = false; bool timed_out_ = false;
// Should we dump the main sandboxed PID's stack? // Should we dump the main sandboxed PID's stack?
@ -178,6 +186,12 @@ class Monitor final {
// Log file specified by // Log file specified by
// --sandbox_danger_danger_permit_all_and_log flag. // --sandbox_danger_danger_permit_all_and_log flag.
FILE* log_file_ = nullptr; FILE* log_file_ = nullptr;
// Handle to the class responsible for proxying and validating connect()
// requests.
std::unique_ptr<NetworkProxyServer> network_proxy_server_;
std::thread network_proxy_thread_;
}; };
} // namespace sandbox2 } // namespace sandbox2

View File

@ -1,217 +0,0 @@
// Copyright 2019 Google LLC
//
// 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.
#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 <iostream>
#include <glog/logging.h>
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "sandboxed_api/sandbox2/util/strerror.h"
#include "sandboxed_api/util/canonical_errors.h"
#include "sandboxed_api/util/status.h"
#include "sandboxed_api/util/status_macros.h"
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[];
int NetworkProxyClient::ConnectHandler(int sockfd, const struct sockaddr* addr,
socklen_t addrlen) {
sapi::Status status = Connect(sockfd, addr, addrlen);
if (status.ok()) {
return 0;
}
LOG(ERROR) << "ConnectHandler() failed: " << status.message();
return -1;
}
sapi::Status NetworkProxyClient::Connect(int sockfd,
const struct sockaddr* addr,
socklen_t addrlen) {
absl::MutexLock lock(&mutex_);
// Check if socket is SOCK_STREAM
int type;
socklen_t type_size = sizeof(int);
int result = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &type, &type_size);
if (result == -1) {
return sapi::FailedPreconditionError("Invalid socket FD");
}
if (type_size != sizeof(int) || type != SOCK_STREAM) {
errno = EINVAL;
return sapi::InvalidArgumentError(
"Invalid socket, only SOCK_STREAM is allowed");
}
// Send sockaddr struct
if (!comms_.SendBytes(reinterpret_cast<const uint8_t*>(addr), addrlen)) {
errno = EIO;
return sapi::InternalError("Sending data to network proxy failed");
}
SAPI_RETURN_IF_ERROR(ReceiveRemoteResult());
// Receive new socket
int s;
if (!comms_.RecvFD(&s)) {
errno = EIO;
return sapi::InternalError("Receiving data from network proxy failed");
}
if (dup2(s, sockfd) == -1) {
close(s);
return sapi::InternalError("Processing data from network proxy failed");
}
return sapi::OkStatus();
}
sapi::Status NetworkProxyClient::ReceiveRemoteResult() {
int result;
if (!comms_.RecvInt32(&result)) {
errno = EIO;
return sapi::InternalError("Receiving data from the network proxy failed");
}
if (result != 0) {
errno = result;
return sapi::InternalError(
absl::StrCat("Error in network proxy server: ", StrError(errno)));
}
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

View File

@ -1,75 +0,0 @@
// Copyright 2019 Google LLC
//
// 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.
#ifndef SANDBOXED_API_SANDBOX2_NETWORK_PROXY_CLIENT_H_
#define SANDBOXED_API_SANDBOX2_NETWORK_PROXY_CLIENT_H_
#include <netinet/in.h>
#include "absl/synchronization/mutex.h"
#include "sandboxed_api/sandbox2/comms.h"
#include "sandboxed_api/util/status.h"
namespace sandbox2 {
class NetworkProxyClient {
public:
static constexpr char kFDName[] = "sb2_networkproxy";
explicit NetworkProxyClient(int fd) : comms_(fd) {}
NetworkProxyClient(const NetworkProxyClient&) = delete;
NetworkProxyClient& operator=(const NetworkProxyClient&) = delete;
// Establishes a new network connection.
// Semantic is similar to a regular connect() call.
// Arguments are sent to network proxy server, which sends back a connected
// socket.
sapi::Status Connect(int sockfd, const struct sockaddr* addr,
socklen_t addrlen);
// Same as Connect, but with same API as regular connect() call.
int ConnectHandler(int sockfd, const struct sockaddr* addr,
socklen_t addrlen);
private:
Comms comms_;
sapi::Status ReceiveRemoteResult();
// Needed to make the Proxy thread safe.
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
#endif // SANDBOXED_API_SANDBOX2_NETWORK_PROXY_CLIENT_H_

View File

@ -1,95 +0,0 @@
// Copyright 2019 Google LLC
//
// 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.
#include "sandboxed_api/sandbox2/network_proxy_server.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syscall.h>
#include <cerrno>
#include <cstring>
#include <glog/logging.h>
#include "absl/memory/memory.h"
#include "sandboxed_api/sandbox2/util/fileops.h"
namespace sandbox2 {
NetworkProxyServer::NetworkProxyServer(int fd)
: comms_{absl::make_unique<Comms>(fd)}, fatal_error_{false} {}
void NetworkProxyServer::ProcessConnectRequest() {
std::vector<uint8_t> addr;
if (!comms_->RecvBytes(&addr)) {
fatal_error_ = true;
return;
}
const struct sockaddr_in* saddr =
reinterpret_cast<const sockaddr_in*>(addr.data());
// Only IPv4 TCP and IPv6 TCP are supported.
if (!((addr.size() == sizeof(sockaddr_in) && saddr->sin_family == AF_INET) ||
(addr.size() == sizeof(sockaddr_in6) &&
saddr->sin_family == AF_INET6))) {
SendError(EINVAL);
return;
}
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);
int result = connect(
new_socket, reinterpret_cast<const sockaddr*>(addr.data()), addr.size());
if (result == 0) {
NotifySuccess();
if (!fatal_error_) {
if (!comms_->SendFD(new_socket)) {
fatal_error_ = true;
return;
}
}
}
}
void NetworkProxyServer::Run() {
while (!fatal_error_) {
ProcessConnectRequest();
}
LOG(INFO)
<< "Clean shutdown or error occurred, shutting down NetworkProxyServer";
}
void NetworkProxyServer::SendError(int saved_errno) {
if (!comms_->SendInt32(saved_errno)) {
fatal_error_ = true;
}
}
void NetworkProxyServer::NotifySuccess() {
if (!comms_->SendInt32(0)) {
fatal_error_ = true;
}
}
} // namespace sandbox2

View File

@ -1,53 +0,0 @@
// Copyright 2019 Google LLC
//
// 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.
#ifndef SANDBOXED_API_SANDBOX2_NETWORK_PROXY_SERVER_H_
#define SANDBOXED_API_SANDBOX2_NETWORK_PROXY_SERVER_H_
#include <memory>
#include "sandboxed_api/sandbox2/comms.h"
namespace sandbox2 {
// This is a proxy server that spawns connected sockets on requests.
// Then it sends the file descriptor to the requestor. It is used to get around
// limitations created by network namespaces.
class NetworkProxyServer {
public:
explicit NetworkProxyServer(int fd);
NetworkProxyServer(const NetworkProxyServer&) = delete;
NetworkProxyServer& operator=(const NetworkProxyServer&) = delete;
// Starts handling incoming connection requests.
void Run();
private:
// Notifies the network proxy client about the error and sends its code.
void SendError(int saved_errno);
// Notifies the network proxy client that no error occurred.
void NotifySuccess();
// Serves connection requests from the network proxy client.
void ProcessConnectRequest();
std::unique_ptr<Comms> comms_;
bool fatal_error_;
};
} // namespace sandbox2
#endif // SANDBOXED_API_SANDBOX2_NETWORK_PROXY_SERVER_H_

View File

@ -31,6 +31,7 @@
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
#include "sandboxed_api/sandbox2/namespace.h" #include "sandboxed_api/sandbox2/namespace.h"
#include "sandboxed_api/sandbox2/network_proxy/filtering.h"
#include "sandboxed_api/sandbox2/syscall.h" #include "sandboxed_api/sandbox2/syscall.h"
#include "sandboxed_api/sandbox2/violation.pb.h" #include "sandboxed_api/sandbox2/violation.pb.h"
@ -104,6 +105,9 @@ class Policy final {
// Get a policy which would allow the Monitor module to track all syscalls. // Get a policy which would allow the Monitor module to track all syscalls.
std::vector<sock_filter> GetTrackingPolicy() const; std::vector<sock_filter> GetTrackingPolicy() const;
// Contains a list of hosts the sandboxee is allowed to connect to.
absl::optional<AllowedHosts> allowed_hosts_;
friend class Monitor; friend class Monitor;
friend class PolicyBuilder; friend class PolicyBuilder;
friend class PolicyBuilderPeer; // For testing friend class PolicyBuilderPeer; // For testing

View File

@ -39,6 +39,7 @@
#include "sandboxed_api/sandbox2/namespace.h" #include "sandboxed_api/sandbox2/namespace.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h" #include "sandboxed_api/sandbox2/util/bpf_helper.h"
#include "sandboxed_api/sandbox2/util/path.h" #include "sandboxed_api/sandbox2/util/path.h"
#include "sandboxed_api/sandbox2/util/strerror.h"
#include "sandboxed_api/util/canonical_errors.h" #include "sandboxed_api/util/canonical_errors.h"
#include "sandboxed_api/util/status_macros.h" #include "sandboxed_api/util/status_macros.h"
@ -47,7 +48,7 @@ namespace {
} // namespace } // namespace
PolicyBuilder& PolicyBuilder::AllowSyscall(unsigned int num) { PolicyBuilder& PolicyBuilder::AllowSyscall(uint32_t num) {
if (handled_syscalls_.insert(num).second) { if (handled_syscalls_.insert(num).second) {
user_policy_.insert(user_policy_.end(), {SYSCALL(num, ALLOW)}); user_policy_.insert(user_policy_.end(), {SYSCALL(num, ALLOW)});
} }
@ -68,8 +69,7 @@ PolicyBuilder& PolicyBuilder::AllowSyscalls(SyscallInitializer nums) {
return *this; return *this;
} }
PolicyBuilder& PolicyBuilder::BlockSyscallWithErrno(unsigned int num, PolicyBuilder& PolicyBuilder::BlockSyscallWithErrno(uint32_t num, int error) {
int error) {
if (handled_syscalls_.insert(num).second) { if (handled_syscalls_.insert(num).second) {
user_policy_.insert(user_policy_.end(), {SYSCALL(num, ERRNO(error))}); user_policy_.insert(user_policy_.end(), {SYSCALL(num, ERRNO(error))});
} }
@ -545,17 +545,17 @@ PolicyBuilder& PolicyBuilder::AllowDynamicStartup() {
}); });
} }
PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(unsigned int num, PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(uint32_t num,
BpfInitializer policy) { BpfInitializer policy) {
return AddPolicyOnSyscalls({num}, policy); return AddPolicyOnSyscalls({num}, policy);
} }
PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall( PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(
unsigned int num, const std::vector<sock_filter>& policy) { uint32_t num, const std::vector<sock_filter>& policy) {
return AddPolicyOnSyscalls({num}, policy); return AddPolicyOnSyscalls({num}, policy);
} }
PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(unsigned int num, BpfFunc f) { PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(uint32_t num, BpfFunc f) {
return AddPolicyOnSyscalls({num}, f); return AddPolicyOnSyscalls({num}, f);
} }
@ -700,6 +700,7 @@ sapi::StatusOr<std::unique_ptr<Policy>> PolicyBuilder::TryBuild() {
StoreDescription(pb_description.get()); StoreDescription(pb_description.get());
output->policy_builder_description_ = std::move(pb_description); output->policy_builder_description_ = std::move(pb_description);
output->allowed_hosts_ = std::move(allowed_hosts_);
already_built_ = true; already_built_ = true;
return std::move(output); return std::move(output);
} }
@ -848,6 +849,15 @@ PolicyBuilder& PolicyBuilder::CollectStacktracesOnKill(bool enable) {
} }
PolicyBuilder& PolicyBuilder::AddNetworkProxyPolicy() { PolicyBuilder& PolicyBuilder::AddNetworkProxyPolicy() {
if (allowed_hosts_) {
SetError(sapi::FailedPreconditionError(
"AddNetworkProxyPolicy or AddNetworkProxyHandlerPolicy can be called "
"at most once"));
return *this;
}
allowed_hosts_ = AllowedHosts();
AllowFutexOp(FUTEX_WAKE); AllowFutexOp(FUTEX_WAKE);
AllowFutexOp(FUTEX_WAIT); AllowFutexOp(FUTEX_WAIT);
AllowFutexOp(FUTEX_WAIT_BITSET); AllowFutexOp(FUTEX_WAIT_BITSET);
@ -920,4 +930,36 @@ void PolicyBuilder::StoreDescription(PolicyBuilderDescription* pb_description) {
} }
} }
PolicyBuilder& PolicyBuilder::AllowIPv4(const std::string& ip_and_mask,
uint32_t port) {
if (!allowed_hosts_) {
SetError(sapi::FailedPreconditionError(
"AddNetworkProxyPolicy or AddNetworkProxyHandlerPolicy must be called "
"before adding IP rules"));
return *this;
}
sapi::Status status = allowed_hosts_->AllowIPv4(ip_and_mask, port);
if (!status.ok()) {
SetError(status);
}
return *this;
}
PolicyBuilder& PolicyBuilder::AllowIPv6(const std::string& ip_and_mask,
uint32_t port) {
if (!allowed_hosts_) {
SetError(sapi::FailedPreconditionError(
"AddNetworkProxyPolicy or AddNetworkProxyHandlerPolicy must be called "
"before adding IP rules"));
return *this;
}
sapi::Status status = allowed_hosts_->AllowIPv6(ip_and_mask, port);
if (!status.ok()) {
SetError(status);
}
return *this;
}
} // namespace sandbox2 } // namespace sandbox2

View File

@ -31,6 +31,7 @@
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "sandboxed_api/sandbox2/mounts.h" #include "sandboxed_api/sandbox2/mounts.h"
#include "sandboxed_api/sandbox2/network_proxy/filtering.h"
#include "sandboxed_api/sandbox2/policy.h" #include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/util/statusor.h" #include "sandboxed_api/util/statusor.h"
@ -501,6 +502,10 @@ class PolicyBuilder final {
// Not recommended // Not recommended
PolicyBuilder& SetRootWritable(); PolicyBuilder& SetRootWritable();
// Allows connections to this IP.
PolicyBuilder& AllowIPv4(const std::string& ip_and_mask, uint32_t port = 0);
PolicyBuilder& AllowIPv6(const std::string& ip_and_mask, uint32_t port = 0);
private: private:
friend class PolicyBuilderPeer; // For testing friend class PolicyBuilderPeer; // For testing
friend class StackTracePeer; friend class StackTracePeer;
@ -542,6 +547,9 @@ class PolicyBuilder final {
// This function returns a PolicyBuilder so that we can use it in the status // This function returns a PolicyBuilder so that we can use it in the status
// macros // macros
PolicyBuilder& SetError(const sapi::Status& status); PolicyBuilder& SetError(const sapi::Status& status);
// Contains list of allowed hosts.
absl::optional<AllowedHosts> allowed_hosts_;
}; };
} // namespace sandbox2 } // namespace sandbox2

View File

@ -79,6 +79,10 @@ std::string Result::ToString() const {
Syscall(GetSyscallArch(), reason_code()).GetName(), Syscall(GetSyscallArch(), reason_code()).GetName(),
"] Stack: ", GetStackTrace()); "] Stack: ", GetStackTrace());
break; break;
case sandbox2::Result::NETWORK_VIOLATION:
result = absl::StrCat("NETWORK VIOLATION - Violating network policy: ",
network_violation_);
break;
case sandbox2::Result::SIGNALED: case sandbox2::Result::SIGNALED:
result = absl::StrCat("Process terminated with a SIGNAL - Signal: ", result = absl::StrCat("Process terminated with a SIGNAL - Signal: ",
util::GetSignalName(reason_code()), util::GetSignalName(reason_code()),
@ -129,6 +133,8 @@ std::string Result::StatusEnumToString(StatusEnum value) {
return "SETUP_ERROR"; return "SETUP_ERROR";
case sandbox2::Result::VIOLATION: case sandbox2::Result::VIOLATION:
return "VIOLATION"; return "VIOLATION";
case sandbox2::Result::NETWORK_VIOLATION:
return "NETWORK_VIOLATION";
case sandbox2::Result::SIGNALED: case sandbox2::Result::SIGNALED:
return "SIGNALED"; return "SIGNALED";
case sandbox2::Result::TIMEOUT: case sandbox2::Result::TIMEOUT:

View File

@ -45,6 +45,8 @@ class Result {
SETUP_ERROR, SETUP_ERROR,
// Syscall violation // Syscall violation
VIOLATION, VIOLATION,
// Network policy violation
NETWORK_VIOLATION,
// Process terminated with a signal // Process terminated with a signal
SIGNALED, SIGNALED,
// Process terminated with a timeout // Process terminated with a timeout
@ -119,6 +121,10 @@ class Result {
syscall_ = std::move(syscall); syscall_ = std::move(syscall);
} }
void SetNetworkViolation(std::string network_violation) {
network_violation_ = std::move(network_violation);
}
StatusEnum final_status() const { return final_status_; } StatusEnum final_status() const { return final_status_; }
uintptr_t reason_code() const { return reason_code_; } uintptr_t reason_code() const { return reason_code_; }
@ -137,6 +143,8 @@ class Result {
const std::string& GetProgName() const { return prog_name_; } const std::string& GetProgName() const { return prog_name_; }
const std::string& GetNetworkViolation() const { return network_violation_; }
void SetProgName(const std::string& name) { prog_name_ = name; } void SetProgName(const std::string& name) { prog_name_ = name; }
const std::string& GetProcMaps() const { return proc_maps_; } const std::string& GetProcMaps() const { return proc_maps_; }
@ -179,6 +187,8 @@ class Result {
std::string prog_name_; std::string prog_name_;
// /proc/pid/maps of the main process. // /proc/pid/maps of the main process.
std::string proc_maps_; std::string proc_maps_;
// IP and port if network violation occurred
std::string network_violation_;
// Final resource usage as defined in <sys/resource.h> (man getrusage), for // Final resource usage as defined in <sys/resource.h> (man getrusage), for
// the Monitor thread. // the Monitor thread.
rusage rusage_monitor_; rusage rusage_monitor_;