1. Sandbox-Ausführungsmethode auswählen

Sandboxing beginnt mit einem Executor (siehe Sandbox-Executor), der für die Ausführung des Sandboxee verantwortlich ist. Die Headerdatei executor.h enthält die dafür erforderliche API. Die API ist sehr flexibel und Sie können auswählen, was für Ihren Anwendungsfall am besten geeignet ist. In den folgenden Abschnitten werden die drei verschiedenen Methoden beschrieben, aus denen Sie auswählen können.

Methode 1: Standalone – Binärdatei mit bereits aktivierter Sandbox ausführen

Dies ist die einfachste Methode für die Verwendung von Sandboxing und wird empfohlen, wenn Sie ein gesamtes Binärprogramm ohne Quellcode in einer Sandbox ausführen möchten. Außerdem ist es die sicherste Methode, Sandboxing zu verwenden, da es keine Initialisierung ohne Sandbox gibt, die sich negativ auswirken könnte.

Im folgenden Code-Snippet definieren wir den Pfad des Binärprogramms, das in der Sandbox ausgeführt werden soll, und die Argumente, die wir an einen execve-Systemaufruf übergeben müssen. Wie Sie in der Headerdatei executor.h sehen können, geben wir keinen Wert für envp an und kopieren daher die Umgebung aus dem übergeordneten Prozess. Das erste Argument ist immer der Name des auszuführenden Programms. In unserem Snippet wird kein anderes Argument definiert.

Beispiele für diese Ausführungsmethode sind static und 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);

Methode 2: Sandbox2-Forkserver – Executor mitteilen, wann er in der Sandbox ausgeführt werden soll

Diese Methode bietet die Flexibilität, während der Initialisierung nicht in der Sandbox zu sein und dann durch Aufrufen von ::sandbox2::Client::SandboxMeHere() zu entscheiden, wann die Sandbox aktiviert werden soll. Sie müssen im Code definieren können, wann die Sandbox gestartet werden soll, und sie muss Single-Threaded sein. Weitere Informationen

Im folgenden Code-Snippet verwenden wir denselben Code wie oben in Methode 1 beschrieben. Damit das Programm jedoch während der Initialisierung ohne Sandbox ausgeführt werden kann, rufen wir set_enable_sandbox_before_exec(false) auf.

#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);

Da die Ausführung jetzt eine deaktivierte Sandbox hat, bis sie vom Sandboxee benachrichtigt wird, müssen wir eine ::sandbox2::Client-Instanz erstellen, die Kommunikation zwischen der Ausführung und dem Sandboxee einrichten und dann die Ausführung benachrichtigen, dass die Initialisierung abgeschlossen ist und wir jetzt mit dem Sandboxing beginnen möchten, indem wir sandbox2_client.SandboxMeHere() aufrufen.

// 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();
  

Ein Beispiel für diese Executor-Methode ist crc4. Dabei ist crc4bin.cc der Sandboxee, der den Executor (crc4sandbox.cc) benachrichtigt, wenn er in die Sandbox eintreten soll.

Methode 3: Benutzerdefinierter Forkserver – Binärdatei vorbereiten, auf Fork-Anfragen warten und selbst sandboxing durchführen

In diesem Modus können Sie ein Binärprogramm starten, es für das Sandboxing vorbereiten und es zu einem bestimmten Zeitpunkt im Lebenszyklus des Binärprogramms für den Executor verfügbar machen.

Der Executor sendet eine Fork-Anfrage an Ihr Binärprogramm, das fork() (über ::sandbox2::ForkingClient::WaitAndFork()) wird. Der neu erstellte Prozess kann dann mit ::sandbox2::Client::SandboxMeHere() in einer Sandbox ausgeführt werden.

#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());

Dieser Modus ist recht kompliziert und nur in wenigen spezifischen Fällen anwendbar, z. B. wenn Sie strenge Anforderungen an den Arbeitsspeicher haben. Sie profitieren von COW, haben aber den Nachteil, dass es kein echtes ASLR gibt. Ein weiteres typisches Anwendungsbeispiel ist, wenn das Sandboxee eine lange, CPU-intensive Initialisierung hat, die vor der Verarbeitung der nicht vertrauenswürdigen Daten ausgeführt werden kann.

Ein Beispiel für diese Executor-Methode finden Sie unter custom_fork.