Switch to a 128-bit token instead of a 64-bit secret.

Protobuf doesn't have a 128-bit type so use two u64s.
We now support partial reads of the token.
This commit is contained in:
Todd C. Miller
2021-09-01 10:17:26 -06:00
parent 4bff82cab4
commit 9a690a8984
6 changed files with 84 additions and 46 deletions

View File

@@ -63,6 +63,7 @@ enum intercept_state {
/* Closure for intercept_cb() */
struct intercept_closure {
union sudo_token_un token;
struct command_details *details;
struct sudo_event ev;
const char *errstr;
@@ -76,7 +77,7 @@ struct intercept_closure {
enum intercept_state state;
};
static uint64_t intercept_secret;
static union sudo_token_un intercept_token;
static in_port_t intercept_listen_port;
static void intercept_accept_cb(int fd, int what, void *v);
static void intercept_cb(int fd, int what, void *v);
@@ -98,7 +99,8 @@ intercept_setup(int fd, struct sudo_event_base *evbase,
}
/* If we've already seen a ClientHello, expect a policy check first. */
closure->state = intercept_secret ? RECV_SECRET : RECV_HELLO_INITIAL;
closure->state = sudo_token_isset(intercept_token) ?
RECV_SECRET : RECV_HELLO_INITIAL;
closure->details = details;
closure->listen_sock = -1;
@@ -153,7 +155,7 @@ intercept_connection_close(int fd, struct intercept_closure *closure)
/*
* Prepare to listen on localhost using an ephemeral port.
* Sets intercept_secret and intercept_listen_port as side effects.
* Sets intercept_token and intercept_listen_port as side effects.
*/
static bool
prepare_listener(struct intercept_closure *closure)
@@ -163,10 +165,10 @@ prepare_listener(struct intercept_closure *closure)
int sock;
debug_decl(prepare_listener, SUDO_DEBUG_EXEC);
/* Secret must be non-zero. */
/* Generate a random token. */
do {
intercept_secret = arc4random() | ((uint64_t)arc4random() << 32);
} while (intercept_secret == 0);
arc4random_buf(&intercept_token, sizeof(intercept_token));
} while (!sudo_token_isset(intercept_token));
/* Create localhost listener socket (currently AF_INET only). */
sock = socket(AF_INET, SOCK_STREAM, 0);
@@ -450,33 +452,43 @@ done:
}
/*
* Read shared secret from sudo_intercept.so and verify w/ intercept_secret.
* Read token from sudo_intercept.so and verify w/ intercept_token.
* Returns true on success, false on mismatch and -1 on error.
*/
static int
intercept_verify_secret(int fd)
intercept_verify_token(int fd, struct intercept_closure *closure)
{
uint64_t secret;
ssize_t nread;
debug_decl(intercept_read_secret, SUDO_DEBUG_EXEC);
debug_decl(intercept_read_token, SUDO_DEBUG_EXEC);
/* Read shared secret (uint64_t in host byte order). */
nread = recv(fd, &secret, sizeof(secret), 0);
if (nread != sizeof(secret)) {
if (nread == -1)
debug_return_int(-1);
/* Treat short read as a secret mismatch. */
nread = recv(fd, &closure->token + closure->off,
sizeof(closure->token) - closure->off, 0);
switch (nread) {
case 0:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"short read, expected %zu, got %zd", sizeof(secret), nread);
debug_return_int(false);
}
if (secret != intercept_secret) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"secret mismatch: got %" PRIu64 ", expected %" PRIu64, secret,
intercept_secret);
"EOF reading token");
debug_return_int(false);
case -1:
debug_return_int(-1);
default:
if (nread + closure->off == sizeof(closure->token))
break;
/* partial read, update offset and try again */
closure->off += nread;
errno = EAGAIN;
debug_return_int(-1);
}
closure->off = 0;
if (memcmp(&closure->token, &intercept_token, sizeof(closure->token)) != 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"token mismatch: got 0x%8x%8x%8x%8x, expected 0x%8x%8x%8x%8x",
closure->token.u32[3], closure->token.u32[2],
closure->token.u32[1], closure->token.u32[0],
intercept_token.u32[3], intercept_token.u32[2],
intercept_token.u32[1], intercept_token.u32[0]);
debug_return_int(false);
}
debug_return_int(true);
}
@@ -496,7 +508,7 @@ intercept_read(int fd, struct intercept_closure *closure)
debug_decl(intercept_read, SUDO_DEBUG_EXEC);
if (closure->state == RECV_SECRET) {
switch (intercept_verify_secret(fd)) {
switch (intercept_verify_token(fd, closure)) {
case true:
closure->state = RECV_POLICY_CHECK;
break;
@@ -505,6 +517,7 @@ intercept_read(int fd, struct intercept_closure *closure)
default:
if (errno == EINTR || errno == EAGAIN)
debug_return_bool(true);
sudo_warn("recv");
goto done;
}
}
@@ -693,7 +706,8 @@ fmt_hello_response(struct intercept_closure *closure)
debug_decl(fmt_hello_response, SUDO_DEBUG_EXEC);
hello_resp.portno = intercept_listen_port;
hello_resp.secret = intercept_secret;
hello_resp.token_lo = intercept_token.u64[0];
hello_resp.token_hi = intercept_token.u64[1];
resp.u.hello_resp = &hello_resp;
resp.type_case = INTERCEPT_RESPONSE__TYPE_HELLO_RESP;