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

@@ -57,7 +57,7 @@ struct _InterceptRequest
/*
* Hello message from sudo_intercept.so to main sudo process.
* Sudo sends back the secret and localhost port number.
* Sudo sends back the token and localhost port number.
*/
struct _ClientHello
{
@@ -71,17 +71,18 @@ struct _ClientHello
/*
* Sudo response to a ClientHello from sudo_intercept.so.
* The client uses the port number and secret to connect back to sudo.
* The client uses the port number and token to connect back to sudo.
*/
struct _HelloResponse
{
ProtobufCMessage base;
uint64_t secret;
uint64_t token_lo;
uint64_t token_hi;
int32_t portno;
};
#define HELLO_RESPONSE__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&hello_response__descriptor) \
, 0, 0 }
, 0, 0, 0 }
/*

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)
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,
"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);
/* Treat short read as a secret mismatch. */
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);
debug_return_int(false);
}
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;

View File

@@ -456,15 +456,27 @@ const ProtobufCMessageDescriptor client_hello__descriptor =
(ProtobufCMessageInit) client_hello__init,
NULL,NULL,NULL /* reserved[123] */
};
static const ProtobufCFieldDescriptor hello_response__field_descriptors[2] =
static const ProtobufCFieldDescriptor hello_response__field_descriptors[3] =
{
{
"secret",
"token_lo",
1,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_FIXED64,
0, /* quantifier_offset */
offsetof(HelloResponse, secret),
offsetof(HelloResponse, token_lo),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"token_hi",
2,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_FIXED64,
0, /* quantifier_offset */
offsetof(HelloResponse, token_hi),
NULL,
NULL,
0, /* flags */
@@ -472,7 +484,7 @@ static const ProtobufCFieldDescriptor hello_response__field_descriptors[2] =
},
{
"portno",
2,
3,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_INT32,
0, /* quantifier_offset */
@@ -484,13 +496,14 @@ static const ProtobufCFieldDescriptor hello_response__field_descriptors[2] =
},
};
static const unsigned hello_response__field_indices_by_name[] = {
1, /* field[1] = portno */
0, /* field[0] = secret */
2, /* field[2] = portno */
1, /* field[1] = token_hi */
0, /* field[0] = token_lo */
};
static const ProtobufCIntRange hello_response__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 2 }
{ 0, 3 }
};
const ProtobufCMessageDescriptor hello_response__descriptor =
{
@@ -500,7 +513,7 @@ const ProtobufCMessageDescriptor hello_response__descriptor =
"HelloResponse",
"",
sizeof(HelloResponse),
2,
3,
hello_response__field_descriptors,
hello_response__field_indices_by_name,
1, hello_response__number_ranges,

View File

@@ -13,7 +13,7 @@ message InterceptRequest {
/*
* Hello message from sudo_intercept.so to main sudo process.
* Sudo sends back the secret and localhost port number.
* Sudo sends back the token and localhost port number.
*/
message ClientHello {
int32 pid = 1;
@@ -21,11 +21,12 @@ message ClientHello {
/*
* Sudo response to a ClientHello from sudo_intercept.so.
* The client uses the port number and secret to connect back to sudo.
* The client uses the port number and token to connect back to sudo.
*/
message HelloResponse {
fixed64 secret = 1;
int32 portno = 2;
fixed64 token_lo = 1;
fixed64 token_hi = 2;
int32 portno = 3;
}
/*

View File

@@ -82,6 +82,14 @@
#define INTERCEPT_FD_MIN 64 /* minimum fd so shell won't close it */
#define MESSAGE_SIZE_MAX 2097152 /* 2Mib max intercept message size */
union sudo_token_un {
unsigned char u8[16];
unsigned int u32[4];
unsigned long long u64[2];
};
#define sudo_token_isset(_t) ((_t).u64[0] || (_t).u64[1])
/*
* Symbols shared between exec.c, exec_nopty.c, exec_pty.c and exec_monitor.c
*/

View File

@@ -58,7 +58,7 @@
extern char **environ;
static uint64_t secret;
static union sudo_token_un intercept_token;
static in_port_t intercept_port;
/* Send entire request to sudo (blocking). */
@@ -247,7 +247,8 @@ sudo_interposer_init(void)
res = recv_intercept_response(fd);
if (res != NULL) {
secret = res->u.hello_resp->secret;
intercept_token.u64[0] = res->u.hello_resp->token_lo;
intercept_token.u64[1] = res->u.hello_resp->token_hi;
intercept_port = res->u.hello_resp->portno;
intercept_response__free_unpacked(res, NULL);
}
@@ -272,10 +273,10 @@ send_policy_check_req(int sock, const char *cmnd, char * const argv[],
size_t len;
debug_decl(fmt_policy_check_req, SUDO_DEBUG_EXEC);
/* Send secret first (out of band) to initiate connection. */
if (!send_req(sock, &secret, sizeof(secret))) {
/* Send token first (out of band) to initiate connection. */
if (!send_req(sock, &intercept_token, sizeof(intercept_token))) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to send secret back to sudo");
"unable to send token back to sudo");
goto done;
}