From 2c8c9a489adc766479cd1ad10474e639a77868b1 Mon Sep 17 00:00:00 2001 From: Christian Blichmann Date: Thu, 14 May 2020 11:40:02 +0200 Subject: [PATCH] Add external embedding example This change contains a "hello world"-style example library to be sandboxed. It consists of a stand-alone CMake and Bazel project that uses Sandboxed API as its dependency. To use Sandboxed API in an external project, it should be enough to copy the files in the `sandboxed_api/examples/hello_sapi` directory as a starting point. --- sandboxed_api/examples/hello_sapi/.bazelrc | 2 + sandboxed_api/examples/hello_sapi/BUILD.bazel | 60 ++++++++++++++ .../examples/hello_sapi/CMakeLists.txt | 82 +++++++++++++++++++ .../examples/hello_sapi/WORKSPACE.bazel | 49 +++++++++++ .../examples/hello_sapi/hello_lib.cc | 19 +++++ .../examples/hello_sapi/hello_main.cc | 38 +++++++++ .../examples/hello_sapi/hello_transacted.cc | 79 ++++++++++++++++++ 7 files changed, 329 insertions(+) create mode 100644 sandboxed_api/examples/hello_sapi/.bazelrc create mode 100644 sandboxed_api/examples/hello_sapi/BUILD.bazel create mode 100644 sandboxed_api/examples/hello_sapi/CMakeLists.txt create mode 100644 sandboxed_api/examples/hello_sapi/WORKSPACE.bazel create mode 100644 sandboxed_api/examples/hello_sapi/hello_lib.cc create mode 100644 sandboxed_api/examples/hello_sapi/hello_main.cc create mode 100644 sandboxed_api/examples/hello_sapi/hello_transacted.cc diff --git a/sandboxed_api/examples/hello_sapi/.bazelrc b/sandboxed_api/examples/hello_sapi/.bazelrc new file mode 100644 index 0000000..a68e070 --- /dev/null +++ b/sandboxed_api/examples/hello_sapi/.bazelrc @@ -0,0 +1,2 @@ +# Build in C++17 mode without a custom CROSSTOOL +build --cxxopt=-std=c++17 diff --git a/sandboxed_api/examples/hello_sapi/BUILD.bazel b/sandboxed_api/examples/hello_sapi/BUILD.bazel new file mode 100644 index 0000000..b594a58 --- /dev/null +++ b/sandboxed_api/examples/hello_sapi/BUILD.bazel @@ -0,0 +1,60 @@ +# Copyright 2020 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. + +load( + "@com_google_sandboxed_api//sandboxed_api/bazel:sapi.bzl", + "sapi_library", +) + +# Library with code that should be sandboxed +cc_library( + name = "hello_lib", + srcs = ["hello_lib.cc"], + alwayslink = 1, +) + +# Sandboxed API for the library above +sapi_library( + name = "hello_sapi", + functions = [ + "AddTwoIntegers", + ], + input_files = ["hello_lib.cc"], + lib = ":hello_lib", + lib_name = "Hello", + visibility = ["//visibility:public"], +) + +# Main executable demonstrating how the sandboxed library is used +cc_binary( + name = "hello", + srcs = ["hello_main.cc"], + copts = ["-I."], # To find the generated header + deps = [":hello_sapi"], +) + +# Another example using the same library, but using the Transaction API that +# automatically retries sandbox operations. Also demonstates error handling +# and a custom security policy. +cc_binary( + name = "hello_transacted", + srcs = ["hello_transacted.cc"], + copts = ["-I."], # To find the generated header + deps = [ + ":hello_sapi", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_sandboxed_api//sandboxed_api/util:status", + ], +) diff --git a/sandboxed_api/examples/hello_sapi/CMakeLists.txt b/sandboxed_api/examples/hello_sapi/CMakeLists.txt new file mode 100644 index 0000000..bb076f5 --- /dev/null +++ b/sandboxed_api/examples/hello_sapi/CMakeLists.txt @@ -0,0 +1,82 @@ +# Copyright 2020 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. + +# Example that demonstrates how to embed Sandboxed API into a project using +# CMake. + +cmake_minimum_required(VERSION 3.12) + +project(hello_sapi_project CXX) + +# Path to the Sandboxed API source tree. Unlike Bazel, CMake does not download +# downstream dependencies by default. So the option below needs to be adjusted +# to point to a local checkout or a Git submodule. +# The default value is chosen so that this example can be easily tried out for +# a regular checkout of Sandboxed API. +set(SAPI_ROOT "${PROJECT_SOURCE_DIR}/../../.." + CACHE PATH "Path to the Sandboxed API source tree") + +# Configure options and include Sandboxed API as a sub-directory. +set(SAPI_ENABLE_EXAMPLES OFF CACHE BOOL "") +set(SAPI_ENABLE_TESTS OFF CACHE BOOL "") +add_subdirectory("${SAPI_ROOT}" + "${CMAKE_BINARY_DIR}/sandboxed-api-build" EXCLUDE_FROM_ALL) + +# Interface library with common settings for this projects +add_library(hello_base INTERFACE) +add_library(hello::base ALIAS hello_base) +target_compile_features(hello_base INTERFACE cxx_std_17) +target_include_directories(hello_base INTERFACE + "${PROJECT_BINARY_DIR}" # To find the generated SAPI header +) + +# Library with code that should be sandboxed +add_library(hello_lib STATIC + hello_lib.cc +) +target_link_libraries(hello_lib PRIVATE + hello::base +) + +# Sandboxed API for the library above +add_sapi_library(hello_sapi + FUNCTIONS AddTwoIntegers + INPUTS hello_lib.cc + LIBRARY hello_lib + LIBRARY_NAME Hello + NAMESPACE "" +) +add_library(hello::sapi ALIAS hello_sapi) + +# Main executable demonstrating how the sandboxed library is used +add_executable(hello + hello_main.cc +) +target_link_libraries(hello PRIVATE + hello::base + hello::sapi + sapi::sapi +) + +# Another example using the same library, but using the Transaction API that +# automatically retries sandbox operations. Also demonstates error handling +# and a custom security policy. +add_executable(hello_transacted + hello_transacted.cc +) +target_link_libraries(hello_transacted PRIVATE + hello::base + hello::sapi + sapi::sapi +) diff --git a/sandboxed_api/examples/hello_sapi/WORKSPACE.bazel b/sandboxed_api/examples/hello_sapi/WORKSPACE.bazel new file mode 100644 index 0000000..cf7455e --- /dev/null +++ b/sandboxed_api/examples/hello_sapi/WORKSPACE.bazel @@ -0,0 +1,49 @@ +# Copyright 2020 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. + +# Example workspace demonstrating how to embed Sandboxed API into a project +# using Bazel. + +workspace(name = "com_google_sandboxed_api_hello") + +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +# Include the Sandboxed API dependency if it does not already exist in this +# project. This ensures that this workspace plays well with other external +# dependencies that might use Sandboxed API. +maybe( + git_repository, + name = "com_google_sandboxed_api", + # This example depends on the latest master. In an embedding project, it + # is advisable to pin Sandboxed API to a specific version. + # commit = "ba47adc21d4c9bc316f3c7c32b0faaef952c111e", # 2020-05-15 + remote = "https://github.com/google/sandboxed-api.git", +) + +# From here on, Sandboxed API files are available. The statements below setup +# transitive dependencies such as Abseil. Like above, those will only be +# included if they don't already exist in the project. +load( + "@com_google_sandboxed_api//sandboxed_api/bazel:sapi_deps.bzl", + "sapi_deps", +) + +sapi_deps() + +# Need to separately setup Protobuf dependencies in order for the build rules +# to work. +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") + +protobuf_deps() diff --git a/sandboxed_api/examples/hello_sapi/hello_lib.cc b/sandboxed_api/examples/hello_sapi/hello_lib.cc new file mode 100644 index 0000000..bc83e9f --- /dev/null +++ b/sandboxed_api/examples/hello_sapi/hello_lib.cc @@ -0,0 +1,19 @@ +// Copyright 2020 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. + +// Adds two integer values and returns the result, serving as an example +// function that will be sandboxed. +extern "C" int AddTwoIntegers(int a, int b) { + return a + b; +} diff --git a/sandboxed_api/examples/hello_sapi/hello_main.cc b/sandboxed_api/examples/hello_sapi/hello_main.cc new file mode 100644 index 0000000..d43c7cd --- /dev/null +++ b/sandboxed_api/examples/hello_sapi/hello_main.cc @@ -0,0 +1,38 @@ +// Copyright 2020 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. + +// A minimal "hello world" style example of how to use Sandboxed API. This +// example does not include any error handling and will simply abort if +// something goes wrong. +// As the library function that is being called into is pure computation (i.e. +// does not issue any syscalls), the restricted default sandbox policy is being +// used. + +#include +#include + +#include "hello_sapi.sapi.h" // Generated header + +int main() { + std::cout << "Calling into a sandboxee to add two numbers...\n"; + + HelloSandbox sandbox; + sandbox.Init().IgnoreError(); + + HelloApi api(&sandbox); + std::cout << " 1000 + 337 = " << api.AddTwoIntegers(1000, 337).value() + << "\n"; + + return EXIT_SUCCESS; +} diff --git a/sandboxed_api/examples/hello_sapi/hello_transacted.cc b/sandboxed_api/examples/hello_sapi/hello_transacted.cc new file mode 100644 index 0000000..728e368 --- /dev/null +++ b/sandboxed_api/examples/hello_sapi/hello_transacted.cc @@ -0,0 +1,79 @@ +// Copyright 2020 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. + +// These are needed for the __NR_xxx syscall numbers +#include +#include + +#include +#include + +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "hello_sapi.sapi.h" // Generated header +#include "sandboxed_api/sandbox2/policy.h" +#include "sandboxed_api/sandbox2/policybuilder.h" +#include "sandboxed_api/transaction.h" +#include "sandboxed_api/util/status_macros.h" + +namespace { + +class CustomHelloSandbox : public HelloSandbox { + public: + std::unique_ptr ModifyPolicy( + sandbox2::PolicyBuilder*) override { + // Return a new policy. + return sandbox2::PolicyBuilder() + .AllowRead() + .AllowWrite() + .AllowOpen() + .AllowSystemMalloc() + .AllowHandleSignals() + .AllowExit() + .AllowStat() + .AllowTime() + .AllowGetIDs() + .AllowGetPIDs() + .AllowSyscalls({ + __NR_tgkill, + __NR_recvmsg, + __NR_sendmsg, + __NR_lseek, + __NR_nanosleep, + __NR_futex, + __NR_close, + }) + .AddFile("/etc/localtime") + .BuildOrDie(); + } +}; + +} // namespace + +int main() { + std::cout << "Calling into a sandboxee to add two numbers...\n"; + + sapi::BasicTransaction transaction(absl::make_unique()); + + absl::Status status = + transaction.Run([](sapi::Sandbox* sandbox) -> absl::Status { + HelloApi api(sandbox); + SAPI_ASSIGN_OR_RETURN(int result, api.AddTwoIntegers(1000, 337)); + std::cout << " 1000 + 337 = " << result << "\n"; + return absl::OkStatus(); + }); + if (!status.ok()) { + std::cerr << "Error during sandbox call: " << status.message() << "\n"; + } +}