2. Crea un criterio nella sandbox

Una volta ottenuto un esecutore, è probabile che tu voglia definire un criterio della sandbox per Sandboxee. In caso contrario, Sandboxee è protetto solo dal criterio di chiamata di sistema predefinito.

Con il criterio della sandbox, l'obiettivo è limitare le chiamate di sistema e gli argomenti che può creare Sandboxee, nonché i file a cui può accedere. Devi avere una comprensione dettagliata delle chiamate di sistema richieste dal codice che prevedi di utilizzare la sandbox. Un modo per osservare le chiamate di sistema consiste nell'eseguire il codice con la strace dello strumento a riga di comando di Linux.

Una volta creato l'elenco delle chiamate di sistema, puoi utilizzare PolicyBuilder per definire il criterio. PolicyBuilder include molte funzioni helper e di pratico utilizzo che consentono molte operazioni comuni. Il seguente elenco rappresenta solo un breve estratto delle funzioni disponibili:

  • Inserisci nella lista consentita qualsiasi chiamata di sistema per l'avvio del processo:
    • AllowStaticStartup();
    • AllowDynamicStartup();
  • Inserisci nella lista consentita qualsiasi chiamata di sistema aperta/lettura/scrittura*:
    • AllowOpen();
    • AllowRead();
    • AllowWrite();
  • Inserisci nella lista consentita qualsiasi chiamata di sistema correlata a uscite/accesso/stato:
    • AllowExit();
    • AllowStat();
    • AllowAccess();
  • Inserisci nella lista consentita eventuali chiamate di sistema relative a sonno e orario:
    • AllowTime();
    • AllowSleep();

Queste funzioni di convenienza aggiungono qualsiasi chiamata di sistema pertinente alla lista consentita. Questo presenta il vantaggio che lo stesso criterio può essere utilizzato in diverse architetture in cui alcune chiamate di sistema non sono disponibili (ad es. ARM64 non ha una chiamata di sistema OPEN), ma con il rischio per la sicurezza minore di attivare più sysall del necessario. Ad esempio, allowOpen() consente a Sandboxee di chiamare qualsiasi chiamata di sistema aperta correlata. Se vuoi inserire nella lista consentita una sola chiamata di sistema specifica, puoi usare AllowSyscall(); per consentire più chiamate di sistema contemporaneamente, puoi usare AllowSyscalls().

Finora il criterio controlla solo l'identificatore di chiamata di sistema. Se hai bisogno di rafforzare ulteriormente il criterio e vuoi definire un criterio in cui sia consentita solo una chiamata di sistema con determinati argomenti, devi usare AddPolicyOnSyscall() o AddPolicyOnSyscalls(). Queste funzioni non prendono solo l'ID syscall come argomento, ma anche un filtro seccomp-bpf non elaborato che utilizza le macro bpf helper del kernel Linux. Per ulteriori informazioni su BPF, consulta la documentazione del kernel. Se ti capita di scrivere codice BPF ripetitivo che ritieni debba avere un wrapper per l'usabilità, non esitare a presentare una richiesta di funzionalità.

Oltre alle funzioni correlate a syscall, PolicyBuilder offre anche una serie di funzioni relative al file system, ad esempio AddFile() o AddDirectory() per il binding e il montaggio di un file/directory nella sandbox. L'helper AddTmpfs() può essere utilizzato per aggiungere uno spazio di archiviazione temporaneo per i file all'interno della sandbox.

Una funzione particolarmente utile è AddLibrariesForBinary(), che aggiunge le librerie e il linker richiesti da un programma binario.

Purtroppo, trovare nuove chiamate di sistema nella lista consentita richiede ancora un po' di lavoro manuale. Crea un criterio con chiamate di sistema note alle esigenze del programma binario ed eseguilo con un carico di lavoro comune. Se viene attivata una violazione, aggiungi la chiamata di sistema alla lista consentita e ripeti la procedura. Se riscontri una violazione che ritieni possa essere rischiosa da inserire nella lista consentita e il programma gestisce gli errori normalmente, puoi provare a fare in modo che restituisca un errore invece con BlockSyscallWithErrno().

#include "sandboxed_api/sandbox2/policy.h"
#include "sandboxed_api/sandbox2/policybuilder.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h"

std::unique_ptr<sandbox2::Policy> CreatePolicy() {
  return sandbox2::PolicyBuilder()
    .AllowSyscall(__NR_read)  // See also AllowRead()
    .AllowTime()              // Allow time, gettimeofday and clock_gettime
    .AddPolicyOnSyscall(__NR_write, {
        ARG(0),        // fd is the first argument of write (argument #0)
        JEQ(1, ALLOW), // allow write only on fd 1
        KILL,          // kill if not fd 1
    })
    .AddPolicyOnSyscall(__NR_mprotect, {
        ARG_32(2), // prot is a 32-bit wide argument, so it's OK to use *_32
                   // macro here
        JNE32(PROT_READ | PROT_WRITE, KILL), // prot must be the RW, otherwise
                                             // kill the process
        ARG(1), // len is a 64-bit argument
        JNE(0x1000, KILL),  // Allow single page syscalls only, otherwise kill
                            // the process
        ALLOW,              // Allow for the syscall to proceed, if prot and
                            // size match
    })
    // Allow the openat() syscall but always return "not found".
    .BlockSyscallWithErrno(__NR_openat, ENOENT)
    .BuildOrDie();
}