Pass a secret value to sudo_intercept.so and verify after policy check.

The goal is to make it harder for someone to have a fake policy checker.
This will not stop a determined adversary since the secret is present
in the address space of the running process.
This commit is contained in:
Todd C. Miller
2021-08-13 09:10:44 -06:00
parent c9d9225469
commit eaf03a382b
8 changed files with 57 additions and 7 deletions

View File

@@ -118,6 +118,7 @@ typedef enum {
struct _PolicyCheckResult struct _PolicyCheckResult
{ {
ProtobufCMessage base; ProtobufCMessage base;
uint64_t secret;
PolicyCheckResult__TypeCase type_case; PolicyCheckResult__TypeCase type_case;
union { union {
PolicyAcceptMessage *accept_msg; PolicyAcceptMessage *accept_msg;
@@ -127,7 +128,7 @@ struct _PolicyCheckResult
}; };
#define POLICY_CHECK_RESULT__INIT \ #define POLICY_CHECK_RESULT__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&policy_check_result__descriptor) \ { PROTOBUF_C_MESSAGE_INIT (&policy_check_result__descriptor) \
, POLICY_CHECK_RESULT__TYPE__NOT_SET, {0} } , 0, POLICY_CHECK_RESULT__TYPE__NOT_SET, {0} }
/* InterceptMessage methods */ /* InterceptMessage methods */

View File

@@ -52,6 +52,7 @@ static void intercept_cb(int fd, int what, void *v);
/* Must match start of exec_closure_nopty and monitor_closure. */ /* Must match start of exec_closure_nopty and monitor_closure. */
struct intercept_fd_closure { struct intercept_fd_closure {
uint64_t secret;
struct command_details *details; struct command_details *details;
struct sudo_event_base *evbase; struct sudo_event_base *evbase;
}; };
@@ -65,6 +66,7 @@ struct intercept_closure {
char **run_argv; /* owned by plugin */ char **run_argv; /* owned by plugin */
char **run_envp; /* dynamically allocated */ char **run_envp; /* dynamically allocated */
uint8_t *buf; /* dynamically allocated */ uint8_t *buf; /* dynamically allocated */
uint64_t secret;
size_t len; size_t len;
int policy_result; int policy_result;
}; };
@@ -380,13 +382,14 @@ done:
} }
static bool static bool
fmt_policy_check_result(PolicyCheckResult *msg, struct intercept_closure *closure) fmt_policy_check_result(PolicyCheckResult *res, struct intercept_closure *closure)
{ {
uint32_t msg_len; uint32_t msg_len;
bool ret = false; bool ret = false;
debug_decl(fmt_policy_check_result, SUDO_DEBUG_EXEC); debug_decl(fmt_policy_check_result, SUDO_DEBUG_EXEC);
closure->len = policy_check_result__get_packed_size(msg); res->secret = closure->secret;
closure->len = policy_check_result__get_packed_size(res);
if (closure->len > MESSAGE_SIZE_MAX) { if (closure->len > MESSAGE_SIZE_MAX) {
sudo_warnx(U_("server message too large: %zu"), closure->len); sudo_warnx(U_("server message too large: %zu"), closure->len);
goto done; goto done;
@@ -404,7 +407,7 @@ fmt_policy_check_result(PolicyCheckResult *msg, struct intercept_closure *closur
goto done; goto done;
} }
memcpy(closure->buf, &msg_len, sizeof(msg_len)); memcpy(closure->buf, &msg_len, sizeof(msg_len));
policy_check_result__pack(msg, closure->buf + sizeof(msg_len)); policy_check_result__pack(res, closure->buf + sizeof(msg_len));
ret = true; ret = true;
@@ -565,6 +568,7 @@ intercept_fd_cb(int fd, int what, void *v)
sudo_warnx("%s", U_("unable to allocate memory")); sudo_warnx("%s", U_("unable to allocate memory"));
goto bad; goto bad;
} }
closure->secret = fdc->secret;
closure->details = fdc->details; closure->details = fdc->details;
/* /*
@@ -598,6 +602,13 @@ intercept_fd_cb(int fd, int what, void *v)
break; break;
} }
if (ch == INTERCEPT_REQ_SEC) {
/* Client requested secret from ctor, no fd is present. */
if (write(fd, &fdc->secret, sizeof(fdc->secret)) != sizeof(fdc->secret))
goto bad;
debug_return;
}
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1 #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg == NULL) { if (cmsg == NULL) {

View File

@@ -40,6 +40,7 @@
/* Note that details and evbase must come first. */ /* Note that details and evbase must come first. */
struct exec_closure_nopty { struct exec_closure_nopty {
uint64_t secret;
struct command_details *details; struct command_details *details;
struct sudo_event_base *evbase; struct sudo_event_base *evbase;
struct sudo_event *errpipe_event; struct sudo_event *errpipe_event;
@@ -201,6 +202,7 @@ fill_exec_closure_nopty(struct exec_closure_nopty *ec,
debug_decl(fill_exec_closure_nopty, SUDO_DEBUG_EXEC); debug_decl(fill_exec_closure_nopty, SUDO_DEBUG_EXEC);
/* Fill in the non-event part of the closure. */ /* Fill in the non-event part of the closure. */
ec->secret = arc4random() | ((uint64_t)arc4random() << 32);
ec->ppgrp = getpgrp(); ec->ppgrp = getpgrp();
ec->cstat = cstat; ec->cstat = cstat;
ec->details = details; ec->details = details;

View File

@@ -57,6 +57,7 @@ TAILQ_HEAD(monitor_message_list, monitor_message);
/* Note that details and evbase must come first. */ /* Note that details and evbase must come first. */
struct exec_closure_pty { struct exec_closure_pty {
uint64_t secret;
struct command_details *details; struct command_details *details;
struct sudo_event_base *evbase; struct sudo_event_base *evbase;
struct sudo_event *backchannel_event; struct sudo_event *backchannel_event;
@@ -1205,6 +1206,7 @@ fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
debug_decl(fill_exec_closure_pty, SUDO_DEBUG_EXEC); debug_decl(fill_exec_closure_pty, SUDO_DEBUG_EXEC);
/* Fill in the non-event part of the closure. */ /* Fill in the non-event part of the closure. */
ec->secret = arc4random() | ((uint64_t)arc4random() << 32);
ec->cmnd_pid = -1; ec->cmnd_pid = -1;
ec->ppgrp = ppgrp; ec->ppgrp = ppgrp;
ec->cstat = cstat; ec->cstat = cstat;

View File

@@ -519,7 +519,7 @@ const ProtobufCMessageDescriptor policy_error_message__descriptor =
(ProtobufCMessageInit) policy_error_message__init, (ProtobufCMessageInit) policy_error_message__init,
NULL,NULL,NULL /* reserved[123] */ NULL,NULL,NULL /* reserved[123] */
}; };
static const ProtobufCFieldDescriptor policy_check_result__field_descriptors[3] = static const ProtobufCFieldDescriptor policy_check_result__field_descriptors[4] =
{ {
{ {
"accept_msg", "accept_msg",
@@ -557,16 +557,29 @@ static const ProtobufCFieldDescriptor policy_check_result__field_descriptors[3]
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */ 0,NULL,NULL /* reserved1,reserved2, etc */
}, },
{
"secret",
4,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_FIXED64,
0, /* quantifier_offset */
offsetof(PolicyCheckResult, secret),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
}; };
static const unsigned policy_check_result__field_indices_by_name[] = { static const unsigned policy_check_result__field_indices_by_name[] = {
0, /* field[0] = accept_msg */ 0, /* field[0] = accept_msg */
2, /* field[2] = error_msg */ 2, /* field[2] = error_msg */
1, /* field[1] = reject_msg */ 1, /* field[1] = reject_msg */
3, /* field[3] = secret */
}; };
static const ProtobufCIntRange policy_check_result__number_ranges[1 + 1] = static const ProtobufCIntRange policy_check_result__number_ranges[1 + 1] =
{ {
{ 1, 0 }, { 1, 0 },
{ 0, 3 } { 0, 4 }
}; };
const ProtobufCMessageDescriptor policy_check_result__descriptor = const ProtobufCMessageDescriptor policy_check_result__descriptor =
{ {
@@ -576,7 +589,7 @@ const ProtobufCMessageDescriptor policy_check_result__descriptor =
"PolicyCheckResult", "PolicyCheckResult",
"", "",
sizeof(PolicyCheckResult), sizeof(PolicyCheckResult),
3, 4,
policy_check_result__field_descriptors, policy_check_result__field_descriptors,
policy_check_result__field_indices_by_name, policy_check_result__field_indices_by_name,
1, policy_check_result__number_ranges, 1, policy_check_result__number_ranges,

View File

@@ -44,4 +44,5 @@ message PolicyCheckResult {
PolicyRejectMessage reject_msg = 2; PolicyRejectMessage reject_msg = 2;
PolicyErrorMessage error_msg = 3; PolicyErrorMessage error_msg = 3;
} }
fixed64 secret = 4;
} }

View File

@@ -80,6 +80,7 @@
#define SESH_ERR_SOME_FILES 33 /* copy error, some files copied */ #define SESH_ERR_SOME_FILES 33 /* copy error, some files copied */
#define INTERCEPT_FD_MIN 64 /* minimum fd so shell won't close it */ #define INTERCEPT_FD_MIN 64 /* minimum fd so shell won't close it */
#define INTERCEPT_REQ_SEC 42 /* request intercept secret */
#define MESSAGE_SIZE_MAX 2097152 /* 2Mib max intercept message size */ #define MESSAGE_SIZE_MAX 2097152 /* 2Mib max intercept message size */
/* /*

View File

@@ -53,6 +53,7 @@
extern char **environ; extern char **environ;
static int intercept_sock = -1; static int intercept_sock = -1;
static uint64_t secret;
/* /*
* Look up SUDO_INTERCEPT_FD in the environment. * Look up SUDO_INTERCEPT_FD in the environment.
@@ -82,6 +83,7 @@ sudo_interposer_init(void)
if (strncmp(*p, "SUDO_INTERCEPT_FD=", sizeof("SUDO_INTERCEPT_FD=") -1) == 0) { if (strncmp(*p, "SUDO_INTERCEPT_FD=", sizeof("SUDO_INTERCEPT_FD=") -1) == 0) {
const char *fdstr = *p + sizeof("SUDO_INTERCEPT_FD=") - 1; const char *fdstr = *p + sizeof("SUDO_INTERCEPT_FD=") - 1;
const char *errstr; const char *errstr;
char ch = INTERCEPT_REQ_SEC;
int fd; int fd;
fd = sudo_strtonum(fdstr, 0, INT_MAX, &errstr); fd = sudo_strtonum(fdstr, 0, INT_MAX, &errstr);
@@ -90,6 +92,19 @@ sudo_interposer_init(void)
"invalid SUDO_INTERCEPT_FD: %s: %s", fdstr, errstr); "invalid SUDO_INTERCEPT_FD: %s: %s", fdstr, errstr);
break; break;
} }
/* Request secret from parent. */
if (send(fd, &ch, sizeof(ch), 0) != sizeof(ch)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to request secret: %s", strerror(errno));
break;
}
if (recv(fd, &secret, sizeof(secret), 0) != sizeof(secret)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to read secret: %s", strerror(errno));
break;
}
intercept_sock = fd; intercept_sock = fd;
break; break;
} }
@@ -308,6 +323,10 @@ command_allowed(const char *cmnd, char * const argv[], char * const envp[],
"unable to unpack %s size %u", "PolicyCheckResult", res_len); "unable to unpack %s size %u", "PolicyCheckResult", res_len);
goto done; goto done;
} }
if (res->secret != secret) {
sudo_warnx("secret mismatch\r");
goto done;
}
switch (res->type_case) { switch (res->type_case) {
case POLICY_CHECK_RESULT__TYPE_ACCEPT_MSG: case POLICY_CHECK_RESULT__TYPE_ACCEPT_MSG:
if (sudo_debug_needed(SUDO_DEBUG_INFO)) { if (sudo_debug_needed(SUDO_DEBUG_INFO)) {