Skip to content

Commit

Permalink
(net/a12) sandbox directory server
Browse files Browse the repository at this point in the history
This makes the directory server end processing much more careful.
It now splits out processing into a worker process that communicates
with the parent over shmif.

This lets all parsing, processing and authentication run with zero
direct file system access (unveil(NULL)) and with only basic stdio
for shmif-event+fdpassing to work.

Another step of paranoia would be to also SIGSUSP the worker while
deriving the session key. This would seal of any attempt for a
compromised worker thread to try and extract bits of the parent
private key via some possible microarchitectural side-channel,
though that ia a bit over the top for now.

The other purpose for this setup is to allow each worker some minor
form of IPC synchronisation - mainly for (if permitted) registering
new sources, sinks, directories and appl updates, with notifications.

When that is working the final two stages would be to a. add a
resource namespace for the appl to be able to load, store and glob
from a shared appl-specific space on the server, as well as allow
appls to communicate with other authenticated users on the same
directory running the same appl.
  • Loading branch information
letoram committed Sep 27, 2023
1 parent 2b45b6e commit 8578606
Show file tree
Hide file tree
Showing 17 changed files with 1,483 additions and 396 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -37,6 +37,7 @@
* add interactive accept/reject/trust for arcan-net use
* reserve 'outbound' domain for interactively added outbound keys
* enforce domain separation for allowed pubkeys
* server in directory mode now splits out into sandboxed worker processes

## Terminal
* SGR reset fix, add CNL / CPL
Expand Down
43 changes: 21 additions & 22 deletions src/a12/a12.c
Expand Up @@ -147,10 +147,9 @@ struct appl_meta* a12int_get_directory(struct a12_state* S, uint64_t* clk)
void a12int_set_directory(struct a12_state* S, struct appl_meta* M)
{
struct appl_meta* C = S->directory;

while (C){
struct appl_meta* old = C;
if (C->handle)
fclose(C->handle);
free(C->buf);

C = C->next;
Expand Down Expand Up @@ -1301,6 +1300,13 @@ static struct blob_out** alloc_attach_blob(struct a12_state* S)
return parent;
}

void a12_set_session(
struct pk_response* dst, uint8_t pubk[static 32], uint8_t privk[static 32])
{
x25519_public_key(privk, dst->key_pub);
x25519_shared_secret(dst->key_session, privk, pubk);
}

/*
* Simplified form of enqueue bstream below, we already have the buffer
* in memory so just build a different blob-out node with a copy
Expand Down Expand Up @@ -1488,8 +1494,12 @@ static bool authdec_buffer(const char* src, struct a12_state* S, size_t block_sz
static void hello_auth_server_hello(struct a12_state* S)
{
uint8_t pubk[32];
uint8_t remote_pubk[32];

uint8_t nonce[8];
int cfl = S->decode[20];

memcpy(remote_pubk, &S->decode[21], 32);
a12int_trace(A12_TRACE_CRYPTO, "state=complete:method=%d", cfl);

/* here is a spot for having more authentication modes if needed (version bump) */
Expand All @@ -1511,7 +1521,7 @@ static void hello_auth_server_hello(struct a12_state* S)
arcan_random(nonce, 8);
send_hello_packet(S, HELLO_MODE_EPHEMPK, pubk, nonce);

x25519_shared_secret((uint8_t*)S->opts->secret, ek, &S->decode[21]);
x25519_shared_secret((uint8_t*)S->opts->secret, ek, remote_pubk);
trace_crypto_key(S->server, "ephem_pub", pubk, 32);
update_keymaterial(S, S->opts->secret, 32, nonce);
S->authentic = AUTH_EPHEMERAL_PK;
Expand All @@ -1528,32 +1538,32 @@ static void hello_auth_server_hello(struct a12_state* S)

/* the lookup function returns the key that should be used in the reply
* and to calculate the shared secret */
trace_crypto_key(S->server, "state=client_pk", &S->decode[21], 32);
struct pk_response res = S->opts->pk_lookup(&S->decode[21]);
trace_crypto_key(S->server, "state=client_pk", remote_pubk, 32);
struct pk_response res = S->opts->pk_lookup(remote_pubk);
if (!res.authentic){
a12int_trace(A12_TRACE_CRYPTO, "state=eperm:kind=x25519-pk-fail");
fail_state(S);
return;
}

memcpy((uint8_t*)S->opts->secret, res.key_session, 32);
memcpy(pubk, res.key_pub, 32);

/* hello packet here will still use the keystate from the process_srvfirst
* which will use the client provided nonce, KDF on preshare-pw */
x25519_public_key(res.key, pubk);
arcan_random(nonce, 8);
send_hello_packet(S, HELLO_MODE_REALPK, pubk, nonce);
memcpy(S->keys.remote_pub, &S->decode[21], 32);
trace_crypto_key(S->server, "state=client_pk_ok:respond_pk", pubk, 32);

/* now we can switch keys, note that the new nonce applies for both enc and dec
* states regardless of the nonce the client provided in the first message */
x25519_shared_secret((uint8_t*)S->opts->secret, res.key, &S->decode[21]);
trace_crypto_key(S->server, "state=server_ssecret", (uint8_t*)S->opts->secret, 32);
update_keymaterial(S, S->opts->secret, 32, nonce);

/* and done, mark latched so a12_unpack saves buffer and returns */
S->authentic = AUTH_FULL_PK;
S->auth_latched = true;
S->state_access = res.state_access;

if (S->on_auth)
S->on_auth(S, S->auth_tag);
Expand Down Expand Up @@ -1814,7 +1824,6 @@ static void add_dirent(struct a12_state* S)
memcpy(new->applname, &S->decode[36], 18);
memcpy(new->short_descr, &S->decode[55], 69);
new->update_ts = arcan_timemillis();
new->remote = true;

if (!S->directory){
S->directory = new;
Expand All @@ -1826,7 +1835,7 @@ static void add_dirent(struct a12_state* S)

while (cur){
/* override / update? */
if (cur->identifier == new->identifier && cur->remote){
if (cur->identifier == new->identifier){
new->next = cur->next;
if (prev)
prev->next = new;
Expand Down Expand Up @@ -2042,7 +2051,7 @@ static void process_blob(struct a12_state* S)

if (!buf){
a12int_trace(A12_TRACE_ALLOC,
"kind=zstd_buffer_fail:size=%zu", content_sz);
"kind=zstd_buffer_fail:size=%zu", (size_t) content_sz);
a12_stream_cancel(S, S->in_channel);
reset_state(S);
return;
Expand Down Expand Up @@ -2651,7 +2660,7 @@ static bool flush_compressed(
if (node->left){
a12int_trace(A12_TRACE_BTRANSFER, "kind=compressed_block:"
"stream=%"PRIu64":ch=%d:size=%zu:base=%zu:left=%zu",
(size_t)node->streamid, (int) node->chid, out, nts, node->left
(uint64_t)node->streamid, (int) node->chid, out, nts, node->left
);
node->left -= nts;
return node->left != 0;
Expand Down Expand Up @@ -3178,13 +3187,3 @@ int a12_remote_mode(struct a12_state* S)
{
return S->remote_mode;
}

int a12_access_state(
struct a12_state* S, const char* id, const char* mode, size_t sz)
{
if (!S || !S->state_access)
return -1;

return S->state_access(S->keys.remote_pub, id, sz, mode);
}

27 changes: 15 additions & 12 deletions src/a12/a12.h
Expand Up @@ -42,14 +42,14 @@ struct a12_state;
* and client- side or the communication will fail regardless of key validity.
*
* return the private key to use with the public key received (server only)
* OR (migration to better process- separation friendly interface), got_session
* set and key_pub + derived session will be provided rather than the key_priv.
*/
struct pk_response {
bool authentic;

/* For server-side, reply with the key that should be used with the specific
* client if there is differentiation, for client-side, the private key in the
* structure to a12_client will be used */
uint8_t key[32];
uint8_t key_pub[32];
uint8_t key_session[32];

/* If state store is provided / permitted for the key, return a lookup function
* for creating or reading named resources from it */
Expand All @@ -60,7 +60,9 @@ struct pk_response {
struct a12_context_options {
/* Provide to enable asymetric key authentication, set valid in the return to
* allow the key, otherwise the session may be continued for a random number of
* time or bytes before being terminated. */
* time or bytes before being terminated. If want_session is requested, the
* lookup function, if it is able to (legacy) should set got_session in the
* reply and calculate the x25519v shared secret itself. */
struct pk_response (*pk_lookup)(uint8_t pub[static 32]);

/* Client only, provide the private key to use with the connection. All [0]
Expand Down Expand Up @@ -127,6 +129,14 @@ struct a12_state* a12_server(struct a12_context_options*);
bool
a12_free(struct a12_state*);

/*
* Used by the canonical key lookup function that provides the pk_response.
* Provide the remote public key in pubk, and the local private key in privk.
* Calculate the session key and local public key and set in [dst].
*/
void a12_set_session(
struct pk_response* dst, uint8_t pubk[static 32], uint8_t privk[static 32]);

/*
* Take an incoming byte buffer and append to the current state of
* the channel. Any received events will be pushed via the callback.
Expand Down Expand Up @@ -559,13 +569,6 @@ enum stream_cancel {
};
void a12_vstream_cancel(struct a12_state* S, uint8_t chid, int reason);

/*
* Return a descriptor to a state store for the a12 context+id pair
* with the desired access mode (r, w+) and (for w+) size boundary
*/
int a12_access_state(
struct a12_state* s, const char* id, const char* mode, size_t sz);

struct a12_iostat {
size_t b_in;
size_t b_out;
Expand Down
12 changes: 6 additions & 6 deletions src/a12/a12_int.h
Expand Up @@ -306,10 +306,6 @@ struct a12_state {
uint8_t remote_pub[32];
} keys;

/* pk_lookup provided state accessor */
int (*state_access)(const uint8_t pub[static 32],
const char* name, size_t sz, const char* mode);

/* client side needs to send the first packet with MAC+nonce, server side
* needs to interpret first packet with MAC+nonce */
bool server;
Expand Down Expand Up @@ -343,9 +339,13 @@ void a12int_append_out(
void a12int_step_vstream(struct a12_state* S, uint32_t id);

struct appl_meta {

/* These are used for local caching of contents, an update on the directory
* bound to the context or freeing the a12 state machine will free them. */
FILE* handle;
char* buf;
uint64_t buf_sz;

struct appl_meta* next;

uint16_t identifier;
Expand All @@ -355,7 +355,8 @@ struct appl_meta {

char applname[18];
char short_descr[69];
bool remote;

int role;
uint64_t update_ts;
};

Expand All @@ -371,5 +372,4 @@ struct appl_meta* a12int_get_directory(struct a12_state*, uint64_t* clk);
/* send the command to get a directory listing,
* results will be provided as BCHUNKHINT events */
void a12int_request_dirlist(struct a12_state*, bool);

#endif
1 change: 1 addition & 0 deletions src/a12/net/CMakeLists.txt
Expand Up @@ -8,6 +8,7 @@ set(SOURCES
net.c
dir_cl.c
dir_srv.c
dir_srv_worker.c
dir_supp.c
${ARCAN_SRC}/frameserver/util/anet_helper.c
${ARCAN_SRC}/frameserver/util/anet_keystore_naive.c
Expand Down
45 changes: 37 additions & 8 deletions src/a12/net/HACKING.md
Expand Up @@ -334,6 +334,7 @@ interleaving.
- [20] Mode : uint8
- [21+ 32] x25519 Pk : blob
- [54] Primary flow : uint8
- [55+ 16] Petname : UTF-8

The hello message contains key-material for normal x25519, according to
the Mode byte [20].
Expand All @@ -357,6 +358,9 @@ that is configured to push (x11-style forwarding) want to act as a source and
would send 1 in its first HELLO, otherwise 2 (sink). If this does not match the
configuration/expectations of the other end, the connection MUST be terminated.

The petname in the direct HELLO state is treated as a suggested (valid utf-8)
visible simplified user presentable handle.

### command = 1, shutdown
- [18..n] : last\_words : UTF-8

Expand Down Expand Up @@ -504,18 +508,43 @@ directory-list commands.

This is sent as a reply to the directory list command and is used to notify
about the update, removal, creation or presence of a retrievable application.
An empty identifier terminates. The applname can be used as the extension
field of a BCHUNKSTATE event to initiate the actual transfer.
An empty identifier terminates. The applname or server-identifier can be used
as the extension field of a BCHUNKSTATE event to initiate the actual transfer.

### command - 11, directory-discover
- [18.. 19] role : uint8 (0) source, (1) sink, (2) directory
- [20 ] state : uint8, (0) added, (1) lost
- [21.. 36] petname : (+16) utf-8 server-generated identifier / user-provided
- [37.. 52] id : (+16) Kpub (x25519)

This is provided when a new source or sink has flagged for availability or been
disconnected. The petname is chosen by hashing into a server-local dictionary
and allocated on first-use or provided on initial HELLO.
- [21.. 36] id : (+16) Kpub (x25519)
- [37 +16] petname : UTF-8 identifier

This is provided when a new source or sink has flagged for availability
(state=0) or been disconnected (state=1). The petname is provided on initial
source/sink/directory HELLO or chosen by the directory server due to local
policy or name collision.

### command - 12, directory-open
- [18 ] Mode : (0: direct, 1: tunnel)
- [19..34] Kpub : (+16) Kpub (x25519)
- [35..50] petname : UTF-8 identifier

This is used to request a connection / connection request to the provided
petname. Kpub is the public key that will be used in the HELLO to the target.
This can be the same Kpub used to make the connection to the directory server,
but might also be a different one in order to differentiate between trust
domains. It will be forwarded to the source/sink/directory in question.

If mode is set to tunnnel:ed, the active connection will be used to route
traffic to/from the nested connection. This is a workthrough for cases where
a direct connection cannot be established, corresponding carriers for NAT
traversal (UDP blocked, misconfigured routers) and might not be permitted
by the server connection.

A client is intended to first try to establish a direct connection, and after a
failed attempt, try the tunnel route.

### command - 14, directory-opened
- [18 ] Status : (0 failed, 1 direct ok, 2 tunnel ok)
- [19 +16] Address : Status = 1, IPv6 address to the host, Status = 2, tunnel ID.

## Event (2), fixed length
- [0..7] sequence number : uint64
Expand Down
4 changes: 4 additions & 0 deletions src/a12/net/dir_cl.c
Expand Up @@ -580,6 +580,10 @@ void anet_directory_cl(

sigaction(SIGPIPE,&(struct sigaction){.sa_handler = SIG_IGN}, 0);

if (opts.source_argv){
return;
}

/* always request dirlist so we can resolve applname against the server-local
* ID as that might change */
a12int_request_dirlist(S, false);
Expand Down

0 comments on commit 8578606

Please sign in to comment.