Change intercept IPC to use a localhost socket instead of inherited fd.

This allows intercept mode to work with shells that close all open
fds upon startup.  The ctor in sudo_intercept.so requests the port
number and secret over the socket inherited from the parent then
closes it.  For each policy request, a TCP connection is made to
the sudo parent process to perform the policy check.  Child processes
re-use the TCP socket to request the port number and secret just like
the initial process started by sudo does.
This commit is contained in:
Todd C. Miller
2021-08-25 14:24:36 -06:00
parent 448536e0f7
commit c465d8971d
11 changed files with 1067 additions and 632 deletions

View File

@@ -842,9 +842,6 @@
/* Define to 1 if the system has the type `struct in6_addr'. */
#undef HAVE_STRUCT_IN6_ADDR
/* Define to 1 if `msg_control' is a member of `struct msghdr'. */
#undef HAVE_STRUCT_MSGHDR_MSG_CONTROL
/* Define to 1 if `pr_ttydev' is a member of `struct psinfo'. */
#undef HAVE_STRUCT_PSINFO_PR_TTYDEV

13
configure vendored
View File

@@ -23254,19 +23254,6 @@ then :
printf "%s\n" "#define HAVE_STRUCT_DIRENT_D_NAMLEN 1" >>confdefs.h
fi
ac_fn_c_check_member "$LINENO" "struct msghdr" "msg_control" "ac_cv_member_struct_msghdr_msg_control" "
$ac_includes_default
#include <sys/socket.h>
"
if test "x$ac_cv_member_struct_msghdr_msg_control" = xyes
then :
printf "%s\n" "#define HAVE_STRUCT_MSGHDR_MSG_CONTROL 1" >>confdefs.h
fi
openssl_missing=no

View File

@@ -2925,13 +2925,6 @@ AC_INCLUDES_DEFAULT
#include <$ac_header_dirent>
])
dnl
dnl Check for POSIX sendmsg() ancillary data support.
dnl
AC_CHECK_MEMBERS(struct msghdr.msg_control, [], [], [
AC_INCLUDES_DEFAULT
#include <sys/socket.h>
])
dnl
dnl Check for functions only present in OpenSSL 1.1 and above
dnl
openssl_missing=no

View File

@@ -15,12 +15,14 @@ PROTOBUF_C__BEGIN_DECLS
#endif
typedef struct _InterceptMessage InterceptMessage;
typedef struct _InterceptRequest InterceptRequest;
typedef struct _ClientHello ClientHello;
typedef struct _HelloResponse HelloResponse;
typedef struct _PolicyCheckRequest PolicyCheckRequest;
typedef struct _PolicyAcceptMessage PolicyAcceptMessage;
typedef struct _PolicyRejectMessage PolicyRejectMessage;
typedef struct _PolicyErrorMessage PolicyErrorMessage;
typedef struct _PolicyCheckResult PolicyCheckResult;
typedef struct _InterceptResponse InterceptResponse;
/* --- enums --- */
@@ -29,30 +31,62 @@ typedef struct _PolicyCheckResult PolicyCheckResult;
/* --- messages --- */
typedef enum {
INTERCEPT_MESSAGE__TYPE__NOT_SET = 0,
INTERCEPT_MESSAGE__TYPE_POLICY_CHECK_REQ = 1
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INTERCEPT_MESSAGE__TYPE)
} InterceptMessage__TypeCase;
INTERCEPT_REQUEST__TYPE__NOT_SET = 0,
INTERCEPT_REQUEST__TYPE_POLICY_CHECK_REQ = 1,
INTERCEPT_REQUEST__TYPE_HELLO = 2
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INTERCEPT_REQUEST__TYPE)
} InterceptRequest__TypeCase;
/*
* Intercept message from sudo_intercept.so. Messages on the
* wire are prefixed with a 32-bit size in network byte order.
*/
struct _InterceptMessage
struct _InterceptRequest
{
ProtobufCMessage base;
InterceptMessage__TypeCase type_case;
InterceptRequest__TypeCase type_case;
union {
PolicyCheckRequest *policy_check_req;
ClientHello *hello;
} u;
};
#define INTERCEPT_MESSAGE__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&intercept_message__descriptor) \
, INTERCEPT_MESSAGE__TYPE__NOT_SET, {0} }
#define INTERCEPT_REQUEST__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&intercept_request__descriptor) \
, INTERCEPT_REQUEST__TYPE__NOT_SET, {0} }
/*
* Hello message from sudo_intercept.so to main sudo process.
* Sudo sends back the secret and localhost port number.
*/
struct _ClientHello
{
ProtobufCMessage base;
int32_t pid;
};
#define CLIENT_HELLO__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&client_hello__descriptor) \
, 0 }
/*
* Sudo response to a ClientHello from sudo_intercept.so.
* The client uses the port number and secret to connect back to sudo.
*/
struct _HelloResponse
{
ProtobufCMessage base;
uint64_t secret;
int32_t portno;
};
#define HELLO_RESPONSE__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&hello_response__descriptor) \
, 0, 0 }
/*
* Policy check request from sudo_intercept.so.
* Must include the correct secret value.
* Note that the plugin API only currently supports passing
* the new environment in to the open() function.
*/
@@ -64,10 +98,12 @@ struct _PolicyCheckRequest
char **argv;
size_t n_envp;
char **envp;
int32_t intercept_fd;
uint64_t secret;
};
#define POLICY_CHECK_REQUEST__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&policy_check_request__descriptor) \
, (char *)protobuf_c_empty_string, 0,NULL, 0,NULL }
, (char *)protobuf_c_empty_string, 0,NULL, 0,NULL, 0, 0 }
struct _PolicyAcceptMessage
@@ -105,50 +141,89 @@ struct _PolicyErrorMessage
typedef enum {
POLICY_CHECK_RESULT__TYPE__NOT_SET = 0,
POLICY_CHECK_RESULT__TYPE_ACCEPT_MSG = 1,
POLICY_CHECK_RESULT__TYPE_REJECT_MSG = 2,
POLICY_CHECK_RESULT__TYPE_ERROR_MSG = 3
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(POLICY_CHECK_RESULT__TYPE)
} PolicyCheckResult__TypeCase;
INTERCEPT_RESPONSE__TYPE__NOT_SET = 0,
INTERCEPT_RESPONSE__TYPE_HELLO_RESP = 1,
INTERCEPT_RESPONSE__TYPE_ACCEPT_MSG = 2,
INTERCEPT_RESPONSE__TYPE_REJECT_MSG = 3,
INTERCEPT_RESPONSE__TYPE_ERROR_MSG = 4
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INTERCEPT_RESPONSE__TYPE)
} InterceptResponse__TypeCase;
/*
* Policy check result sent back to sudo_intercept.so.
* Response sent back to sudo_intercept.so.
*/
struct _PolicyCheckResult
struct _InterceptResponse
{
ProtobufCMessage base;
uint64_t secret;
PolicyCheckResult__TypeCase type_case;
InterceptResponse__TypeCase type_case;
union {
HelloResponse *hello_resp;
PolicyAcceptMessage *accept_msg;
PolicyRejectMessage *reject_msg;
PolicyErrorMessage *error_msg;
} u;
};
#define POLICY_CHECK_RESULT__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&policy_check_result__descriptor) \
, 0, POLICY_CHECK_RESULT__TYPE__NOT_SET, {0} }
#define INTERCEPT_RESPONSE__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&intercept_response__descriptor) \
, INTERCEPT_RESPONSE__TYPE__NOT_SET, {0} }
/* InterceptMessage methods */
void intercept_message__init
(InterceptMessage *message);
size_t intercept_message__get_packed_size
(const InterceptMessage *message);
size_t intercept_message__pack
(const InterceptMessage *message,
/* InterceptRequest methods */
void intercept_request__init
(InterceptRequest *message);
size_t intercept_request__get_packed_size
(const InterceptRequest *message);
size_t intercept_request__pack
(const InterceptRequest *message,
uint8_t *out);
size_t intercept_message__pack_to_buffer
(const InterceptMessage *message,
size_t intercept_request__pack_to_buffer
(const InterceptRequest *message,
ProtobufCBuffer *buffer);
InterceptMessage *
intercept_message__unpack
InterceptRequest *
intercept_request__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void intercept_message__free_unpacked
(InterceptMessage *message,
void intercept_request__free_unpacked
(InterceptRequest *message,
ProtobufCAllocator *allocator);
/* ClientHello methods */
void client_hello__init
(ClientHello *message);
size_t client_hello__get_packed_size
(const ClientHello *message);
size_t client_hello__pack
(const ClientHello *message,
uint8_t *out);
size_t client_hello__pack_to_buffer
(const ClientHello *message,
ProtobufCBuffer *buffer);
ClientHello *
client_hello__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void client_hello__free_unpacked
(ClientHello *message,
ProtobufCAllocator *allocator);
/* HelloResponse methods */
void hello_response__init
(HelloResponse *message);
size_t hello_response__get_packed_size
(const HelloResponse *message);
size_t hello_response__pack
(const HelloResponse *message,
uint8_t *out);
size_t hello_response__pack_to_buffer
(const HelloResponse *message,
ProtobufCBuffer *buffer);
HelloResponse *
hello_response__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void hello_response__free_unpacked
(HelloResponse *message,
ProtobufCAllocator *allocator);
/* PolicyCheckRequest methods */
void policy_check_request__init
@@ -226,29 +301,35 @@ PolicyErrorMessage *
void policy_error_message__free_unpacked
(PolicyErrorMessage *message,
ProtobufCAllocator *allocator);
/* PolicyCheckResult methods */
void policy_check_result__init
(PolicyCheckResult *message);
size_t policy_check_result__get_packed_size
(const PolicyCheckResult *message);
size_t policy_check_result__pack
(const PolicyCheckResult *message,
/* InterceptResponse methods */
void intercept_response__init
(InterceptResponse *message);
size_t intercept_response__get_packed_size
(const InterceptResponse *message);
size_t intercept_response__pack
(const InterceptResponse *message,
uint8_t *out);
size_t policy_check_result__pack_to_buffer
(const PolicyCheckResult *message,
size_t intercept_response__pack_to_buffer
(const InterceptResponse *message,
ProtobufCBuffer *buffer);
PolicyCheckResult *
policy_check_result__unpack
InterceptResponse *
intercept_response__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void policy_check_result__free_unpacked
(PolicyCheckResult *message,
void intercept_response__free_unpacked
(InterceptResponse *message,
ProtobufCAllocator *allocator);
/* --- per-message closures --- */
typedef void (*InterceptMessage_Closure)
(const InterceptMessage *message,
typedef void (*InterceptRequest_Closure)
(const InterceptRequest *message,
void *closure_data);
typedef void (*ClientHello_Closure)
(const ClientHello *message,
void *closure_data);
typedef void (*HelloResponse_Closure)
(const HelloResponse *message,
void *closure_data);
typedef void (*PolicyCheckRequest_Closure)
(const PolicyCheckRequest *message,
@@ -262,8 +343,8 @@ typedef void (*PolicyRejectMessage_Closure)
typedef void (*PolicyErrorMessage_Closure)
(const PolicyErrorMessage *message,
void *closure_data);
typedef void (*PolicyCheckResult_Closure)
(const PolicyCheckResult *message,
typedef void (*InterceptResponse_Closure)
(const InterceptResponse *message,
void *closure_data);
/* --- services --- */
@@ -271,12 +352,14 @@ typedef void (*PolicyCheckResult_Closure)
/* --- descriptors --- */
extern const ProtobufCMessageDescriptor intercept_message__descriptor;
extern const ProtobufCMessageDescriptor intercept_request__descriptor;
extern const ProtobufCMessageDescriptor client_hello__descriptor;
extern const ProtobufCMessageDescriptor hello_response__descriptor;
extern const ProtobufCMessageDescriptor policy_check_request__descriptor;
extern const ProtobufCMessageDescriptor policy_accept_message__descriptor;
extern const ProtobufCMessageDescriptor policy_reject_message__descriptor;
extern const ProtobufCMessageDescriptor policy_error_message__descriptor;
extern const ProtobufCMessageDescriptor policy_check_result__descriptor;
extern const ProtobufCMessageDescriptor intercept_response__descriptor;
PROTOBUF_C__END_DECLS

View File

@@ -24,7 +24,7 @@
#include <config.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#if defined(HAVE_STDINT_H)
# include <stdint.h>
@@ -42,6 +42,7 @@
#include "sudo_exec.h"
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
#include "sudo_rand.h"
#include "intercept.pb-c.h"
/* TCSASOFT is a BSD extension that ignores control flags and speed. */
@@ -49,15 +50,6 @@
# define TCSASOFT 0
#endif
static void intercept_cb(int fd, int what, void *v);
/* Must match start of exec_closure_nopty and monitor_closure. */
struct intercept_fd_closure {
uint64_t secret;
struct command_details *details;
struct sudo_event_base *evbase;
};
/* Closure for intercept_cb() */
struct intercept_closure {
struct command_details *details;
@@ -67,23 +59,68 @@ struct intercept_closure {
char **run_argv; /* owned by plugin */
char **run_envp; /* dynamically allocated */
uint8_t *buf; /* dynamically allocated */
uint64_t secret;
size_t len;
int listen_sock;
int policy_result;
};
static uint64_t intercept_secret;
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);
bool
intercept_setup(int fd, struct sudo_event_base *evbase,
struct command_details *details)
{
struct intercept_closure *closure;
debug_decl(intercept_setup, SUDO_DEBUG_EXEC);
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"intercept fd %d\n", fd);
closure = calloc(1, sizeof(*closure));
if (closure == NULL) {
sudo_warnx("%s", U_("unable to allocate memory"));
goto bad;
}
closure->details = details;
/* XXX - add proper state variable */
closure->policy_result = intercept_secret ? -1 : 1;
closure->listen_sock = -1;
if (sudo_ev_set(&closure->ev, fd, SUDO_EV_READ, intercept_cb, closure) == -1) {
/* This cannot (currently) fail. */
sudo_warn("%s", U_("unable to add event to queue"));
goto bad;
}
if (sudo_ev_add(evbase, &closure->ev, NULL, false) == -1) {
sudo_warn("%s", U_("unable to add event to queue"));
goto bad;
}
debug_return_bool(true);
bad:
free(closure);
debug_return_bool(false);
}
/*
* Close intercept fd and free closure.
* Called on EOF from sudo_intercept.so due to program exit.
* Close intercept socket and free closure when we are done with
* the connection.
*/
static void
intercept_close(int fd, struct intercept_closure *closure)
intercept_connection_close(int fd, struct intercept_closure *closure)
{
size_t n;
debug_decl(intercept_close, SUDO_DEBUG_EXEC);
debug_decl(intercept_connection_close, SUDO_DEBUG_EXEC);
sudo_ev_del(NULL, &closure->ev);
close(fd);
if (closure->listen_sock != -1)
close(closure->listen_sock);
free(closure->buf);
free(closure->command);
@@ -102,6 +139,59 @@ intercept_close(int fd, struct intercept_closure *closure)
debug_return;
}
/*
* Prepare to listen on localhost using an ephemeral port.
* Sets intercept_secret and intercept_listen_port as side effects.
*/
static bool
prepare_listener(struct intercept_closure *closure)
{
struct sockaddr_in sin;
socklen_t sin_len = sizeof(sin);
int sock;
debug_decl(prepare_listener, SUDO_DEBUG_EXEC);
/* Secret must be non-zero. */
do {
intercept_secret = arc4random() | ((uint64_t)arc4random() << 32);
} while (intercept_secret == 0);
/* Create localhost listener socket (currently AF_INET only). */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
sudo_warn("socket");
goto bad;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sin.sin_port = 0;
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
sudo_warn("bind");
goto bad;
}
if (getsockname(sock, (struct sockaddr *)&sin, &sin_len) == -1) {
sudo_warn("getsockname");
goto bad;
}
if (listen(sock, SOMAXCONN) == -1) {
sudo_warn("listen");
goto bad;
}
closure->listen_sock = sock;
intercept_listen_port = ntohs(sin.sin_port);
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"%s: listening on port %hu", __func__, intercept_listen_port);
debug_return_bool(true);
bad:
if (sock != -1)
close(sock);
debug_return_bool(false);
}
static int
intercept_check_policy(PolicyCheckRequest *req,
struct intercept_closure *closure, const char **errstr)
@@ -117,6 +207,13 @@ intercept_check_policy(PolicyCheckRequest *req,
*errstr = N_("invalid PolicyCheckRequest");
goto bad;
}
if (req->secret != intercept_secret) {
*errstr = N_("invalid PolicyCheckRequest");
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"secret mismatch: got %" PRIu64 ", expected %" PRIu64, req->secret,
intercept_secret);
goto bad;
}
if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
@@ -255,45 +352,41 @@ bad:
}
/*
* Read a single message from sudo_intercept.so.
* Read a single message from sudo_intercept.so and unpack it.
* Assumes fd is in blocking mode.
*/
static bool
intercept_read(int fd, struct intercept_closure *closure)
static InterceptRequest *
intercept_recv_request(int fd)
{
struct sudo_event_base *base = sudo_ev_get_base(&closure->ev);
InterceptMessage *msg = NULL;
InterceptRequest *req = NULL;
uint8_t *cp, *buf = NULL;
pid_t saved_pgrp = -1;
struct termios oterm;
uint32_t msg_len;
bool ret = false;
int ttyfd = -1;
uint32_t req_len;
ssize_t nread;
debug_decl(intercept_read, SUDO_DEBUG_EXEC);
debug_decl(intercept_recv_request, SUDO_DEBUG_EXEC);
/* Read message size (uint32_t in host byte order). */
nread = read(fd, &msg_len, sizeof(msg_len));
if (nread != sizeof(msg_len)) {
nread = recv(fd, &req_len, sizeof(req_len), 0);
if (nread != sizeof(req_len)) {
if (nread != 0)
sudo_warn("read");
goto done;
}
if (msg_len > MESSAGE_SIZE_MAX) {
sudo_warnx(U_("client message too large: %zu"), (size_t)msg_len);
if (req_len > MESSAGE_SIZE_MAX) {
sudo_warnx(U_("client request too large: %zu"), (size_t)req_len);
goto done;
}
if (msg_len > 0) {
size_t rem = msg_len;
if (req_len > 0) {
size_t rem = req_len;
if ((buf = malloc(msg_len)) == NULL) {
if ((buf = malloc(req_len)) == NULL) {
sudo_warnx("%s", U_("unable to allocate memory"));
goto done;
}
cp = buf;
do {
nread = read(fd, cp, rem);
nread = recv(fd, cp, rem, 0);
switch (nread) {
case 0:
/* EOF, other side must have exited. */
@@ -309,35 +402,83 @@ intercept_read(int fd, struct intercept_closure *closure)
} while (rem > 0);
}
msg = intercept_message__unpack(NULL, msg_len, buf);
if (msg == NULL) {
sudo_warnx("unable to unpack %s size %zu", "InterceptMessage",
(size_t)msg_len);
goto done;
}
if (msg->type_case != INTERCEPT_MESSAGE__TYPE_POLICY_CHECK_REQ) {
sudo_warnx(U_("unexpected type_case value %d in %s from %s"),
msg->type_case, "InterceptMessage", "sudo_intercept.so");
req = intercept_request__unpack(NULL, req_len, buf);
if (req == NULL) {
sudo_warnx("unable to unpack %s size %zu", "InterceptRequest",
(size_t)req_len);
goto done;
}
/* Take back control of the tty, if necessary, for the policy check. */
ttyfd = open(_PATH_TTY, O_RDWR);
if (ttyfd != -1) {
saved_pgrp = tcgetpgrp(ttyfd);
if (saved_pgrp == -1 || tcsetpgrp(ttyfd, getpgid(0)) == -1 ||
tcgetattr(ttyfd, &oterm) == -1) {
close(ttyfd);
ttyfd = -1;
done:
free(buf);
debug_return_ptr(req);
}
/*
* Read a message from sudo_intercept.so and act on it.
*/
static bool
intercept_read(int fd, struct intercept_closure *closure)
{
struct sudo_event_base *base = sudo_ev_get_base(&closure->ev);
InterceptRequest *req;
pid_t saved_pgrp = -1;
struct termios oterm;
bool ret = false;
int ttyfd = -1;
debug_decl(intercept_read, SUDO_DEBUG_EXEC);
req = intercept_recv_request(fd);
if (req == NULL)
goto done;
switch (req->type_case) {
case INTERCEPT_REQUEST__TYPE_POLICY_CHECK_REQ:
if (closure->policy_result != -1) {
/* Only a single policy check request is allowed. */
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"got another PolicyCheckRequest on the socket (%d)",
closure->policy_result);
goto done;
}
}
closure->policy_result = intercept_check_policy(msg->u.policy_check_req,
closure, &closure->errstr);
/* Take back control of the tty, if necessary, for the policy check. */
ttyfd = open(_PATH_TTY, O_RDWR);
if (ttyfd != -1) {
saved_pgrp = tcgetpgrp(ttyfd);
if (saved_pgrp == -1 || tcsetpgrp(ttyfd, getpgid(0)) == -1 ||
tcgetattr(ttyfd, &oterm) == -1) {
close(ttyfd);
ttyfd = -1;
}
}
if (ttyfd != -1) {
(void)tcsetattr(ttyfd, TCSASOFT|TCSAFLUSH, &oterm);
(void)tcsetpgrp(ttyfd, saved_pgrp);
closure->policy_result = intercept_check_policy(req->u.policy_check_req,
closure, &closure->errstr);
if (ttyfd != -1) {
(void)tcsetattr(ttyfd, TCSASOFT|TCSAFLUSH, &oterm);
(void)tcsetpgrp(ttyfd, saved_pgrp);
}
break;
case INTERCEPT_REQUEST__TYPE_HELLO:
if (closure->policy_result != 1) {
/* Only accept hello on a socket with an accepted command. */
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"got ClientHello without an accepted command");
goto done;
}
/* Start listener after first Hello. */
if (intercept_secret == 0 && !prepare_listener(closure))
goto done;
closure->policy_result = 2; /* XXX */
break;
default:
sudo_warnx(U_("unexpected type_case value %d in %s from %s"),
req->type_case, "InterceptRequest", "sudo_intercept.so");
goto done;
}
/* Switch event to write mode for the reply. */
@@ -356,38 +497,37 @@ intercept_read(int fd, struct intercept_closure *closure)
done:
if (ttyfd != -1)
close(ttyfd);
intercept_message__free_unpacked(msg, NULL);
free(buf);
intercept_request__free_unpacked(req, NULL);
debug_return_bool(ret);
}
static bool
fmt_policy_check_result(PolicyCheckResult *res, struct intercept_closure *closure)
fmt_intercept_response(InterceptResponse *resp,
struct intercept_closure *closure)
{
uint32_t msg_len;
uint32_t resp_len;
bool ret = false;
debug_decl(fmt_policy_check_result, SUDO_DEBUG_EXEC);
debug_decl(fmt_intercept_response, SUDO_DEBUG_EXEC);
res->secret = closure->secret;
closure->len = policy_check_result__get_packed_size(res);
closure->len = intercept_response__get_packed_size(resp);
if (closure->len > MESSAGE_SIZE_MAX) {
sudo_warnx(U_("server message too large: %zu"), closure->len);
goto done;
}
/* Wire message size is used for length encoding, precedes message. */
msg_len = closure->len;
closure->len += sizeof(msg_len);
resp_len = closure->len;
closure->len += sizeof(resp_len);
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"size + PolicyCheckResult %zu bytes", closure->len);
"size + InterceptResponse %zu bytes", closure->len);
if ((closure->buf = malloc(closure->len)) == NULL) {
sudo_warnx("%s", U_("unable to allocate memory"));
goto done;
}
memcpy(closure->buf, &msg_len, sizeof(msg_len));
policy_check_result__pack(res, closure->buf + sizeof(msg_len));
memcpy(closure->buf, &resp_len, sizeof(resp_len));
intercept_response__pack(resp, closure->buf + sizeof(resp_len));
ret = true;
@@ -395,11 +535,27 @@ done:
debug_return_bool(ret);
}
static bool
fmt_hello_response(struct intercept_closure *closure)
{
HelloResponse hello_resp = HELLO_RESPONSE__INIT;
InterceptResponse resp = INTERCEPT_RESPONSE__INIT;
debug_decl(fmt_hello_response, SUDO_DEBUG_EXEC);
hello_resp.portno = intercept_listen_port;
hello_resp.secret = intercept_secret;
resp.u.hello_resp = &hello_resp;
resp.type_case = INTERCEPT_RESPONSE__TYPE_HELLO_RESP;
debug_return_bool(fmt_intercept_response(&resp, closure));
}
static bool
fmt_accept_message(struct intercept_closure *closure)
{
PolicyAcceptMessage msg = POLICY_ACCEPT_MESSAGE__INIT;
PolicyCheckResult res = POLICY_CHECK_RESULT__INIT;
InterceptResponse resp = INTERCEPT_RESPONSE__INIT;
size_t n;
debug_decl(fmt_accept_message, SUDO_DEBUG_EXEC);
@@ -413,40 +569,40 @@ fmt_accept_message(struct intercept_closure *closure)
continue;
msg.n_run_envp = n;
res.u.accept_msg = &msg;
res.type_case = POLICY_CHECK_RESULT__TYPE_ACCEPT_MSG;
resp.u.accept_msg = &msg;
resp.type_case = INTERCEPT_RESPONSE__TYPE_ACCEPT_MSG;
debug_return_bool(fmt_policy_check_result(&res, closure));
debug_return_bool(fmt_intercept_response(&resp, closure));
}
static bool
fmt_reject_message(struct intercept_closure *closure)
{
PolicyRejectMessage msg = POLICY_REJECT_MESSAGE__INIT;
PolicyCheckResult res = POLICY_CHECK_RESULT__INIT;
InterceptResponse resp = INTERCEPT_RESPONSE__INIT;
debug_decl(fmt_reject_message, SUDO_DEBUG_EXEC);
msg.reject_message = (char *)closure->errstr;
res.u.reject_msg = &msg;
res.type_case = POLICY_CHECK_RESULT__TYPE_REJECT_MSG;
resp.u.reject_msg = &msg;
resp.type_case = INTERCEPT_RESPONSE__TYPE_REJECT_MSG;
debug_return_bool(fmt_policy_check_result(&res, closure));
debug_return_bool(fmt_intercept_response(&resp, closure));
}
static bool
fmt_error_message(struct intercept_closure *closure)
{
PolicyErrorMessage msg = POLICY_ERROR_MESSAGE__INIT;
PolicyCheckResult res = POLICY_CHECK_RESULT__INIT;
InterceptResponse resp = INTERCEPT_RESPONSE__INIT;
debug_decl(fmt_error_message, SUDO_DEBUG_EXEC);
msg.error_message = (char *)closure->errstr;
res.u.error_msg = &msg;
res.type_case = POLICY_CHECK_RESULT__TYPE_ERROR_MSG;
resp.u.error_msg = &msg;
resp.type_case = INTERCEPT_RESPONSE__TYPE_ERROR_MSG;
debug_return_bool(fmt_policy_check_result(&res, closure));
debug_return_bool(fmt_intercept_response(&resp, closure));
}
/*
@@ -455,13 +611,19 @@ fmt_error_message(struct intercept_closure *closure)
static bool
intercept_write(int fd, struct intercept_closure *closure)
{
size_t rem;
uint8_t *cp;
struct sudo_event_base *evbase = sudo_ev_get_base(&closure->ev);
ssize_t nwritten;
bool ret = false;
uint8_t *cp;
size_t rem;
debug_decl(intercept_write, SUDO_DEBUG_EXEC);
/* XXX - proper state variable */
switch (closure->policy_result) {
case 2:
if (!fmt_hello_response(closure))
goto done;
break;
case 1:
if (!fmt_accept_message(closure))
goto done;
@@ -479,15 +641,55 @@ intercept_write(int fd, struct intercept_closure *closure)
cp = closure->buf;
rem = closure->len;
do {
nwritten = write(fd, cp, rem);
nwritten = send(fd, cp, rem, 0);
if (nwritten == -1) {
sudo_warn("write");
sudo_warn("send");
goto done;
}
cp += nwritten;
rem -= nwritten;
} while (rem > 0);
switch (closure->policy_result) {
case 1:
/* Switch event to read mode for sudo_intercept.so ctor. */
if (sudo_ev_set(&closure->ev, fd, SUDO_EV_READ, intercept_cb, closure) == -1) {
/* This cannot (currently) fail. */
sudo_warn("%s", U_("unable to add event to queue"));
goto done;
}
if (sudo_ev_add(evbase, &closure->ev, NULL, false) == -1) {
sudo_warn("%s", U_("unable to add event to queue"));
goto done;
}
break;
case 2:
if (closure->listen_sock != -1) {
/* Re-use intercept_event for the listener. */
if (sudo_ev_set(&closure->ev, closure->listen_sock, SUDO_EV_READ|SUDO_EV_PERSIST, intercept_accept_cb, closure) == -1) {
/* This cannot (currently) fail. */
sudo_warn("%s", U_("unable to add event to queue"));
goto done;
}
if (sudo_ev_add(evbase, &closure->ev, NULL, false) == -1) {
sudo_warn("%s", U_("unable to add event to queue"));
goto done;
}
close(fd);
/* Reset bits of closure we used for Hello. */
free(closure->buf);
closure->buf = NULL;
closure->len = 0;
closure->listen_sock = -1;
break;
}
FALLTHROUGH;
default:
/* Done with this connection. */
intercept_connection_close(fd, closure);
}
ret = true;
done:
@@ -513,115 +715,40 @@ intercept_cb(int fd, int what, void *v)
break;
}
if (!success || what == SUDO_EV_WRITE) {
intercept_close(fd, closure);
}
if (!success)
intercept_connection_close(fd, closure);
debug_return;
}
/*
* Accept a single fd passed from the child to use for policy checks.
* This acts a bit like accept() in reverse since the client allocates
* the socketpair() that is used for the actual communication.
* Accept a new connection from the client and fill in a client closure.
* Registers a new event for the connection.
*/
void
intercept_fd_cb(int fd, int what, void *v)
static void
intercept_accept_cb(int fd, int what, void *v)
{
struct intercept_closure *closure = NULL;
struct intercept_fd_closure *fdc = v;
struct msghdr msg;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(int))];
} cmsgbuf;
struct cmsghdr *cmsg;
#endif
struct iovec iov[1];
int newfd = -1;
char ch;
debug_decl(intercept_fd_cb, SUDO_DEBUG_EXEC);
struct intercept_closure *closure = v;
struct sudo_event_base *evbase = sudo_ev_get_base(&closure->ev);
struct sockaddr_in sin;
socklen_t sin_len = sizeof(sin);
int client_sock;
debug_decl(intercept_accept_cb, SUDO_DEBUG_EXEC);
/*
* We send a single byte of data along with the fd; some systems
* don't support sending file descriptors without data.
* Note that the intercept fd is *blocking*.
*/
iov[0].iov_base = &ch;
iov[0].iov_len = 1;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
#else
msg.msg_accrights = (caddr_t)&newfd;
msg.msg_accrightslen = sizeof(newfd);
#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
switch (recvmsg(fd, &msg, 0)) {
case -1:
if (errno != EAGAIN && errno != EINTR)
sudo_warn("recvmsg");
goto bad;
case 0:
/* EOF */
goto bad;
default:
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
cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg == NULL) {
sudo_warnx(U_("%s: missing message header"), __func__);
client_sock = accept(fd, (struct sockaddr *)&sin, &sin_len);
if (client_sock == -1) {
sudo_warn("accept");
goto bad;
}
if (cmsg->cmsg_type != SCM_RIGHTS) {
sudo_warnx(U_("%s: expected message type %d, got %d"), __func__,
SCM_RIGHTS, cmsg->cmsg_type);
goto bad;
}
memcpy(&newfd, CMSG_DATA(cmsg), sizeof(newfd));
#else
if (msg.msg_accrightslen != sizeof(newfd)) {
sudo_warnx(U_("%s: missing message header"), __func__);
goto bad;
}
#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
closure = calloc(1, sizeof(*closure));
if (closure == NULL) {
sudo_warnx("%s", U_("unable to allocate memory"));
goto bad;
}
closure->secret = fdc->secret;
closure->details = fdc->details;
if (sudo_ev_set(&closure->ev, newfd, SUDO_EV_READ, intercept_cb, closure) == -1) {
sudo_warn("%s", U_("unable to add event to queue"));
goto bad;
}
if (sudo_ev_add(fdc->evbase, &closure->ev, NULL, false) == -1) {
sudo_warn("%s", U_("unable to add event to queue"));
if (!intercept_setup(client_sock, evbase, closure->details)) {
goto bad;
}
debug_return;
bad:
if (newfd != -1)
close(newfd);
free(closure);
if (client_sock != -1)
close(client_sock);
debug_return;
}

View File

@@ -43,15 +43,11 @@
#include "sudo_exec.h"
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
#include "sudo_rand.h"
/* Note that details and evbase must come first. */
struct exec_closure_nopty {
uint64_t secret;
struct command_details *details;
struct sudo_event_base *evbase;
struct sudo_event *errpipe_event;
struct sudo_event *intercept_event;
struct sudo_event *sigint_event;
struct sudo_event *sigquit_event;
struct sudo_event *sigtstp_event;
@@ -203,13 +199,11 @@ signal_cb_nopty(int signo, int what, void *v)
*/
static void
fill_exec_closure_nopty(struct exec_closure_nopty *ec,
struct command_status *cstat, struct command_details *details,
int intercept_fd, int errfd)
struct command_status *cstat, struct command_details *details, int errfd)
{
debug_decl(fill_exec_closure_nopty, SUDO_DEBUG_EXEC);
/* Fill in the non-event part of the closure. */
ec->secret = arc4random() | ((uint64_t)arc4random() << 32);
ec->ppgrp = getpgrp();
ec->cstat = cstat;
ec->details = details;
@@ -227,17 +221,6 @@ fill_exec_closure_nopty(struct exec_closure_nopty *ec,
sudo_fatal("%s", U_("unable to add event to queue"));
sudo_debug_printf(SUDO_DEBUG_INFO, "error pipe fd %d\n", errfd);
/* Event for sudo_intercept.so (optional). */
if (intercept_fd != -1) {
ec->intercept_event = sudo_ev_alloc(intercept_fd,
SUDO_EV_READ|SUDO_EV_PERSIST, intercept_fd_cb, ec);
if (ec->intercept_event == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
if (sudo_ev_add(ec->evbase, ec->intercept_event, NULL, false) == -1)
sudo_fatal("%s", U_("unable to add event to queue"));
sudo_debug_printf(SUDO_DEBUG_INFO, "intercept fd %d\n", intercept_fd);
}
/* Events for local signals. */
ec->sigint_event = sudo_ev_alloc(SIGINT,
SUDO_EV_SIGINFO, signal_cb_nopty, ec);
@@ -387,7 +370,7 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
* This must be inherited across exec, hence no FD_CLOEXEC.
*/
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_CHILDREN)) {
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, intercept_sv) == -1)
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
sudo_fatal("%s", U_("unable to create sockets"));
}
@@ -454,7 +437,13 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
* Fill in exec closure, allocate event base, signal events and
* the error pipe event.
*/
fill_exec_closure_nopty(&ec, cstat, details, intercept_sv[0], errpipe[0]);
fill_exec_closure_nopty(&ec, cstat, details, errpipe[0]);
/* Create event and closure for intercept mode. */
if (intercept_sv[0] != -1) {
if (!intercept_setup(intercept_sv[0], ec.evbase, details))
exit(EXIT_FAILURE);
}
/* Restore signal mask now that signal handlers are setup. */
sigprocmask(SIG_SETMASK, &oset, NULL);

View File

@@ -47,7 +47,6 @@
#include "sudo_exec.h"
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
#include "sudo_rand.h"
/* Evaluates to true if the event has /dev/tty as its fd. */
#define USERTTY_EVENT(_ev) (sudo_ev_get_fd((_ev)) == io_fds[SFD_USERTTY])
@@ -62,14 +61,11 @@ struct monitor_message {
};
TAILQ_HEAD(monitor_message_list, monitor_message);
/* Note that details and evbase must come first. */
struct exec_closure_pty {
uint64_t secret;
struct command_details *details;
struct sudo_event_base *evbase;
struct sudo_event *backchannel_event;
struct sudo_event *fwdchannel_event;
struct sudo_event *intercept_event;
struct sudo_event *sigint_event;
struct sudo_event *sigquit_event;
struct sudo_event *sigtstp_event;
@@ -1207,13 +1203,11 @@ fwdchannel_cb(int sock, int what, void *v)
*/
static void
fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
struct command_details *details, pid_t ppgrp, int backchannel,
int intercept_fd)
struct command_details *details, pid_t ppgrp, int backchannel)
{
debug_decl(fill_exec_closure_pty, SUDO_DEBUG_EXEC);
/* Fill in the non-event part of the closure. */
ec->secret = arc4random() | ((uint64_t)arc4random() << 32);
ec->cmnd_pid = -1;
ec->ppgrp = ppgrp;
ec->cstat = cstat;
@@ -1239,17 +1233,6 @@ fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
sudo_fatal("%s", U_("unable to add event to queue"));
sudo_debug_printf(SUDO_DEBUG_INFO, "backchannel fd %d\n", backchannel);
/* Event for sudo_intercept.so (optional). */
if (intercept_fd != -1) {
ec->intercept_event = sudo_ev_alloc(intercept_fd,
SUDO_EV_READ|SUDO_EV_PERSIST, intercept_fd_cb, ec);
if (ec->intercept_event == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
if (sudo_ev_add(ec->evbase, ec->intercept_event, NULL, false) == -1)
sudo_fatal("%s", U_("unable to add event to queue"));
sudo_debug_printf(SUDO_DEBUG_INFO, "intercept fd %d\n", intercept_fd);
}
/* Events for local signals. */
ec->sigint_event = sudo_ev_alloc(SIGINT,
SUDO_EV_SIGINFO, signal_cb_pty, ec);
@@ -1409,7 +1392,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
* This must be inherited across exec, hence no FD_CLOEXEC.
*/
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_CHILDREN)) {
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, intercept_sv) == -1)
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
sudo_fatal("%s", U_("unable to create sockets"));
}
@@ -1652,7 +1635,13 @@ exec_pty(struct command_details *details, struct command_status *cstat)
* Fill in exec closure, allocate event base, signal events and
* the backchannel event.
*/
fill_exec_closure_pty(&ec, cstat, details, ppgrp, sv[0], intercept_sv[0]);
fill_exec_closure_pty(&ec, cstat, details, ppgrp, sv[0]);
/* Create event and closure for intercept mode. */
if (intercept_sv[0] != -1) {
if (!intercept_setup(intercept_sv[0], ec.evbase, details))
exit(EXIT_FAILURE);
}
/* Restore signal mask now that signal handlers are setup. */
sigprocmask(SIG_SETMASK, &oset, NULL);

View File

@@ -7,49 +7,139 @@
#endif
#include "intercept.pb-c.h"
void intercept_message__init
(InterceptMessage *message)
void intercept_request__init
(InterceptRequest *message)
{
static const InterceptMessage init_value = INTERCEPT_MESSAGE__INIT;
static const InterceptRequest init_value = INTERCEPT_REQUEST__INIT;
*message = init_value;
}
size_t intercept_message__get_packed_size
(const InterceptMessage *message)
size_t intercept_request__get_packed_size
(const InterceptRequest *message)
{
assert(message->base.descriptor == &intercept_message__descriptor);
assert(message->base.descriptor == &intercept_request__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t intercept_message__pack
(const InterceptMessage *message,
size_t intercept_request__pack
(const InterceptRequest *message,
uint8_t *out)
{
assert(message->base.descriptor == &intercept_message__descriptor);
assert(message->base.descriptor == &intercept_request__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t intercept_message__pack_to_buffer
(const InterceptMessage *message,
size_t intercept_request__pack_to_buffer
(const InterceptRequest *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &intercept_message__descriptor);
assert(message->base.descriptor == &intercept_request__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
InterceptMessage *
intercept_message__unpack
InterceptRequest *
intercept_request__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (InterceptMessage *)
protobuf_c_message_unpack (&intercept_message__descriptor,
return (InterceptRequest *)
protobuf_c_message_unpack (&intercept_request__descriptor,
allocator, len, data);
}
void intercept_message__free_unpacked
(InterceptMessage *message,
void intercept_request__free_unpacked
(InterceptRequest *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &intercept_message__descriptor);
assert(message->base.descriptor == &intercept_request__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
void client_hello__init
(ClientHello *message)
{
static const ClientHello init_value = CLIENT_HELLO__INIT;
*message = init_value;
}
size_t client_hello__get_packed_size
(const ClientHello *message)
{
assert(message->base.descriptor == &client_hello__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t client_hello__pack
(const ClientHello *message,
uint8_t *out)
{
assert(message->base.descriptor == &client_hello__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t client_hello__pack_to_buffer
(const ClientHello *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &client_hello__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
ClientHello *
client_hello__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (ClientHello *)
protobuf_c_message_unpack (&client_hello__descriptor,
allocator, len, data);
}
void client_hello__free_unpacked
(ClientHello *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &client_hello__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
void hello_response__init
(HelloResponse *message)
{
static const HelloResponse init_value = HELLO_RESPONSE__INIT;
*message = init_value;
}
size_t hello_response__get_packed_size
(const HelloResponse *message)
{
assert(message->base.descriptor == &hello_response__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t hello_response__pack
(const HelloResponse *message,
uint8_t *out)
{
assert(message->base.descriptor == &hello_response__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t hello_response__pack_to_buffer
(const HelloResponse *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &hello_response__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
HelloResponse *
hello_response__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (HelloResponse *)
protobuf_c_message_unpack (&hello_response__descriptor,
allocator, len, data);
}
void hello_response__free_unpacked
(HelloResponse *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &hello_response__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
void policy_check_request__init
@@ -232,90 +322,192 @@ void policy_error_message__free_unpacked
assert(message->base.descriptor == &policy_error_message__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
void policy_check_result__init
(PolicyCheckResult *message)
void intercept_response__init
(InterceptResponse *message)
{
static const PolicyCheckResult init_value = POLICY_CHECK_RESULT__INIT;
static const InterceptResponse init_value = INTERCEPT_RESPONSE__INIT;
*message = init_value;
}
size_t policy_check_result__get_packed_size
(const PolicyCheckResult *message)
size_t intercept_response__get_packed_size
(const InterceptResponse *message)
{
assert(message->base.descriptor == &policy_check_result__descriptor);
assert(message->base.descriptor == &intercept_response__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t policy_check_result__pack
(const PolicyCheckResult *message,
size_t intercept_response__pack
(const InterceptResponse *message,
uint8_t *out)
{
assert(message->base.descriptor == &policy_check_result__descriptor);
assert(message->base.descriptor == &intercept_response__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t policy_check_result__pack_to_buffer
(const PolicyCheckResult *message,
size_t intercept_response__pack_to_buffer
(const InterceptResponse *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &policy_check_result__descriptor);
assert(message->base.descriptor == &intercept_response__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
PolicyCheckResult *
policy_check_result__unpack
InterceptResponse *
intercept_response__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (PolicyCheckResult *)
protobuf_c_message_unpack (&policy_check_result__descriptor,
return (InterceptResponse *)
protobuf_c_message_unpack (&intercept_response__descriptor,
allocator, len, data);
}
void policy_check_result__free_unpacked
(PolicyCheckResult *message,
void intercept_response__free_unpacked
(InterceptResponse *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &policy_check_result__descriptor);
assert(message->base.descriptor == &intercept_response__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
static const ProtobufCFieldDescriptor intercept_message__field_descriptors[1] =
static const ProtobufCFieldDescriptor intercept_request__field_descriptors[2] =
{
{
"policy_check_req",
1,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_MESSAGE,
offsetof(InterceptMessage, type_case),
offsetof(InterceptMessage, u.policy_check_req),
offsetof(InterceptRequest, type_case),
offsetof(InterceptRequest, u.policy_check_req),
&policy_check_request__descriptor,
NULL,
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"hello",
2,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_MESSAGE,
offsetof(InterceptRequest, type_case),
offsetof(InterceptRequest, u.hello),
&client_hello__descriptor,
NULL,
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned intercept_message__field_indices_by_name[] = {
static const unsigned intercept_request__field_indices_by_name[] = {
1, /* field[1] = hello */
0, /* field[0] = policy_check_req */
};
static const ProtobufCIntRange intercept_message__number_ranges[1 + 1] =
static const ProtobufCIntRange intercept_request__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 2 }
};
const ProtobufCMessageDescriptor intercept_request__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"InterceptRequest",
"InterceptRequest",
"InterceptRequest",
"",
sizeof(InterceptRequest),
2,
intercept_request__field_descriptors,
intercept_request__field_indices_by_name,
1, intercept_request__number_ranges,
(ProtobufCMessageInit) intercept_request__init,
NULL,NULL,NULL /* reserved[123] */
};
static const ProtobufCFieldDescriptor client_hello__field_descriptors[1] =
{
{
"pid",
1,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_INT32,
0, /* quantifier_offset */
offsetof(ClientHello, pid),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned client_hello__field_indices_by_name[] = {
0, /* field[0] = pid */
};
static const ProtobufCIntRange client_hello__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 1 }
};
const ProtobufCMessageDescriptor intercept_message__descriptor =
const ProtobufCMessageDescriptor client_hello__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"InterceptMessage",
"InterceptMessage",
"InterceptMessage",
"ClientHello",
"ClientHello",
"ClientHello",
"",
sizeof(InterceptMessage),
sizeof(ClientHello),
1,
intercept_message__field_descriptors,
intercept_message__field_indices_by_name,
1, intercept_message__number_ranges,
(ProtobufCMessageInit) intercept_message__init,
client_hello__field_descriptors,
client_hello__field_indices_by_name,
1, client_hello__number_ranges,
(ProtobufCMessageInit) client_hello__init,
NULL,NULL,NULL /* reserved[123] */
};
static const ProtobufCFieldDescriptor policy_check_request__field_descriptors[3] =
static const ProtobufCFieldDescriptor hello_response__field_descriptors[2] =
{
{
"secret",
1,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_FIXED64,
0, /* quantifier_offset */
offsetof(HelloResponse, secret),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"portno",
2,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_INT32,
0, /* quantifier_offset */
offsetof(HelloResponse, portno),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned hello_response__field_indices_by_name[] = {
1, /* field[1] = portno */
0, /* field[0] = secret */
};
static const ProtobufCIntRange hello_response__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 2 }
};
const ProtobufCMessageDescriptor hello_response__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"HelloResponse",
"HelloResponse",
"HelloResponse",
"",
sizeof(HelloResponse),
2,
hello_response__field_descriptors,
hello_response__field_indices_by_name,
1, hello_response__number_ranges,
(ProtobufCMessageInit) hello_response__init,
NULL,NULL,NULL /* reserved[123] */
};
static const ProtobufCFieldDescriptor policy_check_request__field_descriptors[5] =
{
{
"command",
@@ -353,16 +545,42 @@ static const ProtobufCFieldDescriptor policy_check_request__field_descriptors[3]
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"intercept_fd",
4,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_INT32,
0, /* quantifier_offset */
offsetof(PolicyCheckRequest, intercept_fd),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"secret",
5,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_FIXED64,
0, /* quantifier_offset */
offsetof(PolicyCheckRequest, secret),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned policy_check_request__field_indices_by_name[] = {
1, /* field[1] = argv */
0, /* field[0] = command */
2, /* field[2] = envp */
3, /* field[3] = intercept_fd */
4, /* field[4] = secret */
};
static const ProtobufCIntRange policy_check_request__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 3 }
{ 0, 5 }
};
const ProtobufCMessageDescriptor policy_check_request__descriptor =
{
@@ -372,7 +590,7 @@ const ProtobufCMessageDescriptor policy_check_request__descriptor =
"PolicyCheckRequest",
"",
sizeof(PolicyCheckRequest),
3,
5,
policy_check_request__field_descriptors,
policy_check_request__field_indices_by_name,
1, policy_check_request__number_ranges,
@@ -519,15 +737,27 @@ const ProtobufCMessageDescriptor policy_error_message__descriptor =
(ProtobufCMessageInit) policy_error_message__init,
NULL,NULL,NULL /* reserved[123] */
};
static const ProtobufCFieldDescriptor policy_check_result__field_descriptors[4] =
static const ProtobufCFieldDescriptor intercept_response__field_descriptors[4] =
{
{
"accept_msg",
"hello_resp",
1,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_MESSAGE,
offsetof(PolicyCheckResult, type_case),
offsetof(PolicyCheckResult, u.accept_msg),
offsetof(InterceptResponse, type_case),
offsetof(InterceptResponse, u.hello_resp),
&hello_response__descriptor,
NULL,
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"accept_msg",
2,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_MESSAGE,
offsetof(InterceptResponse, type_case),
offsetof(InterceptResponse, u.accept_msg),
&policy_accept_message__descriptor,
NULL,
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
@@ -535,11 +765,11 @@ static const ProtobufCFieldDescriptor policy_check_result__field_descriptors[4]
},
{
"reject_msg",
2,
3,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_MESSAGE,
offsetof(PolicyCheckResult, type_case),
offsetof(PolicyCheckResult, u.reject_msg),
offsetof(InterceptResponse, type_case),
offsetof(InterceptResponse, u.reject_msg),
&policy_reject_message__descriptor,
NULL,
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
@@ -547,52 +777,40 @@ static const ProtobufCFieldDescriptor policy_check_result__field_descriptors[4]
},
{
"error_msg",
3,
4,
PROTOBUF_C_LABEL_NONE,
PROTOBUF_C_TYPE_MESSAGE,
offsetof(PolicyCheckResult, type_case),
offsetof(PolicyCheckResult, u.error_msg),
offsetof(InterceptResponse, type_case),
offsetof(InterceptResponse, u.error_msg),
&policy_error_message__descriptor,
NULL,
0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */
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[] = {
0, /* field[0] = accept_msg */
2, /* field[2] = error_msg */
1, /* field[1] = reject_msg */
3, /* field[3] = secret */
static const unsigned intercept_response__field_indices_by_name[] = {
1, /* field[1] = accept_msg */
3, /* field[3] = error_msg */
0, /* field[0] = hello_resp */
2, /* field[2] = reject_msg */
};
static const ProtobufCIntRange policy_check_result__number_ranges[1 + 1] =
static const ProtobufCIntRange intercept_response__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 4 }
};
const ProtobufCMessageDescriptor policy_check_result__descriptor =
const ProtobufCMessageDescriptor intercept_response__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"PolicyCheckResult",
"PolicyCheckResult",
"PolicyCheckResult",
"InterceptResponse",
"InterceptResponse",
"InterceptResponse",
"",
sizeof(PolicyCheckResult),
sizeof(InterceptResponse),
4,
policy_check_result__field_descriptors,
policy_check_result__field_indices_by_name,
1, policy_check_result__number_ranges,
(ProtobufCMessageInit) policy_check_result__init,
intercept_response__field_descriptors,
intercept_response__field_indices_by_name,
1, intercept_response__number_ranges,
(ProtobufCMessageInit) intercept_response__init,
NULL,NULL,NULL /* reserved[123] */
};

View File

@@ -4,14 +4,33 @@ syntax = "proto3";
* Intercept message from sudo_intercept.so. Messages on the
* wire are prefixed with a 32-bit size in network byte order.
*/
message InterceptMessage {
message InterceptRequest {
oneof type {
PolicyCheckRequest policy_check_req = 1;
ClientHello hello = 2;
}
}
/*
* Hello message from sudo_intercept.so to main sudo process.
* Sudo sends back the secret and localhost port number.
*/
message ClientHello {
int32 pid = 1;
}
/*
* Sudo response to a ClientHello from sudo_intercept.so.
* The client uses the port number and secret to connect back to sudo.
*/
message HelloResponse {
fixed64 secret = 1;
int32 portno = 2;
}
/*
* Policy check request from sudo_intercept.so.
* Must include the correct secret value.
* Note that the plugin API only currently supports passing
* the new environment in to the open() function.
*/
@@ -19,6 +38,8 @@ message PolicyCheckRequest {
string command = 1;
repeated string argv = 2;
repeated string envp = 3;
int32 intercept_fd = 4;
fixed64 secret = 5;
}
message PolicyAcceptMessage {
@@ -36,13 +57,13 @@ message PolicyErrorMessage {
}
/*
* Policy check result sent back to sudo_intercept.so.
* Response sent back to sudo_intercept.so.
*/
message PolicyCheckResult {
message InterceptResponse {
oneof type {
PolicyAcceptMessage accept_msg = 1;
PolicyRejectMessage reject_msg = 2;
PolicyErrorMessage error_msg = 3;
HelloResponse hello_resp = 1;
PolicyAcceptMessage accept_msg = 2;
PolicyRejectMessage reject_msg = 3;
PolicyErrorMessage error_msg = 4;
}
fixed64 secret = 4;
}

View File

@@ -80,7 +80,6 @@
#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_REQ_SEC 42 /* request intercept secret */
#define MESSAGE_SIZE_MAX 2097152 /* 2Mib max intercept message size */
/*
@@ -88,19 +87,22 @@
*/
struct command_details;
struct command_status;
struct sudo_event_base;
struct stat;
/* exec.c */
void exec_cmnd(struct command_details *details, int intercept_fd, int errfd);
void terminate_command(pid_t pid, bool use_pgrp);
bool sudo_terminated(struct command_status *cstat);
void intercept_fd_cb(int fd, int what, void *v);
/* exec_common.c */
int sudo_execve(int fd, const char *path, char *const argv[], char *envp[], int intercept_fd, int flags);
char **disable_execute(char *envp[], const char *dso);
char **enable_monitor(char *envp[], const char *dso);
/* exec_intercept.c */
bool intercept_setup(int fd, struct sudo_event_base *evbase, struct command_details *details);
/* exec_nopty.c */
void exec_nopty(struct command_details *details, struct command_status *cstat);

View File

@@ -25,6 +25,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#if defined(HAVE_STDINT_H)
# include <stdint.h>
@@ -57,102 +58,49 @@
extern char **environ;
static int intercept_sock = -1;
static uint64_t secret;
static in_port_t intercept_port;
/*
* Look up SUDO_INTERCEPT_FD in the environment.
* This function is run when the shared library is loaded.
*/
__attribute__((constructor)) static void
sudo_interposer_init(void)
/* Send entire request to sudo (blocking). */
static bool
send_req(int sock, const uint8_t *buf, size_t len)
{
static bool initialized;
char **p;
debug_decl(sudo_interposer_init, SUDO_DEBUG_EXEC);
const uint8_t *cp = buf;
ssize_t nwritten;
debug_decl(send_req, SUDO_DEBUG_EXEC);
if (!initialized) {
initialized = true;
/* Read debug section of sudo.conf and init debugging. */
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) != -1) {
sudo_debug_register("sudo_intercept.so", NULL, NULL,
sudo_conf_debug_files("sudo_intercept.so"));
do {
nwritten = send(sock, cp, len, 0);
if (nwritten == -1) {
debug_return_bool(false);
}
sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys);
len -= nwritten;
cp += nwritten;
} while (len > 0);
/*
* Missing SUDO_INTERCEPT_FD will result in execve() failure.
* Note that we cannot use getenv(3) here on Linux at least.
*/
for (p = environ; *p != NULL; p++) {
if (strncmp(*p, "SUDO_INTERCEPT_FD=", sizeof("SUDO_INTERCEPT_FD=") -1) == 0) {
const char *fdstr = *p + sizeof("SUDO_INTERCEPT_FD=") - 1;
const char *errstr;
char ch = INTERCEPT_REQ_SEC;
int fd;
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "%s", *p);
fd = sudo_strtonum(fdstr, 0, INT_MAX, &errstr);
if (errstr != NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"invalid SUDO_INTERCEPT_FD: %s: %s", fdstr, errstr);
debug_return;
}
/* 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 on fd %d: %s", fd,
strerror(errno));
debug_return;
}
if (recv(fd, &secret, sizeof(secret), 0) != sizeof(secret)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to read secret on fd %d: %s", fd,
strerror(errno));
debug_return;
}
intercept_sock = fd;
debug_return;
}
}
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"SUDO_INTERCEPT_FD not found in environment");
}
debug_return;
debug_return_bool(true);
}
static uint8_t *
fmt_policy_check_req(const char *cmnd, char * const argv[], char * const envp[],
size_t *buflen)
static bool
send_client_hello(int sock)
{
InterceptMessage msg = INTERCEPT_MESSAGE__INIT;
PolicyCheckRequest req = POLICY_CHECK_REQUEST__INIT;
InterceptRequest msg = INTERCEPT_REQUEST__INIT;
ClientHello hello = CLIENT_HELLO__INIT;
uint8_t *buf = NULL;
uint32_t msg_len;
size_t len;
debug_decl(sudo_interposer_init, SUDO_DEBUG_EXEC);
bool ret = false;
debug_decl(send_client_hello, SUDO_DEBUG_EXEC);
/* Setup policy check request. */
req.command = (char *)cmnd;
req.argv = (char **)argv;
for (len = 0; argv[len] != NULL; len++)
continue;
req.n_argv = len;
req.envp = (char **)envp;
for (len = 0; envp[len] != NULL; len++)
continue;
req.n_envp = len;
msg.type_case = INTERCEPT_MESSAGE__TYPE_POLICY_CHECK_REQ;
msg.u.policy_check_req = &req;
hello.pid = getpid();
msg.type_case = INTERCEPT_REQUEST__TYPE_HELLO;;
msg.u.hello = &hello;
len = intercept_message__get_packed_size(&msg);
len = intercept_request__get_packed_size(&msg);
if (len > MESSAGE_SIZE_MAX) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"InterceptMessage too large: %zu", len);
"InterceptRequest too large: %zu", len);
goto done;
}
/* Wire message size is used for length encoding, precedes message. */
@@ -164,78 +112,239 @@ fmt_policy_check_req(const char *cmnd, char * const argv[], char * const envp[],
goto done;
}
memcpy(buf, &msg_len, sizeof(msg_len));
intercept_message__pack(&msg, buf + sizeof(msg_len));
*buflen = len;
intercept_request__pack(&msg, buf + sizeof(msg_len));
ret = send_req(sock, buf, len);
done:
debug_return_ptr(buf);
free(buf);
debug_return_bool(ret);
}
/* Send fd over a unix domain socket. */
static bool
intercept_send_fd(int sock, int fd)
/*
* Receive HelloResponse from sudo over fd.
*/
InterceptResponse *
recv_intercept_response(int fd)
{
struct msghdr msg;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(int))];
} cmsgbuf;
struct cmsghdr *cmsg;
#endif
struct iovec iov[1];
char ch = '\0';
ssize_t nsent;
debug_decl(intercept_send_fd, SUDO_DEBUG_EXEC);
InterceptResponse *res = NULL;
ssize_t nread;
uint32_t res_len;
uint8_t *buf = NULL;
debug_decl(recv_intercept_response, SUDO_DEBUG_EXEC);
/* Read message size (uint32_t in host byte order). */
nread = recv(fd, &res_len, sizeof(res_len), 0);
if ((size_t)nread != sizeof(res_len)) {
if (nread == 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected EOF reading response size");
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"error reading response size");
}
goto done;
}
if (res_len > MESSAGE_SIZE_MAX) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"InterceptResponse too large: %u", res_len);
goto done;
}
/* Read response from sudo (blocking). */
if ((buf = malloc(res_len)) == NULL) {
goto done;
}
nread = recv(fd, buf, res_len, 0);
if ((size_t)nread != res_len) {
if (nread == 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected EOF reading response");
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"error reading response");
}
goto done;
}
res = intercept_response__unpack(NULL, res_len, buf);
if (res == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to unpack %s size %u", "InterceptResponse", res_len);
goto done;
}
done:
free(buf);
debug_return_ptr(res);
}
/*
* Look up SUDO_INTERCEPT_FD in the environment.
* This function is run when the shared library is loaded.
*/
__attribute__((constructor)) static void
sudo_interposer_init(void)
{
InterceptResponse *res = NULL;
static bool initialized;
int fd = -1;
char **p;
debug_decl(sudo_interposer_init, SUDO_DEBUG_EXEC);
if (initialized)
debug_return;
initialized = true;
/* Read debug section of sudo.conf and init debugging. */
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) != -1) {
sudo_debug_register("sudo_intercept.so", NULL, NULL,
sudo_conf_debug_files("sudo_intercept.so"));
}
sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys);
/*
* We send a single byte of data along with the fd; some systems
* don't support sending file descriptors without data.
* Note that the intercept fd is *blocking*.
*/
iov[0].iov_base = &ch;
iov[0].iov_len = 1;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
#else
msg.msg_accrights = (caddr_t)&fd;
msg.msg_accrightslen = sizeof(fd);
#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
* Missing SUDO_INTERCEPT_FD will result in execve() failure.
* Note that we cannot use getenv(3) here on Linux at least.
*/
for (p = environ; *p != NULL; p++) {
if (strncmp(*p, "SUDO_INTERCEPT_FD=", sizeof("SUDO_INTERCEPT_FD=") -1) == 0) {
const char *fdstr = *p + sizeof("SUDO_INTERCEPT_FD=") - 1;
const char *errstr;
for (;;) {
nsent = sendmsg(sock, &msg, 0);
if (nsent != -1)
debug_return_bool(true);
if (errno != EAGAIN && errno != EINTR)
break;
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "%s", *p);
fd = sudo_strtonum(fdstr, 0, INT_MAX, &errstr);
if (errstr != NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"invalid SUDO_INTERCEPT_FD: %s: %s", fdstr, errstr);
goto done;
}
}
}
sudo_warn("sendmsg(%d)", sock);
debug_return_bool(false);
if (fd == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"SUDO_INTERCEPT_FD not found in environment");
goto done;
}
/*
* Send ClientHello message to over the fd.
*/
if (!send_client_hello(fd))
goto done;
res = recv_intercept_response(fd);
if (res != NULL) {
secret = res->u.hello_resp->secret;
intercept_port = res->u.hello_resp->portno;
intercept_response__free_unpacked(res, NULL);
}
done:
if (fd != -1)
close(fd);
debug_return;
}
static bool
send_policy_check_req(int sock, const char *cmnd, char * const argv[],
char * const envp[])
{
InterceptRequest msg = INTERCEPT_REQUEST__INIT;
PolicyCheckRequest req = POLICY_CHECK_REQUEST__INIT;
uint8_t *buf = NULL;
bool ret = false;
uint32_t msg_len;
size_t len;
debug_decl(fmt_policy_check_req, SUDO_DEBUG_EXEC);
/* Setup policy check request. */
req.secret = secret;
req.intercept_fd = sock;
req.command = (char *)cmnd;
req.argv = (char **)argv;
for (len = 0; argv[len] != NULL; len++)
continue;
req.n_argv = len;
req.envp = (char **)envp;
for (len = 0; envp[len] != NULL; len++)
continue;
req.n_envp = len;
msg.type_case = INTERCEPT_REQUEST__TYPE_POLICY_CHECK_REQ;
msg.u.policy_check_req = &req;
len = intercept_request__get_packed_size(&msg);
if (len > MESSAGE_SIZE_MAX) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"InterceptRequest too large: %zu", len);
goto done;
}
/* Wire message size is used for length encoding, precedes message. */
msg_len = len;
len += sizeof(msg_len);
if ((buf = malloc(len)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
memcpy(buf, &msg_len, sizeof(msg_len));
intercept_request__pack(&msg, buf + sizeof(msg_len));
ret = send_req(sock, buf, len);
done:
free(buf);
debug_return_bool(ret);
}
/*
* Connect back to sudo process at localhost:intercept_port
*/
static int
intercept_connect(void)
{
int sock = -1;
struct sockaddr_in sin;
debug_decl(command_allowed, SUDO_DEBUG_EXEC);
if (intercept_port == 0) {
sudo_warnx(U_("intercept port not set"));
goto done;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sin.sin_port = htons(intercept_port);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
sudo_warn("socket");
goto done;
}
if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
sudo_warn("connect");
close(sock);
sock = -1;
goto done;
}
done:
debug_return_int(sock);
}
bool
command_allowed(const char *cmnd, char * const argv[], char * const envp[],
char **ncmndp, char ***nargvp, char ***nenvpp)
command_allowed(const char *cmnd, char * const argv[],
char * const envp[], char **ncmndp, char ***nargvp, char ***nenvpp)
{
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
PolicyCheckResult *res = NULL;
int sv[2] = { -1, -1 };
ssize_t nread, nwritten;
uint8_t *cp, *buf = NULL;
InterceptResponse *res = NULL;
bool ret = false;
uint32_t res_len;
size_t idx, len;
debug_decl(intercept_send_fd, SUDO_DEBUG_EXEC);
size_t idx, len = 0;
int sock;
debug_decl(command_allowed, SUDO_DEBUG_EXEC);
if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
@@ -246,97 +355,19 @@ command_allowed(const char *cmnd, char * const argv[], char * const envp[],
}
}
if (intercept_sock < INTERCEPT_FD_MIN) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"invalid intercept fd: %d", intercept_sock);
errno = EINVAL;
goto done;
}
if (fcntl(intercept_sock, F_GETFD, 0) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"intercept fd %d not open", intercept_sock);
errno = EINVAL;
goto done;
}
/*
* We communicate with the main sudo process over a socket pair
* which is passed over the intercept_sock. The reason for not
* using intercept_sock directly is that multiple processes
* could be trying to use it at once. Sending an fd like this
* is atomic but regular communication is not.
*/
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
sudo_warn("socketpair");
goto done;
}
if (!intercept_send_fd(intercept_sock, sv[1]))
goto done;
close(sv[1]);
sv[1] = -1;
buf = fmt_policy_check_req(cmnd, argv, envp, &len);
if (buf == NULL)
sock = intercept_connect();
if (sock == -1)
goto done;
/* Send request to sudo (blocking). */
cp = buf;
do {
nwritten = write(sv[0], cp, len);
if (nwritten == -1) {
goto done;
}
len -= nwritten;
cp += nwritten;
} while (len > 0);
free(buf);
buf = NULL;
/* Read message size (uint32_t in host byte order). */
nread = read(sv[0], &res_len, sizeof(res_len));
if ((size_t)nread != sizeof(res_len)) {
if (nread == 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected EOF reading result size");
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"error reading result size");
}
goto done;
}
if (res_len > MESSAGE_SIZE_MAX) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"PolicyCheckResult too large: %zu", len);
goto done;
}
/* Read result from sudo (blocking). */
if ((buf = malloc(res_len)) == NULL) {
if (!send_policy_check_req(sock, cmnd, argv, envp))
goto done;
}
nread = read(sv[0], buf, res_len);
if ((size_t)nread != res_len) {
if (nread == 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected EOF reading result");
} else {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"error reading result");
}
goto done;
}
res = policy_check_result__unpack(NULL, res_len, buf);
if (res == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to unpack %s size %u", "PolicyCheckResult", res_len);
goto done;
}
if (res->secret != secret) {
sudo_warnx("secret mismatch\r");
res = recv_intercept_response(sock);
if (res == NULL)
goto done;
}
switch (res->type_case) {
case POLICY_CHECK_RESULT__TYPE_ACCEPT_MSG:
case INTERCEPT_RESPONSE__TYPE_ACCEPT_MSG:
if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"run_command: %s", res->u.accept_msg->run_command);
@@ -360,7 +391,7 @@ command_allowed(const char *cmnd, char * const argv[], char * const envp[],
nargv[len] = NULL;
// XXX - bogus cast
nenvp = sudo_preload_dso((char **)envp, sudo_conf_intercept_path(),
intercept_sock);
sock);
if (nenvp == NULL)
goto oom;
*ncmndp = ncmnd;
@@ -368,11 +399,11 @@ command_allowed(const char *cmnd, char * const argv[], char * const envp[],
*nenvpp = nenvp;
ret = true;
goto done;
case POLICY_CHECK_RESULT__TYPE_REJECT_MSG:
case INTERCEPT_RESPONSE__TYPE_REJECT_MSG:
/* Policy module displayed reject message but we are in raw mode. */
fputc('\r', stderr);
goto done;
case POLICY_CHECK_RESULT__TYPE_ERROR_MSG:
case INTERCEPT_RESPONSE__TYPE_ERROR_MSG:
/* Policy module may display error message but we are in raw mode. */
fputc('\r', stderr);
sudo_warnx("%s", res->u.error_msg->error_message);
@@ -380,7 +411,7 @@ command_allowed(const char *cmnd, char * const argv[], char * const envp[],
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected type_case value %d in %s from %s",
res->type_case, "PolicyCheckResult", "sudo");
res->type_case, "InterceptResponse", "sudo");
goto done;
}
@@ -390,12 +421,10 @@ oom:
free(nargv[--len]);
done:
policy_check_result__free_unpacked(res, NULL);
if (sv[0] != -1)
close(sv[0]);
if (sv[1] != -1)
close(sv[1]);
free(buf);
/* Keep socket open for ctor when we execute the command. */
if (!ret && sock != -1)
close(sock);
intercept_response__free_unpacked(res, NULL);
debug_return_bool(ret);
}