1. Scegliere un metodo di esecuzione della sandbox

Il sandboxing inizia con un executor (vedi Sandbox Executor), che è responsabile dell'esecuzione di Sandboxee. Il file di intestazione executor.h contiene l'API necessaria a questo scopo. L'API è molto flessibile e ti consente di scegliere la soluzione migliore per il tuo caso d'uso. Le sezioni seguenti descrivono i tre metodi diversi tra cui puoi scegliere.

Metodo 1: autonomo: esegui un binario con il sandboxing già abilitato

Questo è il modo più semplice per utilizzare il sandboxing ed è il metodo consigliato quando vuoi eseguire il sandboxing di un intero binario per il quale non disponi del codice sorgente. È anche il modo più sicuro per utilizzare il sandboxing, in quanto non esiste un'inizializzazione non sottoposta a sandbox che potrebbe avere effetti negativi.

Nel seguente snippet di codice, definiamo il percorso del file binario da inserire nel sandbox e gli argomenti da passare a una chiamata di sistema execve. Come puoi vedere nel file di intestazione executor.h, non specifichiamo un valore per envp e quindi copiamo l'ambiente dal processo padre. Ricorda che il primo argomento è sempre il nome del programma da eseguire e il nostro snippet non definisce altri argomenti.

Esempi di questo metodo di esecuzione sono: static e 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);

Metodo 2: Forkserver Sandbox2: indica all'executor quando deve essere sottoposto a sandbox

Questo metodo offre la flessibilità di non essere sottoposto a sandbox durante l'inizializzazione e di scegliere quando attivare la sandbox chiamando ::sandbox2::Client::SandboxMeHere(). Richiede di poter definire nel codice quando vuoi iniziare il sandboxing e deve essere single-thread (leggi il motivo nelle domande frequenti).

Nel seguente snippet di codice, utilizziamo lo stesso codice descritto nel Metodo 1 sopra. Tuttavia, per consentire l'esecuzione del programma in modo non in sandbox durante l'inizializzazione, chiamiamo 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);

Poiché l'executor ha ora una sandbox disattivata fino a quando non riceve una notifica da Sandboxee, dobbiamo creare un'istanza ::sandbox2::Client, configurare la comunicazione tra l'executor e Sandboxee e quindi notificare all'executor che l'inizializzazione è terminata e che vogliamo iniziare ora il sandboxing chiamando 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();
  

Un esempio di questo metodo di esecuzione è crc4, dove crc4bin.cc è Sandboxee e notifica all'executor (crc4sandbox.cc) quando deve entrare nel sandbox.

Metodo 3: Custom Forkserver: prepara un binario, attendi le richieste di fork e crea un sandbox autonomamente

Questa modalità ti consente di avviare un binario, prepararlo per il sandboxing e, in un momento specifico del ciclo di vita del binario, renderlo disponibile all'executor.

L'esecutore invierà una richiesta di fork al tuo binario, che fork() (tramite ::sandbox2::ForkingClient::WaitAndFork()). Il processo appena creato sarà pronto per essere sottoposto al sandboxing con ::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());

Tieni presente che questa modalità è piuttosto complicata e applicabile solo in alcuni casi specifici, ad esempio quando hai requisiti di memoria rigorosi. Beneficerai del COW, ma avrai lo svantaggio di non avere un ASLR reale. Un altro esempio di utilizzo tipico si verifica quando Sandboxee ha un'inizializzazione lunga e che richiede un utilizzo elevato della CPU che può essere eseguita prima dell'elaborazione dei dati non attendibili.

Per un esempio di questo metodo di esecuzione, vedi custom_fork.