5. Comunicazione con il sandboxee

Per impostazione predefinita, l'esecutore può comunicare con Sandboxee tramite i descrittori di file. 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 probabilmente hai bisogno di una logica di comunicazione più complessa tra l'executor 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 di file

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

  • Utilizza MapFd() per mappare i descrittori di file dall'executor a Sandboxee. Può essere utilizzato per condividere un file aperto dall'executor per l'utilizzo nel Sandboxee. Un esempio di utilizzo è disponibile in static.

    // The executor opened /proc/version and passes it to the sandboxee as stdin
    executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
    
  • UtilizzaReceiveFd() per creare un endpoint socketpair. Può essere utilizzato per leggere l'output standard o gli errori standard del sandboxee. Un esempio di utilizzo è visibile nello strumento.

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

Utilizzo dell'API comms

Sandbox2 fornisce una comoda API comms. Questo è un modo semplice e facile per condividere numeri interi, stringhe o buffer di byte tra l'executor 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 disponibile l'oggetto comms, i dati possono essere inviati a Sandboxee utilizzando una delle funzioni della famiglia Send*. Puoi trovare un esempio di utilizzo dell'API comms nell'esempio crc4. Lo snippet di codice di seguito mostra un estratto di questo esempio. L'esecutore testamentario 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 dal sandboxee, utilizza una delle funzioni Recv*. Lo snippet di codice riportato di seguito è 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 */
}

Condividere dati con i buffer

Un'altra funzionalità di condivisione dei dati è l'utilizzo dell'API buffer per condividere grandi quantità di dati ed evitare costose copie inviate avanti e indietro tra l'executor e Sandboxee.

L'executor crea un buffer, in base alle dimensioni e ai dati da trasferire, oppure direttamente da un descrittore di file e lo passa a Sandboxee utilizzando comms->SendFD() nell'executor e comms->RecvFD() in Sandboxee.

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());

Dal lato del sandboxee, devi anche creare un oggetto buffer e leggere i dati dal descrittore di file inviato dall'executor:

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