diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index bac3822..7a94c20 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", - "//sandboxed_api/util:statusor", + "@com_google_absl//absl/base", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", ], @@ -127,6 +127,36 @@ 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"], @@ -136,6 +166,8 @@ 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", @@ -154,7 +186,6 @@ 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", @@ -275,10 +306,10 @@ cc_library( ":client", ":executor", ":comms", - ":forkserver_cc_proto", - ":global_forkserver", ":violation_cc_proto", ":forkserver", + ":forkserver_cc_proto", + ":global_forkserver", ":ipc", ":limits", ":logsink", @@ -290,6 +321,7 @@ 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", @@ -301,17 +333,12 @@ 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", @@ -337,7 +364,7 @@ cc_library( deps = [ ":comms", ":logsink", - "//sandboxed_api/sandbox2/network_proxy:client", + ":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 fa8c515..b2b58fd 100644 --- a/sandboxed_api/sandbox2/CMakeLists.txt +++ b/sandboxed_api/sandbox2/CMakeLists.txt @@ -15,7 +15,6 @@ add_subdirectory(examples) add_subdirectory(unwind) add_subdirectory(util) -add_subdirectory(network_proxy) # sandboxed_api/sandbox2:bpfdisassembler add_library(sandbox2_bpfdisassembler STATIC @@ -122,6 +121,36 @@ 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 d07c9b1..235ec19 100644 --- a/sandboxed_api/sandbox2/client.cc +++ b/sandboxed_api/sandbox2/client.cc @@ -40,6 +40,7 @@ #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 3518ff8..d1a4c2c 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 ad15d87..2a3faed 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. -load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts") - licenses(["notice"]) +load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts") + # Executor cc_binary( name = "networkproxy_sandbox", @@ -27,7 +27,6 @@ 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", @@ -43,7 +42,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 d859f67..cd57021 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,6 +113,5 @@ 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 ef80b00..46aae97 100644 --- a/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc +++ b/sandboxed_api/sandbox2/examples/network_proxy/networkproxy_sandbox.cc @@ -42,8 +42,6 @@ std::unique_ptr GetPolicy(absl::string_view sandboxee_path) { .AllowSyscall(__NR_munmap) .AllowSyscall(__NR_getpid) .AddNetworkProxyHandlerPolicy() - .AllowTcMalloc() - .AllowIPv6("::1") .AddLibrariesForBinary(sandboxee_path) .BuildOrDie(); } @@ -131,7 +129,10 @@ 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("/"); + .set_cwd("/") + // Enable built-in network proxy. + .ipc() + ->EnableNetworkProxyServer(); 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 7a25ff5..7c77654 100644 --- a/sandboxed_api/sandbox2/ipc.cc +++ b/sandboxed_api/sandbox2/ipc.cc @@ -17,12 +17,16 @@ #include "sandboxed_api/sandbox2/ipc.h" #include -#include +#include + +#include // NOLINT(build/c++11) #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 { @@ -99,4 +103,16 @@ 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 69f8421..52c4704 100644 --- a/sandboxed_api/sandbox2/ipc.h +++ b/sandboxed_api/sandbox2/ipc.h @@ -23,6 +23,7 @@ #include #include +#include "absl/base/macros.h" #include "absl/strings/string_view.h" #include "sandboxed_api/sandbox2/comms.h" @@ -59,6 +60,10 @@ 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 9cc251f..1e62f86 100644 --- a/sandboxed_api/sandbox2/monitor.cc +++ b/sandboxed_api/sandbox2/monitor.cc @@ -53,7 +53,6 @@ #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" @@ -141,9 +140,6 @@ Monitor::~Monitor() { if (log_file_) { std::fclose(log_file_); } - if (network_proxy_server_) { - network_proxy_thread_.join(); - } } namespace { @@ -202,10 +198,6 @@ 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(); @@ -357,14 +349,6 @@ 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 @@ -417,10 +401,7 @@ void Monitor::MainLoop(sigset_t* sset) { VLOG(1) << "PID: " << ret << " terminated with signal: " << util::GetSignalName(WTERMSIG(status)); if (ret == pid_) { - if (network_violation_) { - SetExitStatusCode(Result::NETWORK_VIOLATION, 0); - result_.SetNetworkViolation(network_proxy_server_->violation_msg_); - } else if (external_kill_) { + if (external_kill_) { SetExitStatusCode(Result::EXTERNAL_KILL, 0); } else if (timed_out_) { SetExitStatusCode(Result::TIMEOUT, 0); @@ -813,10 +794,7 @@ 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 (network_violation_) { - SetExitStatusCode(Result::NETWORK_VIOLATION, 0); - result_.SetNetworkViolation(network_proxy_server_->violation_msg_); - } else if (external_kill_) { + if (external_kill_) { SetExitStatusCode(Result::EXTERNAL_KILL, 0); } else if (timed_out_) { SetExitStatusCode(Result::TIMEOUT, 0); @@ -948,14 +926,4 @@ 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 069d93e..61358c8 100644 --- a/sandboxed_api/sandbox2/monitor.h +++ b/sandboxed_api/sandbox2/monitor.h @@ -26,13 +26,11 @@ #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" @@ -141,10 +139,6 @@ 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_; @@ -174,8 +168,6 @@ 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? @@ -186,12 +178,6 @@ 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 new file mode 100644 index 0000000..5e214e9 --- /dev/null +++ b/sandboxed_api/sandbox2/network_proxy_client.cc @@ -0,0 +1,217 @@ +// 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 new file mode 100644 index 0000000..111a1c1 --- /dev/null +++ b/sandboxed_api/sandbox2/network_proxy_client.h @@ -0,0 +1,75 @@ +// 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 new file mode 100644 index 0000000..e0f7552 --- /dev/null +++ b/sandboxed_api/sandbox2/network_proxy_server.cc @@ -0,0 +1,95 @@ +// 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 new file mode 100644 index 0000000..55f6c5b --- /dev/null +++ b/sandboxed_api/sandbox2/network_proxy_server.h @@ -0,0 +1,53 @@ +// 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 d4a0ca1..b507f22 100644 --- a/sandboxed_api/sandbox2/policy.h +++ b/sandboxed_api/sandbox2/policy.h @@ -31,7 +31,6 @@ #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" @@ -105,9 +104,6 @@ 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 99b4b97..c731e97 100644 --- a/sandboxed_api/sandbox2/policybuilder.cc +++ b/sandboxed_api/sandbox2/policybuilder.cc @@ -39,7 +39,6 @@ #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" @@ -48,7 +47,7 @@ namespace { } // namespace -PolicyBuilder& PolicyBuilder::AllowSyscall(uint32_t num) { +PolicyBuilder& PolicyBuilder::AllowSyscall(unsigned int num) { if (handled_syscalls_.insert(num).second) { user_policy_.insert(user_policy_.end(), {SYSCALL(num, ALLOW)}); } @@ -69,7 +68,8 @@ PolicyBuilder& PolicyBuilder::AllowSyscalls(SyscallInitializer nums) { return *this; } -PolicyBuilder& PolicyBuilder::BlockSyscallWithErrno(uint32_t num, int error) { +PolicyBuilder& PolicyBuilder::BlockSyscallWithErrno(unsigned int 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(uint32_t num, +PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(unsigned int num, BpfInitializer policy) { return AddPolicyOnSyscalls({num}, policy); } PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall( - uint32_t num, const std::vector& policy) { + unsigned int num, const std::vector& policy) { return AddPolicyOnSyscalls({num}, policy); } -PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(uint32_t num, BpfFunc f) { +PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(unsigned int num, BpfFunc f) { return AddPolicyOnSyscalls({num}, f); } @@ -700,7 +700,6 @@ 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); } @@ -849,15 +848,6 @@ 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); @@ -930,36 +920,4 @@ 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 c4997a7..b9d48db 100644 --- a/sandboxed_api/sandbox2/policybuilder.h +++ b/sandboxed_api/sandbox2/policybuilder.h @@ -31,7 +31,6 @@ #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" @@ -502,10 +501,6 @@ 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; @@ -547,9 +542,6 @@ 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 a83e457..1fa194f 100644 --- a/sandboxed_api/sandbox2/result.cc +++ b/sandboxed_api/sandbox2/result.cc @@ -79,10 +79,6 @@ 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()), @@ -133,8 +129,6 @@ 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 cbda608..77cb043 100644 --- a/sandboxed_api/sandbox2/result.h +++ b/sandboxed_api/sandbox2/result.h @@ -45,8 +45,6 @@ class Result { SETUP_ERROR, // Syscall violation VIOLATION, - // Network policy violation - NETWORK_VIOLATION, // Process terminated with a signal SIGNALED, // Process terminated with a timeout @@ -121,10 +119,6 @@ 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_; } @@ -143,8 +137,6 @@ 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_; } @@ -187,8 +179,6 @@ 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_;