5. Communiquer avec les Sandboxee

Par défaut, l'exécuteur peut communiquer avec le Sandboxee via des descripteurs de fichier. C'est tout ce dont vous avez besoin, par exemple si vous voulez simplement partager un fichier avec le Sandboxee ou lire la sortie standard du Sandboxee.

Cependant, vous aurez probablement besoin d'une logique de communication plus complexe entre l'exécuteur et le bac à sable. Vous pouvez utiliser l'API comms (voir le fichier d'en-tête comms.h) pour envoyer des entiers, des chaînes, des tampons d'octets, des tampons de protocole ou des descripteurs de fichier.

Partager des descripteurs de fichier

Avec l'API Inter-Process Communication (voir ipc.h), vous pouvez utiliser MapFd() ou ReceiveFd():

  • Utilisez MapFd() pour mapper les descripteurs de fichier de l'exécuteur à l'environnement Sandboxee. Cela peut être utilisé pour partager un fichier ouvert à partir de l'exécuteur et l'utiliser dans Sandboxee. Un exemple d'utilisation est visible dans l'élément static.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • Utilisez ReceiveFd() pour créer un point de terminaison de paire de sockets. Cela permet de lire la sortie ou les erreurs standards du Sandboxee. Vous trouverez un exemple d'utilisation dans l'outil.

    // The executor receives a file descriptor of the sandboxee stdout
    int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
    

Utiliser l'API comms

Sandbox2 fournit une API de communication pratique. C'est un moyen simple et facile de partager des entiers, des chaînes ou des tampons d'octets entre l'exécuteur et Sandboxee. Vous trouverez ci-dessous quelques extraits de code disponibles dans l'exemple crc4.

Pour faire vos premiers pas avec l'API comms, vous devez d'abord récupérer l'objet comms à partir de l'objet Sandbox2:

sandbox2::Comms* comms = s2.comms();

Une fois que l'objet comms est disponible, les données peuvent être envoyées au Sandboxee à l'aide de l'une des fonctions de la famille Send*. Vous trouverez un exemple d'utilisation de l'API comms dans l'exemple crc4. L'extrait de code ci-dessous reprend un extrait de cet exemple. L'exécuteur envoie un unsigned char buf[size] avec SendBytes(buf, size):

if (!(comms->SendBytes(static_cast<const uint8_t*>(buf), sz))) {
  /* handle error */
}

Pour recevoir des données de Sandboxee, utilisez l'une des fonctions Recv*. L'extrait de code ci-dessous est tiré de l'exemple crc4. L'exécuteur reçoit la somme de contrôle sous la forme d'un entier non signé de 32 bits : uint32_t crc4;

if (!(comms->RecvUint32(&crc4))) {
  /* handle error */
}

Partager des données avec des tampons

Une autre fonctionnalité de partage de données consiste à utiliser l'API tampon pour partager de grandes quantités de données et éviter les échanges de copies coûteuses entre l'exécuteur et le bac à sable.

L'exécuteur crée un tampon, soit par taille et par données à transmettre, soit directement à partir d'un descripteur de fichier, puis le transmet à Sandboxee à l'aide de comms->SendFD() dans l'exécuteur et de comms->RecvFD() dans Sandboxee.

Dans l'extrait de code ci-dessous, vous pouvez voir du côté de l'exécuteur. Le bac à sable s'exécute de manière asynchrone et partage des données avec le Sandboxee via un tampon:

// start the sandbox asynchronously
s2.RunAsync();

// instantiate the comms object
sandbox2::Comms* comms = s2.comms();

// random buffer data we want to send
constexpr unsigned char buffer_data[] = /* random data */;
constexpr unsigned int buffer_dataLen = 34;

// create sandbox2 buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
     sandbox2::Buffer::CreateWithSize(1ULL << 20 /* 1Mib */);
std::unique_ptr<sandbox2::Buffer> buffer_ptr = std::move(buffer).value();

// point to the sandbox2 buffer and fill with data
uint8_t* buf = buffer_ptr‑>data();
memcpy(buf, buffer_data, buffer_data_len);

// send the data to the sandboxee
comms‑>SendFd(buffer_ptr‑>fd());

Du côté de Sandboxee, vous devez également créer un objet tampon et lire les données du descripteur de fichier envoyé par l'exécuteur:

// establish the communication with the executor
int fd;
comms.RecvFD(&fd);

// create the buffer
absl::StatusOr<std::unique_ptr<sandbox2::Buffer>> buffer =
     sandbox2::Buffer::createFromFd(fd);

// get the data
auto buffer_ptr = std::move(buffer).value();
uint8_t* buf = buffer_ptr‑>data();

/* work with the buf object */