5. Communiquer avec le Sandboxee
Par défaut, l'exécuteur peut communiquer avec le Sandboxee via des descripteurs de fichiers. Cela peut suffire, par exemple si vous souhaitez simplement partager un fichier avec le bac à sable ou lire la sortie standard du bac à sable.
Toutefois, vous aurez probablement besoin d'une logique de communication plus complexe entre l'exécuteur et Sandboxee. L'API comms (voir le fichier d'en-tête comms.h) peut être utilisée pour envoyer des entiers, des chaînes, des tampons d'octets, des fichiers protobuf ou des descripteurs de fichier.
Partager des descripteurs de fichiers
À l'aide de l'API de communication interprocessus (voir ipc.h), vous pouvez utiliser MapFd()
ou ReceiveFd()
:
Utilisez
MapFd()
pour mapper les descripteurs de fichiers de l'exécuteur à Sandboxee. Permet de partager un fichier ouvert à partir de l'exécuteur pour l'utiliser dans Sandboxee. Vous trouverez un exemple d'utilisation dans 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 socketpair. Cela peut être utilisé pour lire la sortie standard ou les erreurs standards de Sandboxee. Un exemple d'utilisation est disponible dans l'outil.// The executor receives a file descriptor of the sandboxee stdout int recv_fd1 = executor->ipc())->ReceiveFd(STDOUT_FILENO);
Utiliser l'API Communications
Sandbox2 fournit une API de communication pratique. Il s'agit d'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 dans l'exemple crc4.
Pour commencer à utiliser l'API Comms, vous devez d'abord obtenir l'objet Comms à partir de l'objet Sandbox2 :
sandbox2::Comms* comms = s2.comms();
Une fois l'objet de communication disponible, les données peuvent être envoyées à Sandboxee à l'aide de l'une des fonctions de la famille Send*. Vous trouverez un exemple d'utilisation de l'API Communications dans l'exemple crc4. L'extrait de code ci-dessous montre 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 un extrait 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 de mémoire tampon pour partager de grandes quantités de données et éviter les copies coûteuses qui sont envoyées dans les deux sens entre l'exécuteur et Sandboxee.
L'exécuteur crée un tampon, soit par taille et données à transmettre, soit directement à partir d'un descripteur de fichier, et 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 le côté de l'exécuteur. Le bac à sable s'exécute de manière asynchrone et partage les données via un tampon avec Sandboxee :
// 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 */