5. Comunicare con il Sandboxee

Per impostazione predefinita, l'esecutore può comunicare con Sandboxee tramite descrittori di file. Questo potrebbe essere tutto ciò di cui hai bisogno, ad esempio se vuoi solo condividere un file con Sandboxee o leggere l'output standard di Sandboxee.

Tuttavia, è molto probabile che tu abbia bisogno di una logica di comunicazione più complessa tra l'esecutore e Sandboxee. L'API comms (vedi il file di intestazione comms.h) può essere utilizzata per inviare numeri interi, stringhe, buffer di byte, protobuf o descrittori di file.

Condivisione dei descrittori dei file

Con l'API Inter-Process Communication (vedi ipc.h), puoi utilizzare MapFd() o ReceiveFd():

  • Utilizza MapFd() per mappare i descrittori dei file dall'esecutore a Sandboxee. Può essere utilizzato per condividere un file aperto dall'esecutore per utilizzarlo in Sandboxee. Un esempio di utilizzo può essere visto in statico.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • Utilizza ReceiveFd() per creare un endpoint della coppia di socket. Può essere utilizzato per leggere l'output standard o gli errori standard di Sandboxee. Un esempio di utilizzo è rappresentato dallo strumento.

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

Utilizzo dell'API comms

Sandbox2 offre una pratica API per le comunicazioni. Questo è un modo semplice e semplice per condividere numeri interi, stringhe o buffer di byte tra l'esecutore e Sandboxee. Di seguito sono riportati alcuni snippet di codice che puoi trovare nell'esempio crc4.

Per iniziare a utilizzare l'API comms, devi prima ottenere l'oggetto comms dall'oggetto Sandbox2:

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

Una volta che l'oggetto comms è disponibile, i dati possono essere inviati a Sandboxee utilizzando una delle funzioni della famiglia di funzioni Send*. Puoi trovare un esempio di utilizzo dell'API comms nell'esempio crc4. Lo snippet di codice riportato di seguito mostra un estratto di tale esempio. L'esecutore invia un unsigned char buf[size] con SendBytes(buf, size):

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

Per ricevere dati da Sandboxee, utilizza una delle funzioni di Recv*. Lo snippet di codice seguente è un estratto dell'esempio crc4. L'esecutore riceve il checksum in un numero intero senza segno a 32 bit: uint32_t crc4;

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

Condivisione di dati con i buffer

Un'altra funzionalità di condivisione dei dati consiste nell'utilizzare l'API buffer per condividere grandi quantità di dati ed evitare copie costose che vengono scambiate avanti e indietro tra l'esecutore e Sandboxee.

L'esecutore crea un buffer in base alle dimensioni e ai dati da trasmettere o direttamente da un descrittore di file e lo passa a Sandboxee utilizzando comms->SendFD() nell'esecutore e comms->RecvFD() in Sandbox.

Nello snippet di codice riportato di seguito puoi vedere il lato dell'esecutore. La sandbox viene eseguita in modo asincrono e condivide i dati tramite un buffer con 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());

Sul lato di Sandboxee, devi anche creare un oggetto buffer e leggere i dati dal descrittore del file inviato dall'esecutore:

// 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 */