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:
@@ -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 }
|
||||
|
||||
|
||||
/*
|
||||
|
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user