1. Wybieranie metody wykonania piaskownicy
Piaskownica zaczyna się od wykonawcy (patrz Sandbox Executor), który jest odpowiedzialny za uruchomienie Sandboxee. Plik nagłówkowy executor.h zawiera interfejs API potrzebny do tego celu. API jest bardzo elastyczny i umożliwia wybór opcji, która najlepiej sprawdzi się w Twoim przypadku użycia. W sekcjach poniżej znajdziesz opis 3 metod, które możesz wybrać.
Metoda 1. Samodzielna – uruchomienie pliku binarnego z włączonym piaskownicą
Jest to najprostszy sposób korzystania z piaskownicy i zalecana metoda, gdy chcesz umieścić w niej cały plik binarny, do którego nie masz kodu źródłowego. Jest to też najbezpieczniejszy sposób korzystania z piaskownicy, ponieważ nie ma niezabezpieczonej inicjalizacji, która mogłaby mieć negatywne skutki.
W poniższym fragmencie kodu definiujemy ścieżkę do pliku binarnego, który ma być uruchamiany w piaskownicy, oraz argumenty, które musimy przekazać do wywołania systemowego execve. Jak widać w pliku nagłówkowym executor.h, nie określamy wartości dla envp
, a więc kopiujemy środowisko z procesu nadrzędnego. Pamiętaj, że pierwszy argument to zawsze nazwa programu do wykonania, a nasz fragment kodu nie definiuje żadnego innego argumentu.
Przykłady tej metody wykonawcy to: static i tool.
#include "sandboxed_api/sandbox2/executor.h"
std::string path = "path/to/binary";
std::vector<std::string> args = {path}; // args[0] will become the sandboxed
// process' argv[0], typically the
// path to the binary.
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
Metoda 2. Serwer rozwidlenia Sandbox2 – informowanie wykonawcy, kiedy ma działać w piaskownicy
Ta metoda zapewnia elastyczność, ponieważ podczas inicjowania nie jest objęta piaskownicą, a następnie możesz wybrać moment, w którym chcesz ją włączyć, wywołując funkcję ::sandbox2::Client::SandboxMeHere()
. Wymaga to zdefiniowania w kodzie momentu, w którym chcesz rozpocząć piaskownicę. Musi ona działać w jednym wątku (odpowiedź na pytanie „dlaczego?” znajdziesz w najczęstszych pytaniach).
W poniższym fragmencie kodu używamy tego samego kodu, który został opisany powyżej w sekcji Metoda 1. Aby jednak umożliwić programowi wykonywanie działań w sposób nieograniczony podczas inicjowania, wywołujemy funkcję set_enable_sandbox_before_exec(false)
.
#include "sandboxed_api/sandbox2/executor.h"
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
executor->set_enable_sandbox_before_exec(false);
Ponieważ wykonawca ma teraz wyłączoną piaskownicę, dopóki nie otrzyma powiadomienia od procesu podrzędnego, musimy utworzyć instancję ::sandbox2::Client
, skonfigurować komunikację między wykonawcą a procesem podrzędnym, a następnie powiadomić wykonawcę, że inicjowanie zostało zakończone i chcemy rozpocząć piaskownicę, wywołując sandbox2_client.SandboxMeHere()
.
// main() of sandboxee
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, false);
// Set-up the sandbox2::Client object, using a file descriptor (1023).
sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
sandbox2::Client sandbox2_client(&comms);
// Enable sandboxing from here.
sandbox2_client.SandboxMeHere();
…
Przykładem tej metody wykonawcy jest crc4, gdzie crc4bin.cc
to Sandboxee, który powiadamia wykonawcę (crc4sandbox.cc
), kiedy powinien wejść do piaskownicy.
Metoda 3. Niestandardowy serwer rozwidlenia – przygotuj plik binarny, poczekaj na żądania rozwidlenia i utwórz piaskownicę
Ten tryb umożliwia uruchomienie pliku binarnego, przygotowanie go do piaskownicy i udostępnienie go wykonawcy w określonym momencie cyklu życia pliku binarnego.
Wykonawca wyśle do Twojego pliku binarnego żądanie rozwidlenia, które fork()
(za pomocą ::sandbox2::ForkingClient::WaitAndFork()
). Nowo utworzony proces będzie gotowy do umieszczenia w piaskownicy za pomocą ::sandbox2::Client::SandboxMeHere()
.
#include "sandboxed_api/sandbox2/executor.h"
// Start the custom ForkServer
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto fork_executor = absl::make_unique<sandbox2::Executor>(path, args);
fork_executor->StartForkServer();
// Initialize Executor with Comms channel to the ForkServer
auto executor = absl::make_unique<sandbox2::Executor>(
fork_executor->ipc()->GetComms());
Pamiętaj, że ten tryb jest dość skomplikowany i ma zastosowanie tylko w kilku konkretnych przypadkach, np. gdy masz ograniczone wymagania dotyczące pamięci. Będziesz korzystać z COW, ale nie będziesz mieć prawdziwego ASLR. Innym typowym przykładem użycia jest sytuacja, w której piaskownica ma długą, wymagającą dużego obciążenia procesora inicjację, którą można uruchomić przed przetworzeniem niezaufanych danych.
Przykład tej metody wykonawcy znajdziesz w artykule custom_fork.