1. Escolher um método de executor de sandbox

O sandbox começa com um executor (consulte Sandbox Executor), que é responsável por executar o Sandboxee. O arquivo de cabeçalho executor.h contém a API necessária para essa finalidade. A API é muito flexível e permite que você escolha o que funciona melhor para seu caso de uso. As seções a seguir descrevem os três métodos diferentes que você pode escolher.

Método 1: independente – execute um binário com o sandboxing já ativado

Essa é a maneira mais simples de usar o sandbox e é o método recomendado quando você quer colocar em sandbox um binário inteiro sem código-fonte. Também é a maneira mais segura de usar o sandbox, já que não há inicialização sem sandbox que possa ter efeitos adversos.

No snippet de código a seguir, definimos o caminho do binário a ser isolado em sandbox e os argumentos que precisamos transmitir para uma syscall execve. Como você pode ver no arquivo de cabeçalho executor.h, não especificamos um valor para envp e, portanto, copiamos o ambiente do processo principal. O primeiro argumento é sempre o nome do programa a ser executado, e nosso snippet não define nenhum outro argumento.

Exemplos desse método de executor são: 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);

Método 2: Forkserver do Sandbox2 – informar ao executor quando usar o sandbox

Esse método oferece a flexibilidade de não estar em sandbox durante a inicialização e, em seguida, escolher quando entrar no sandbox chamando ::sandbox2::Client::SandboxMeHere(). É necessário definir no código quando você quer iniciar o isolamento em sandbox, e ele precisa ser de uma única linha de execução. Leia o motivo no FAQ.

No snippet de código a seguir, usamos o mesmo código descrito no Método 1 acima. No entanto, para permitir que o programa seja executado de maneira não isolada durante a inicialização, chamamos 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);

Como o executor agora tem um sandbox desativado até ser notificado pelo Sandboxee, precisamos criar uma instância ::sandbox2::Client, configurar a comunicação entre o executor e o Sandboxee e notificar o executor que a inicialização foi concluída e queremos iniciar o sandbox agora chamando 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();
  

Um exemplo desse método de executor é crc4, em que crc4bin.cc é o Sandboxee e notifica o executor (crc4sandbox.cc) quando ele precisa entrar na sandbox.

Método 3: Forkserver personalizado – prepare um binário, aguarde solicitações de fork e crie um sandbox por conta própria

Esse modo permite iniciar um binário, prepará-lo para o sandbox e, em um momento específico do ciclo de vida do binário, disponibilizá-lo para o executor.

O executor vai enviar uma solicitação de fork para seu binário, que vai fork() (via ::sandbox2::ForkingClient::WaitAndFork()). O processo recém-criado estará pronto para ser isolado em sandbox com ::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());

Esse modo é bastante complicado e aplicável apenas em alguns casos específicos, por exemplo, quando você tem requisitos de memória restritos. Você vai se beneficiar do COW, mas terá a desvantagem de não ter ASLR real. Outro exemplo de uso típico seria quando o Sandboxee tem uma inicialização longa e com uso intenso da CPU que pode ser executada antes do processamento dos dados não confiáveis.

Para ver um exemplo desse método de executor, consulte custom_fork.