diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index 7a94c20..bac3822 100644 --- a/sandboxed_api/sandbox2/BUILD.bazel +++ b/sandboxed_api/sandbox2/BUILD.bazel @@ -89,7 +89,7 @@ cc_library( ":syscall", ":util", "//sandboxed_api/util:status", - "@com_google_absl//absl/base", + "//sandboxed_api/util:statusor", "@com_google_absl//absl/memory", "@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( name = "ipc", srcs = ["ipc.cc"], @@ -166,8 +136,6 @@ cc_library( ":comms", ":logserver", ":logsink", - ":network_proxy_client", - ":network_proxy_server", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", @@ -186,6 +154,7 @@ cc_library( ":regs", ":syscall", ":violation_cc_proto", + "//sandboxed_api/sandbox2/network_proxy:filtering", "//sandboxed_api/sandbox2/util:bpf_helper", "//sandboxed_api/util:flags", "@com_google_absl//absl/base:core_headers", @@ -306,10 +275,10 @@ cc_library( ":client", ":executor", ":comms", - ":violation_cc_proto", - ":forkserver", ":forkserver_cc_proto", ":global_forkserver", + ":violation_cc_proto", + ":forkserver", ":ipc", ":limits", ":logsink", @@ -321,7 +290,6 @@ cc_library( ":result", ":syscall", ":util", - ":network_proxy_client", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", @@ -333,12 +301,17 @@ cc_library( "@com_google_absl//absl/time", "@com_google_absl//absl/types:optional", "@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:unwind_cc_proto", "//sandboxed_api/sandbox2/util:bpf_helper", "//sandboxed_api/sandbox2/util:file_base", "//sandboxed_api/sandbox2/util:fileops", + "//sandboxed_api/sandbox2/util:strerror", "//sandboxed_api/util:raw_logging", + "//util/functional", "//sandboxed_api/util:status", "//sandboxed_api/util:statusor", @@ -364,7 +337,7 @@ cc_library( deps = [ ":comms", ":logsink", - ":network_proxy_client", + "//sandboxed_api/sandbox2/network_proxy:client", "//sandboxed_api/sandbox2/util:file_helpers", "//sandboxed_api/sandbox2/util:fileops", "//sandboxed_api/sandbox2/util:strerror", diff --git a/sandboxed_api/sandbox2/CMakeLists.txt b/sandboxed_api/sandbox2/CMakeLists.txt index b2b58fd..fa8c515 100644 --- a/sandboxed_api/sandbox2/CMakeLists.txt +++ b/sandboxed_api/sandbox2/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(examples) add_subdirectory(unwind) add_subdirectory(util) +add_subdirectory(network_proxy) # sandboxed_api/sandbox2:bpfdisassembler add_library(sandbox2_bpfdisassembler STATIC @@ -121,36 +122,6 @@ target_link_libraries(sandbox2_logsink 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 add_library(sandbox2_ipc STATIC ipc.cc diff --git a/sandboxed_api/sandbox2/client.cc b/sandboxed_api/sandbox2/client.cc index 235ec19..d07c9b1 100644 --- a/sandboxed_api/sandbox2/client.cc +++ b/sandboxed_api/sandbox2/client.cc @@ -40,7 +40,6 @@ #include "absl/strings/str_join.h" #include "absl/strings/str_split.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/util/strerror.h" #include "sandboxed_api/util/raw_logging.h" diff --git a/sandboxed_api/sandbox2/client.h b/sandboxed_api/sandbox2/client.h index d1a4c2c..3518ff8 100644 --- a/sandboxed_api/sandbox2/client.h +++ b/sandboxed_api/sandbox2/client.h @@ -25,7 +25,7 @@ #include "sandboxed_api/sandbox2/comms.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 { diff --git a/sandboxed_api/sandbox2/examples/network_proxy/BUILD.bazel b/sandboxed_api/sandbox2/examples/network_proxy/BUILD.bazel index 2a3faed..ad15d87 100644 --- a/sandboxed_api/sandbox2/examples/network_proxy/BUILD.bazel +++ b/sandboxed_api/sandbox2/examples/network_proxy/BUILD.bazel @@ -14,10 +14,10 @@ # The 'network proxy' example demonstrates how to use network proxy server. -licenses(["notice"]) - load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts") +licenses(["notice"]) + # Executor cc_binary( name = "networkproxy_sandbox", @@ -27,6 +27,7 @@ cc_binary( deps = [ "//sandboxed_api/sandbox2", "//sandboxed_api/sandbox2:comms", + "//sandboxed_api/sandbox2/network_proxy:filtering", "//sandboxed_api/sandbox2/util:bpf_helper", "//sandboxed_api/sandbox2/util:fileops", "//sandboxed_api/sandbox2/util:runfiles", @@ -42,7 +43,7 @@ cc_binary( deps = [ "//sandboxed_api/sandbox2:client", "//sandboxed_api/sandbox2:comms", - "//sandboxed_api/sandbox2:network_proxy_client", + "//sandboxed_api/sandbox2/network_proxy:client", "//sandboxed_api/sandbox2/util:fileops", "@com_google_absl//absl/strings:str_format", ], diff --git a/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_bin.cc b/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_bin.cc index cd57021..d859f67 100644 --- a/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_bin.cc +++ b/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_bin.cc @@ -14,7 +14,7 @@ #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/network_proxy/client.h" #include "sandboxed_api/sandbox2/util/fileops.h" 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())) { return 4; } + return 0; } diff --git a/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc b/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc index 46aae97..ef80b00 100644 --- a/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc +++ b/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc @@ -42,6 +42,8 @@ std::unique_ptr GetPolicy(absl::string_view sandboxee_path) { .AllowSyscall(__NR_munmap) .AllowSyscall(__NR_getpid) .AddNetworkProxyHandlerPolicy() + .AllowTcMalloc() + .AllowIPv6("::1") .AddLibrariesForBinary(sandboxee_path) .BuildOrDie(); } @@ -129,10 +131,7 @@ int main(int argc, char** argv) { // 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(); + .set_cwd("/"); executor ->limits() // Remove restrictions on the size of address-space of sandboxed diff --git a/sandboxed_api/sandbox2/ipc.cc b/sandboxed_api/sandbox2/ipc.cc index 7c77654..7a25ff5 100644 --- a/sandboxed_api/sandbox2/ipc.cc +++ b/sandboxed_api/sandbox2/ipc.cc @@ -17,16 +17,12 @@ #include "sandboxed_api/sandbox2/ipc.h" #include -#include - -#include // NOLINT(build/c++11) +#include #include #include "absl/memory/memory.h" #include "sandboxed_api/sandbox2/logserver.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 { @@ -103,16 +99,4 @@ void IPC::EnableLogServer() { 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 diff --git a/sandboxed_api/sandbox2/ipc.h b/sandboxed_api/sandbox2/ipc.h index 52c4704..69f8421 100644 --- a/sandboxed_api/sandbox2/ipc.h +++ b/sandboxed_api/sandbox2/ipc.h @@ -23,7 +23,6 @@ #include #include -#include "absl/base/macros.h" #include "absl/strings/string_view.h" #include "sandboxed_api/sandbox2/comms.h" @@ -60,10 +59,6 @@ class IPC final { // Client::SendLogsToSupervisor in the sandboxee. 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: friend class Executor; friend class Monitor; diff --git a/sandboxed_api/sandbox2/monitor.cc b/sandboxed_api/sandbox2/monitor.cc index 1e62f86..9cc251f 100644 --- a/sandboxed_api/sandbox2/monitor.cc +++ b/sandboxed_api/sandbox2/monitor.cc @@ -53,6 +53,7 @@ #include "sandboxed_api/sandbox2/limits.h" #include "sandboxed_api/sandbox2/mounts.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/regs.h" #include "sandboxed_api/sandbox2/result.h" @@ -140,6 +141,9 @@ Monitor::~Monitor() { if (log_file_) { std::fclose(log_file_); } + if (network_proxy_server_) { + network_proxy_thread_.join(); + } } namespace { @@ -198,6 +202,10 @@ void Monitor::Run() { // sandbox master/monitor, which ptrace_attach'es to the child. int clone_flags = CLONE_UNTRACED; + if (policy_->allowed_hosts_) { + EnableNetworkProxyServer(); + } + // Get PID of the sandboxee. pid_t init_pid = 0; Namespace* ns = policy_->GetNamespace(); @@ -349,6 +357,14 @@ void Monitor::MainLoop(sigset_t* sset) { 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 // returns quickly if there are no events to be processed. // Prioritize main pid to avoid resource starvation @@ -401,7 +417,10 @@ void Monitor::MainLoop(sigset_t* sset) { VLOG(1) << "PID: " << ret << " terminated with signal: " << util::GetSignalName(WTERMSIG(status)); 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); } else if (timed_out_) { SetExitStatusCode(Result::TIMEOUT, 0); @@ -794,7 +813,10 @@ void Monitor::EventPtraceExit(pid_t pid, int event_msg) { // 3) Regular signal/other exit cause. if (pid == pid_) { 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); } else if (timed_out_) { 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( + fd, &policy_->allowed_hosts_.value(), pthread_self()); + + network_proxy_thread_ = std::thread(&NetworkProxyServer::Run, + network_proxy_server_.get()); +} + } // namespace sandbox2 diff --git a/sandboxed_api/sandbox2/monitor.h b/sandboxed_api/sandbox2/monitor.h index 61358c8..069d93e 100644 --- a/sandboxed_api/sandbox2/monitor.h +++ b/sandboxed_api/sandbox2/monitor.h @@ -26,11 +26,13 @@ #include #include #include +#include #include "absl/synchronization/notification.h" #include "sandboxed_api/sandbox2/comms.h" #include "sandboxed_api/sandbox2/executor.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/policy.h" #include "sandboxed_api/sandbox2/regs.h" @@ -139,6 +141,10 @@ class Monitor final { // Processes stop path. 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. Executor* executor_; Notify* notify_; @@ -168,6 +174,8 @@ class Monitor final { std::atomic deadline_millis_{0}; // Was external kill sent to the sandboxee bool external_kill_ = false; + // Network violation occurred and process of killing sandboxee started + bool network_violation_ = false; // Is the sandboxee timed out bool timed_out_ = false; // Should we dump the main sandboxed PID's stack? @@ -178,6 +186,12 @@ class Monitor final { // Log file specified by // --sandbox_danger_danger_permit_all_and_log flag. FILE* log_file_ = nullptr; + + // Handle to the class responsible for proxying and validating connect() + // requests. + std::unique_ptr network_proxy_server_; + + std::thread network_proxy_thread_; }; } // namespace sandbox2 diff --git a/sandboxed_api/sandbox2/network_proxy_client.cc b/sandboxed_api/sandbox2/network_proxy_client.cc deleted file mode 100644 index 5e214e9..0000000 --- a/sandboxed_api/sandbox2/network_proxy_client.cc +++ /dev/null @@ -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 -#include -#include -#include -#include - -#include -#include - -#include -#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(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::type; -#endif - - int syscall = registers[kRegSyscall]; - - int sockfd; - const struct sockaddr* addr; - socklen_t addrlen; - - if (syscall == __NR_connect) { - sockfd = static_cast(registers[kRegArg0]); - addr = reinterpret_cast(registers[kRegArg1]); - addrlen = static_cast(registers[kRegArg2]); -#if defined(__powerpc64__) - } else if (syscall == __NR_socketcall && - static_cast(registers[kRegArg0]) == SYS_CONNECT) { - ppc_gpreg_t* args = reinterpret_cast(registers[kRegArg1]); - - sockfd = static_cast(args[0]); - addr = reinterpret_cast(args[1]); - addrlen = static_cast(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 diff --git a/sandboxed_api/sandbox2/network_proxy_client.h b/sandboxed_api/sandbox2/network_proxy_client.h deleted file mode 100644 index 111a1c1..0000000 --- a/sandboxed_api/sandbox2/network_proxy_client.h +++ /dev/null @@ -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 - -#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_ diff --git a/sandboxed_api/sandbox2/network_proxy_server.cc b/sandboxed_api/sandbox2/network_proxy_server.cc deleted file mode 100644 index e0f7552..0000000 --- a/sandboxed_api/sandbox2/network_proxy_server.cc +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include "absl/memory/memory.h" -#include "sandboxed_api/sandbox2/util/fileops.h" - -namespace sandbox2 { - -NetworkProxyServer::NetworkProxyServer(int fd) - : comms_{absl::make_unique(fd)}, fatal_error_{false} {} - -void NetworkProxyServer::ProcessConnectRequest() { - std::vector addr; - if (!comms_->RecvBytes(&addr)) { - fatal_error_ = true; - return; - } - - const struct sockaddr_in* saddr = - reinterpret_cast(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(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 diff --git a/sandboxed_api/sandbox2/network_proxy_server.h b/sandboxed_api/sandbox2/network_proxy_server.h deleted file mode 100644 index 55f6c5b..0000000 --- a/sandboxed_api/sandbox2/network_proxy_server.h +++ /dev/null @@ -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 - -#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_; - bool fatal_error_; -}; - -} // namespace sandbox2 - -#endif // SANDBOXED_API_SANDBOX2_NETWORK_PROXY_SERVER_H_ diff --git a/sandboxed_api/sandbox2/policy.h b/sandboxed_api/sandbox2/policy.h index b507f22..d4a0ca1 100644 --- a/sandboxed_api/sandbox2/policy.h +++ b/sandboxed_api/sandbox2/policy.h @@ -31,6 +31,7 @@ #include "absl/base/macros.h" #include "absl/types/optional.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/violation.pb.h" @@ -104,6 +105,9 @@ class Policy final { // Get a policy which would allow the Monitor module to track all syscalls. std::vector GetTrackingPolicy() const; + // Contains a list of hosts the sandboxee is allowed to connect to. + absl::optional allowed_hosts_; + friend class Monitor; friend class PolicyBuilder; friend class PolicyBuilderPeer; // For testing diff --git a/sandboxed_api/sandbox2/policybuilder.cc b/sandboxed_api/sandbox2/policybuilder.cc index c731e97..99b4b97 100644 --- a/sandboxed_api/sandbox2/policybuilder.cc +++ b/sandboxed_api/sandbox2/policybuilder.cc @@ -39,6 +39,7 @@ #include "sandboxed_api/sandbox2/namespace.h" #include "sandboxed_api/sandbox2/util/bpf_helper.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/status_macros.h" @@ -47,7 +48,7 @@ namespace { } // namespace -PolicyBuilder& PolicyBuilder::AllowSyscall(unsigned int num) { +PolicyBuilder& PolicyBuilder::AllowSyscall(uint32_t num) { if (handled_syscalls_.insert(num).second) { user_policy_.insert(user_policy_.end(), {SYSCALL(num, ALLOW)}); } @@ -68,8 +69,7 @@ PolicyBuilder& PolicyBuilder::AllowSyscalls(SyscallInitializer nums) { return *this; } -PolicyBuilder& PolicyBuilder::BlockSyscallWithErrno(unsigned int num, - int error) { +PolicyBuilder& PolicyBuilder::BlockSyscallWithErrno(uint32_t num, int error) { if (handled_syscalls_.insert(num).second) { 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) { return AddPolicyOnSyscalls({num}, policy); } PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall( - unsigned int num, const std::vector& policy) { + uint32_t num, const std::vector& 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); } @@ -700,6 +700,7 @@ sapi::StatusOr> PolicyBuilder::TryBuild() { StoreDescription(pb_description.get()); output->policy_builder_description_ = std::move(pb_description); + output->allowed_hosts_ = std::move(allowed_hosts_); already_built_ = true; return std::move(output); } @@ -848,6 +849,15 @@ PolicyBuilder& PolicyBuilder::CollectStacktracesOnKill(bool enable) { } 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_WAIT); 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 diff --git a/sandboxed_api/sandbox2/policybuilder.h b/sandboxed_api/sandbox2/policybuilder.h index b9d48db..c4997a7 100644 --- a/sandboxed_api/sandbox2/policybuilder.h +++ b/sandboxed_api/sandbox2/policybuilder.h @@ -31,6 +31,7 @@ #include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "sandboxed_api/sandbox2/mounts.h" +#include "sandboxed_api/sandbox2/network_proxy/filtering.h" #include "sandboxed_api/sandbox2/policy.h" #include "sandboxed_api/util/statusor.h" @@ -501,6 +502,10 @@ class PolicyBuilder final { // Not recommended 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: friend class PolicyBuilderPeer; // For testing friend class StackTracePeer; @@ -542,6 +547,9 @@ class PolicyBuilder final { // This function returns a PolicyBuilder so that we can use it in the status // macros PolicyBuilder& SetError(const sapi::Status& status); + + // Contains list of allowed hosts. + absl::optional allowed_hosts_; }; } // namespace sandbox2 diff --git a/sandboxed_api/sandbox2/result.cc b/sandboxed_api/sandbox2/result.cc index 1fa194f..a83e457 100644 --- a/sandboxed_api/sandbox2/result.cc +++ b/sandboxed_api/sandbox2/result.cc @@ -79,6 +79,10 @@ std::string Result::ToString() const { Syscall(GetSyscallArch(), reason_code()).GetName(), "] Stack: ", GetStackTrace()); break; + case sandbox2::Result::NETWORK_VIOLATION: + result = absl::StrCat("NETWORK VIOLATION - Violating network policy: ", + network_violation_); + break; case sandbox2::Result::SIGNALED: result = absl::StrCat("Process terminated with a SIGNAL - Signal: ", util::GetSignalName(reason_code()), @@ -129,6 +133,8 @@ std::string Result::StatusEnumToString(StatusEnum value) { return "SETUP_ERROR"; case sandbox2::Result::VIOLATION: return "VIOLATION"; + case sandbox2::Result::NETWORK_VIOLATION: + return "NETWORK_VIOLATION"; case sandbox2::Result::SIGNALED: return "SIGNALED"; case sandbox2::Result::TIMEOUT: diff --git a/sandboxed_api/sandbox2/result.h b/sandboxed_api/sandbox2/result.h index 77cb043..cbda608 100644 --- a/sandboxed_api/sandbox2/result.h +++ b/sandboxed_api/sandbox2/result.h @@ -45,6 +45,8 @@ class Result { SETUP_ERROR, // Syscall violation VIOLATION, + // Network policy violation + NETWORK_VIOLATION, // Process terminated with a signal SIGNALED, // Process terminated with a timeout @@ -119,6 +121,10 @@ class Result { syscall_ = std::move(syscall); } + void SetNetworkViolation(std::string network_violation) { + network_violation_ = std::move(network_violation); + } + StatusEnum final_status() const { return final_status_; } 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& GetNetworkViolation() const { return network_violation_; } + void SetProgName(const std::string& name) { prog_name_ = name; } const std::string& GetProcMaps() const { return proc_maps_; } @@ -179,6 +187,8 @@ class Result { std::string prog_name_; // /proc/pid/maps of the main process. std::string proc_maps_; + // IP and port if network violation occurred + std::string network_violation_; // Final resource usage as defined in (man getrusage), for // the Monitor thread. rusage rusage_monitor_;