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'. */ /* Define to 1 if the system has the type `struct in6_addr'. */
#undef HAVE_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'. */ /* Define to 1 if `pr_ttydev' is a member of `struct psinfo'. */
#undef HAVE_STRUCT_PSINFO_PR_TTYDEV #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 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 fi
openssl_missing=no openssl_missing=no

View File

@@ -2925,13 +2925,6 @@ AC_INCLUDES_DEFAULT
#include <$ac_header_dirent> #include <$ac_header_dirent>
]) ])
dnl 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 Check for functions only present in OpenSSL 1.1 and above
dnl dnl
openssl_missing=no openssl_missing=no

View File

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

View File

@@ -24,7 +24,7 @@
#include <config.h> #include <config.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/uio.h> #include <netinet/in.h>
#if defined(HAVE_STDINT_H) #if defined(HAVE_STDINT_H)
# include <stdint.h> # include <stdint.h>
@@ -42,6 +42,7 @@
#include "sudo_exec.h" #include "sudo_exec.h"
#include "sudo_plugin.h" #include "sudo_plugin.h"
#include "sudo_plugin_int.h" #include "sudo_plugin_int.h"
#include "sudo_rand.h"
#include "intercept.pb-c.h" #include "intercept.pb-c.h"
/* TCSASOFT is a BSD extension that ignores control flags and speed. */ /* TCSASOFT is a BSD extension that ignores control flags and speed. */
@@ -49,15 +50,6 @@
# define TCSASOFT 0 # define TCSASOFT 0
#endif #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() */ /* Closure for intercept_cb() */
struct intercept_closure { struct intercept_closure {
struct command_details *details; struct command_details *details;
@@ -67,23 +59,68 @@ 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 listen_sock;
int policy_result; 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. * Close intercept socket and free closure when we are done with
* Called on EOF from sudo_intercept.so due to program exit. * the connection.
*/ */
static void static void
intercept_close(int fd, struct intercept_closure *closure) intercept_connection_close(int fd, struct intercept_closure *closure)
{ {
size_t n; size_t n;
debug_decl(intercept_close, SUDO_DEBUG_EXEC); debug_decl(intercept_connection_close, SUDO_DEBUG_EXEC);
sudo_ev_del(NULL, &closure->ev); sudo_ev_del(NULL, &closure->ev);
close(fd); close(fd);
if (closure->listen_sock != -1)
close(closure->listen_sock);
free(closure->buf); free(closure->buf);
free(closure->command); free(closure->command);
@@ -102,6 +139,59 @@ intercept_close(int fd, struct intercept_closure *closure)
debug_return; 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 static int
intercept_check_policy(PolicyCheckRequest *req, intercept_check_policy(PolicyCheckRequest *req,
struct intercept_closure *closure, const char **errstr) struct intercept_closure *closure, const char **errstr)
@@ -117,6 +207,13 @@ intercept_check_policy(PolicyCheckRequest *req,
*errstr = N_("invalid PolicyCheckRequest"); *errstr = N_("invalid PolicyCheckRequest");
goto bad; 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)) { if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, 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 static InterceptRequest *
intercept_read(int fd, struct intercept_closure *closure) intercept_recv_request(int fd)
{ {
struct sudo_event_base *base = sudo_ev_get_base(&closure->ev); InterceptRequest *req = NULL;
InterceptMessage *msg = NULL;
uint8_t *cp, *buf = NULL; uint8_t *cp, *buf = NULL;
pid_t saved_pgrp = -1; uint32_t req_len;
struct termios oterm;
uint32_t msg_len;
bool ret = false;
int ttyfd = -1;
ssize_t nread; 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). */ /* Read message size (uint32_t in host byte order). */
nread = read(fd, &msg_len, sizeof(msg_len)); nread = recv(fd, &req_len, sizeof(req_len), 0);
if (nread != sizeof(msg_len)) { if (nread != sizeof(req_len)) {
if (nread != 0) if (nread != 0)
sudo_warn("read"); sudo_warn("read");
goto done; goto done;
} }
if (msg_len > MESSAGE_SIZE_MAX) { if (req_len > MESSAGE_SIZE_MAX) {
sudo_warnx(U_("client message too large: %zu"), (size_t)msg_len); sudo_warnx(U_("client request too large: %zu"), (size_t)req_len);
goto done; goto done;
} }
if (msg_len > 0) { if (req_len > 0) {
size_t rem = msg_len; 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")); sudo_warnx("%s", U_("unable to allocate memory"));
goto done; goto done;
} }
cp = buf; cp = buf;
do { do {
nread = read(fd, cp, rem); nread = recv(fd, cp, rem, 0);
switch (nread) { switch (nread) {
case 0: case 0:
/* EOF, other side must have exited. */ /* EOF, other side must have exited. */
@@ -309,15 +402,43 @@ intercept_read(int fd, struct intercept_closure *closure)
} while (rem > 0); } while (rem > 0);
} }
msg = intercept_message__unpack(NULL, msg_len, buf); req = intercept_request__unpack(NULL, req_len, buf);
if (msg == NULL) { if (req == NULL) {
sudo_warnx("unable to unpack %s size %zu", "InterceptMessage", sudo_warnx("unable to unpack %s size %zu", "InterceptRequest",
(size_t)msg_len); (size_t)req_len);
goto done; goto done;
} }
if (msg->type_case != INTERCEPT_MESSAGE__TYPE_POLICY_CHECK_REQ) {
sudo_warnx(U_("unexpected type_case value %d in %s from %s"), done:
msg->type_case, "InterceptMessage", "sudo_intercept.so"); 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; goto done;
} }
@@ -332,13 +453,33 @@ intercept_read(int fd, struct intercept_closure *closure)
} }
} }
closure->policy_result = intercept_check_policy(msg->u.policy_check_req, closure->policy_result = intercept_check_policy(req->u.policy_check_req,
closure, &closure->errstr); closure, &closure->errstr);
if (ttyfd != -1) { if (ttyfd != -1) {
(void)tcsetattr(ttyfd, TCSASOFT|TCSAFLUSH, &oterm); (void)tcsetattr(ttyfd, TCSASOFT|TCSAFLUSH, &oterm);
(void)tcsetpgrp(ttyfd, saved_pgrp); (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. */ /* Switch event to write mode for the reply. */
if (sudo_ev_set(&closure->ev, fd, SUDO_EV_WRITE, intercept_cb, closure) == -1) { if (sudo_ev_set(&closure->ev, fd, SUDO_EV_WRITE, intercept_cb, closure) == -1) {
@@ -356,38 +497,37 @@ intercept_read(int fd, struct intercept_closure *closure)
done: done:
if (ttyfd != -1) if (ttyfd != -1)
close(ttyfd); close(ttyfd);
intercept_message__free_unpacked(msg, NULL); intercept_request__free_unpacked(req, NULL);
free(buf);
debug_return_bool(ret); debug_return_bool(ret);
} }
static bool 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; 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 = intercept_response__get_packed_size(resp);
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;
} }
/* Wire message size is used for length encoding, precedes message. */ /* Wire message size is used for length encoding, precedes message. */
msg_len = closure->len; resp_len = closure->len;
closure->len += sizeof(msg_len); closure->len += sizeof(resp_len);
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, 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) { if ((closure->buf = malloc(closure->len)) == NULL) {
sudo_warnx("%s", U_("unable to allocate memory")); sudo_warnx("%s", U_("unable to allocate memory"));
goto done; goto done;
} }
memcpy(closure->buf, &msg_len, sizeof(msg_len)); memcpy(closure->buf, &resp_len, sizeof(resp_len));
policy_check_result__pack(res, closure->buf + sizeof(msg_len)); intercept_response__pack(resp, closure->buf + sizeof(resp_len));
ret = true; ret = true;
@@ -395,11 +535,27 @@ done:
debug_return_bool(ret); 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 static bool
fmt_accept_message(struct intercept_closure *closure) fmt_accept_message(struct intercept_closure *closure)
{ {
PolicyAcceptMessage msg = POLICY_ACCEPT_MESSAGE__INIT; PolicyAcceptMessage msg = POLICY_ACCEPT_MESSAGE__INIT;
PolicyCheckResult res = POLICY_CHECK_RESULT__INIT; InterceptResponse resp = INTERCEPT_RESPONSE__INIT;
size_t n; size_t n;
debug_decl(fmt_accept_message, SUDO_DEBUG_EXEC); debug_decl(fmt_accept_message, SUDO_DEBUG_EXEC);
@@ -413,40 +569,40 @@ fmt_accept_message(struct intercept_closure *closure)
continue; continue;
msg.n_run_envp = n; msg.n_run_envp = n;
res.u.accept_msg = &msg; resp.u.accept_msg = &msg;
res.type_case = POLICY_CHECK_RESULT__TYPE_ACCEPT_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 static bool
fmt_reject_message(struct intercept_closure *closure) fmt_reject_message(struct intercept_closure *closure)
{ {
PolicyRejectMessage msg = POLICY_REJECT_MESSAGE__INIT; 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); debug_decl(fmt_reject_message, SUDO_DEBUG_EXEC);
msg.reject_message = (char *)closure->errstr; msg.reject_message = (char *)closure->errstr;
res.u.reject_msg = &msg; resp.u.reject_msg = &msg;
res.type_case = POLICY_CHECK_RESULT__TYPE_REJECT_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 static bool
fmt_error_message(struct intercept_closure *closure) fmt_error_message(struct intercept_closure *closure)
{ {
PolicyErrorMessage msg = POLICY_ERROR_MESSAGE__INIT; 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); debug_decl(fmt_error_message, SUDO_DEBUG_EXEC);
msg.error_message = (char *)closure->errstr; msg.error_message = (char *)closure->errstr;
res.u.error_msg = &msg; resp.u.error_msg = &msg;
res.type_case = POLICY_CHECK_RESULT__TYPE_ERROR_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 static bool
intercept_write(int fd, struct intercept_closure *closure) intercept_write(int fd, struct intercept_closure *closure)
{ {
size_t rem; struct sudo_event_base *evbase = sudo_ev_get_base(&closure->ev);
uint8_t *cp;
ssize_t nwritten; ssize_t nwritten;
bool ret = false; bool ret = false;
uint8_t *cp;
size_t rem;
debug_decl(intercept_write, SUDO_DEBUG_EXEC); debug_decl(intercept_write, SUDO_DEBUG_EXEC);
/* XXX - proper state variable */
switch (closure->policy_result) { switch (closure->policy_result) {
case 2:
if (!fmt_hello_response(closure))
goto done;
break;
case 1: case 1:
if (!fmt_accept_message(closure)) if (!fmt_accept_message(closure))
goto done; goto done;
@@ -479,15 +641,55 @@ intercept_write(int fd, struct intercept_closure *closure)
cp = closure->buf; cp = closure->buf;
rem = closure->len; rem = closure->len;
do { do {
nwritten = write(fd, cp, rem); nwritten = send(fd, cp, rem, 0);
if (nwritten == -1) { if (nwritten == -1) {
sudo_warn("write"); sudo_warn("send");
goto done; goto done;
} }
cp += nwritten; cp += nwritten;
rem -= nwritten; rem -= nwritten;
} while (rem > 0); } 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; ret = true;
done: done:
@@ -513,115 +715,40 @@ intercept_cb(int fd, int what, void *v)
break; break;
} }
if (!success || what == SUDO_EV_WRITE) { if (!success)
intercept_close(fd, closure); intercept_connection_close(fd, closure);
}
debug_return; debug_return;
} }
/* /*
* Accept a single fd passed from the child to use for policy checks. * Accept a new connection from the client and fill in a client closure.
* This acts a bit like accept() in reverse since the client allocates * Registers a new event for the connection.
* the socketpair() that is used for the actual communication.
*/ */
void static void
intercept_fd_cb(int fd, int what, void *v) intercept_accept_cb(int fd, int what, void *v)
{ {
struct intercept_closure *closure = NULL; struct intercept_closure *closure = v;
struct intercept_fd_closure *fdc = v; struct sudo_event_base *evbase = sudo_ev_get_base(&closure->ev);
struct msghdr msg; struct sockaddr_in sin;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1 socklen_t sin_len = sizeof(sin);
union { int client_sock;
struct cmsghdr hdr; debug_decl(intercept_accept_cb, SUDO_DEBUG_EXEC);
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);
/* client_sock = accept(fd, (struct sockaddr *)&sin, &sin_len);
* We send a single byte of data along with the fd; some systems if (client_sock == -1) {
* don't support sending file descriptors without data. sudo_warn("accept");
* 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__);
goto bad; goto bad;
} }
if (cmsg->cmsg_type != SCM_RIGHTS) { if (!intercept_setup(client_sock, evbase, closure->details)) {
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"));
goto bad; goto bad;
} }
debug_return; debug_return;
bad: bad:
if (newfd != -1) if (client_sock != -1)
close(newfd); close(client_sock);
free(closure);
debug_return; debug_return;
} }

View File

@@ -43,15 +43,11 @@
#include "sudo_exec.h" #include "sudo_exec.h"
#include "sudo_plugin.h" #include "sudo_plugin.h"
#include "sudo_plugin_int.h" #include "sudo_plugin_int.h"
#include "sudo_rand.h"
/* 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;
struct sudo_event *intercept_event;
struct sudo_event *sigint_event; struct sudo_event *sigint_event;
struct sudo_event *sigquit_event; struct sudo_event *sigquit_event;
struct sudo_event *sigtstp_event; struct sudo_event *sigtstp_event;
@@ -203,13 +199,11 @@ signal_cb_nopty(int signo, int what, void *v)
*/ */
static void static void
fill_exec_closure_nopty(struct exec_closure_nopty *ec, fill_exec_closure_nopty(struct exec_closure_nopty *ec,
struct command_status *cstat, struct command_details *details, struct command_status *cstat, struct command_details *details, int errfd)
int intercept_fd, int errfd)
{ {
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;
@@ -227,17 +221,6 @@ fill_exec_closure_nopty(struct exec_closure_nopty *ec,
sudo_fatal("%s", U_("unable to add event to queue")); sudo_fatal("%s", U_("unable to add event to queue"));
sudo_debug_printf(SUDO_DEBUG_INFO, "error pipe fd %d\n", errfd); 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. */ /* Events for local signals. */
ec->sigint_event = sudo_ev_alloc(SIGINT, ec->sigint_event = sudo_ev_alloc(SIGINT,
SUDO_EV_SIGINFO, signal_cb_nopty, ec); 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. * This must be inherited across exec, hence no FD_CLOEXEC.
*/ */
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_CHILDREN)) { 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")); 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 * Fill in exec closure, allocate event base, signal events and
* the error pipe event. * 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. */ /* Restore signal mask now that signal handlers are setup. */
sigprocmask(SIG_SETMASK, &oset, NULL); sigprocmask(SIG_SETMASK, &oset, NULL);

View File

@@ -47,7 +47,6 @@
#include "sudo_exec.h" #include "sudo_exec.h"
#include "sudo_plugin.h" #include "sudo_plugin.h"
#include "sudo_plugin_int.h" #include "sudo_plugin_int.h"
#include "sudo_rand.h"
/* Evaluates to true if the event has /dev/tty as its fd. */ /* 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]) #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); TAILQ_HEAD(monitor_message_list, monitor_message);
/* 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;
struct sudo_event *fwdchannel_event; struct sudo_event *fwdchannel_event;
struct sudo_event *intercept_event;
struct sudo_event *sigint_event; struct sudo_event *sigint_event;
struct sudo_event *sigquit_event; struct sudo_event *sigquit_event;
struct sudo_event *sigtstp_event; struct sudo_event *sigtstp_event;
@@ -1207,13 +1203,11 @@ fwdchannel_cb(int sock, int what, void *v)
*/ */
static void static void
fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat, fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
struct command_details *details, pid_t ppgrp, int backchannel, struct command_details *details, pid_t ppgrp, int backchannel)
int intercept_fd)
{ {
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;
@@ -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_fatal("%s", U_("unable to add event to queue"));
sudo_debug_printf(SUDO_DEBUG_INFO, "backchannel fd %d\n", backchannel); 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. */ /* Events for local signals. */
ec->sigint_event = sudo_ev_alloc(SIGINT, ec->sigint_event = sudo_ev_alloc(SIGINT,
SUDO_EV_SIGINFO, signal_cb_pty, ec); 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. * This must be inherited across exec, hence no FD_CLOEXEC.
*/ */
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_CHILDREN)) { 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")); 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 * Fill in exec closure, allocate event base, signal events and
* the backchannel event. * 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. */ /* Restore signal mask now that signal handlers are setup. */
sigprocmask(SIG_SETMASK, &oset, NULL); sigprocmask(SIG_SETMASK, &oset, NULL);

View File

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

View File

@@ -4,14 +4,33 @@ syntax = "proto3";
* Intercept message from sudo_intercept.so. Messages on the * Intercept message from sudo_intercept.so. Messages on the
* wire are prefixed with a 32-bit size in network byte order. * wire are prefixed with a 32-bit size in network byte order.
*/ */
message InterceptMessage { message InterceptRequest {
oneof type { oneof type {
PolicyCheckRequest policy_check_req = 1; 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. * Policy check request from sudo_intercept.so.
* Must include the correct secret value.
* Note that the plugin API only currently supports passing * Note that the plugin API only currently supports passing
* the new environment in to the open() function. * the new environment in to the open() function.
*/ */
@@ -19,6 +38,8 @@ message PolicyCheckRequest {
string command = 1; string command = 1;
repeated string argv = 2; repeated string argv = 2;
repeated string envp = 3; repeated string envp = 3;
int32 intercept_fd = 4;
fixed64 secret = 5;
} }
message PolicyAcceptMessage { 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 { oneof type {
PolicyAcceptMessage accept_msg = 1; HelloResponse hello_resp = 1;
PolicyRejectMessage reject_msg = 2; PolicyAcceptMessage accept_msg = 2;
PolicyErrorMessage error_msg = 3; 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 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 */
/* /*
@@ -88,19 +87,22 @@
*/ */
struct command_details; struct command_details;
struct command_status; struct command_status;
struct sudo_event_base;
struct stat; struct stat;
/* exec.c */ /* exec.c */
void exec_cmnd(struct command_details *details, int intercept_fd, int errfd); void exec_cmnd(struct command_details *details, int intercept_fd, int errfd);
void terminate_command(pid_t pid, bool use_pgrp); void terminate_command(pid_t pid, bool use_pgrp);
bool sudo_terminated(struct command_status *cstat); bool sudo_terminated(struct command_status *cstat);
void intercept_fd_cb(int fd, int what, void *v);
/* exec_common.c */ /* exec_common.c */
int sudo_execve(int fd, const char *path, char *const argv[], char *envp[], int intercept_fd, int flags); 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 **disable_execute(char *envp[], const char *dso);
char **enable_monitor(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 */ /* exec_nopty.c */
void exec_nopty(struct command_details *details, struct command_status *cstat); void exec_nopty(struct command_details *details, struct command_status *cstat);

View File

@@ -25,6 +25,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h>
#if defined(HAVE_STDINT_H) #if defined(HAVE_STDINT_H)
# include <stdint.h> # include <stdint.h>
@@ -57,8 +58,125 @@
extern char **environ; extern char **environ;
static int intercept_sock = -1;
static uint64_t secret; static uint64_t secret;
static in_port_t intercept_port;
/* Send entire request to sudo (blocking). */
static bool
send_req(int sock, const uint8_t *buf, size_t len)
{
const uint8_t *cp = buf;
ssize_t nwritten;
debug_decl(send_req, SUDO_DEBUG_EXEC);
do {
nwritten = send(sock, cp, len, 0);
if (nwritten == -1) {
debug_return_bool(false);
}
len -= nwritten;
cp += nwritten;
} while (len > 0);
debug_return_bool(true);
}
static bool
send_client_hello(int sock)
{
InterceptRequest msg = INTERCEPT_REQUEST__INIT;
ClientHello hello = CLIENT_HELLO__INIT;
uint8_t *buf = NULL;
uint32_t msg_len;
size_t len;
bool ret = false;
debug_decl(send_client_hello, SUDO_DEBUG_EXEC);
/* Setup policy check request. */
hello.pid = getpid();
msg.type_case = INTERCEPT_REQUEST__TYPE_HELLO;;
msg.u.hello = &hello;
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);
}
/*
* Receive HelloResponse from sudo over fd.
*/
InterceptResponse *
recv_intercept_response(int fd)
{
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. * Look up SUDO_INTERCEPT_FD in the environment.
@@ -67,11 +185,14 @@ static uint64_t secret;
__attribute__((constructor)) static void __attribute__((constructor)) static void
sudo_interposer_init(void) sudo_interposer_init(void)
{ {
InterceptResponse *res = NULL;
static bool initialized; static bool initialized;
int fd = -1;
char **p; char **p;
debug_decl(sudo_interposer_init, SUDO_DEBUG_EXEC); debug_decl(sudo_interposer_init, SUDO_DEBUG_EXEC);
if (!initialized) { if (initialized)
debug_return;
initialized = true; initialized = true;
/* Read debug section of sudo.conf and init debugging. */ /* Read debug section of sudo.conf and init debugging. */
@@ -89,8 +210,6 @@ 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;
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "%s", *p); sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "%s", *p);
@@ -98,45 +217,51 @@ sudo_interposer_init(void)
if (errstr != NULL) { if (errstr != NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"invalid SUDO_INTERCEPT_FD: %s: %s", fdstr, errstr); "invalid SUDO_INTERCEPT_FD: %s: %s", fdstr, errstr);
debug_return; goto done;
}
/* 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;
} }
} }
}
if (fd == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"SUDO_INTERCEPT_FD not found in environment"); "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; debug_return;
} }
static uint8_t * static bool
fmt_policy_check_req(const char *cmnd, char * const argv[], char * const envp[], send_policy_check_req(int sock, const char *cmnd, char * const argv[],
size_t *buflen) char * const envp[])
{ {
InterceptMessage msg = INTERCEPT_MESSAGE__INIT; InterceptRequest msg = INTERCEPT_REQUEST__INIT;
PolicyCheckRequest req = POLICY_CHECK_REQUEST__INIT; PolicyCheckRequest req = POLICY_CHECK_REQUEST__INIT;
uint8_t *buf = NULL; uint8_t *buf = NULL;
bool ret = false;
uint32_t msg_len; uint32_t msg_len;
size_t len; size_t len;
debug_decl(sudo_interposer_init, SUDO_DEBUG_EXEC); debug_decl(fmt_policy_check_req, SUDO_DEBUG_EXEC);
/* Setup policy check request. */ /* Setup policy check request. */
req.secret = secret;
req.intercept_fd = sock;
req.command = (char *)cmnd; req.command = (char *)cmnd;
req.argv = (char **)argv; req.argv = (char **)argv;
for (len = 0; argv[len] != NULL; len++) for (len = 0; argv[len] != NULL; len++)
@@ -146,13 +271,13 @@ fmt_policy_check_req(const char *cmnd, char * const argv[], char * const envp[],
for (len = 0; envp[len] != NULL; len++) for (len = 0; envp[len] != NULL; len++)
continue; continue;
req.n_envp = len; req.n_envp = len;
msg.type_case = INTERCEPT_MESSAGE__TYPE_POLICY_CHECK_REQ; msg.type_case = INTERCEPT_REQUEST__TYPE_POLICY_CHECK_REQ;
msg.u.policy_check_req = &req; msg.u.policy_check_req = &req;
len = intercept_message__get_packed_size(&msg); len = intercept_request__get_packed_size(&msg);
if (len > MESSAGE_SIZE_MAX) { if (len > MESSAGE_SIZE_MAX) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"InterceptMessage too large: %zu", len); "InterceptRequest too large: %zu", len);
goto done; goto done;
} }
/* Wire message size is used for length encoding, precedes message. */ /* Wire message size is used for length encoding, precedes message. */
@@ -164,78 +289,62 @@ fmt_policy_check_req(const char *cmnd, char * const argv[], char * const envp[],
goto done; goto done;
} }
memcpy(buf, &msg_len, sizeof(msg_len)); memcpy(buf, &msg_len, sizeof(msg_len));
intercept_message__pack(&msg, buf + sizeof(msg_len)); intercept_request__pack(&msg, buf + sizeof(msg_len));
*buflen = len;
ret = send_req(sock, buf, len);
done: 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)
{
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);
/* /*
* We send a single byte of data along with the fd; some systems * Connect back to sudo process at localhost:intercept_port
* don't support sending file descriptors without data.
* Note that the intercept fd is *blocking*.
*/ */
iov[0].iov_base = &ch; static int
iov[0].iov_len = 1; intercept_connect(void)
memset(&msg, 0, sizeof(msg)); {
msg.msg_iov = iov; int sock = -1;
msg.msg_iovlen = 1; struct sockaddr_in sin;
#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && HAVE_STRUCT_MSGHDR_MSG_CONTROL == 1 debug_decl(command_allowed, SUDO_DEBUG_EXEC);
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 */
for (;;) { if (intercept_port == 0) {
nsent = sendmsg(sock, &msg, 0); sudo_warnx(U_("intercept port not set"));
if (nsent != -1) goto done;
debug_return_bool(true);
if (errno != EAGAIN && errno != EINTR)
break;
} }
sudo_warn("sendmsg(%d)", sock);
debug_return_bool(false); 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 bool
command_allowed(const char *cmnd, char * const argv[], char * const envp[], command_allowed(const char *cmnd, char * const argv[],
char **ncmndp, char ***nargvp, char ***nenvpp) char * const envp[], char **ncmndp, char ***nargvp, char ***nenvpp)
{ {
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL; char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
PolicyCheckResult *res = NULL; InterceptResponse *res = NULL;
int sv[2] = { -1, -1 };
ssize_t nread, nwritten;
uint8_t *cp, *buf = NULL;
bool ret = false; bool ret = false;
uint32_t res_len; size_t idx, len = 0;
size_t idx, len; int sock;
debug_decl(intercept_send_fd, SUDO_DEBUG_EXEC); debug_decl(command_allowed, SUDO_DEBUG_EXEC);
if (sudo_debug_needed(SUDO_DEBUG_INFO)) { if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, 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) { sock = intercept_connect();
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, if (sock == -1)
"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)
goto done; goto done;
/* Send request to sudo (blocking). */ if (!send_policy_check_req(sock, cmnd, argv, envp))
cp = buf;
do {
nwritten = write(sv[0], cp, len);
if (nwritten == -1) {
goto done; goto done;
}
len -= nwritten;
cp += nwritten;
} while (len > 0);
free(buf);
buf = NULL;
/* Read message size (uint32_t in host byte order). */ res = recv_intercept_response(sock);
nread = read(sv[0], &res_len, sizeof(res_len)); if (res == NULL)
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; 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) {
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");
goto done;
}
switch (res->type_case) { switch (res->type_case) {
case POLICY_CHECK_RESULT__TYPE_ACCEPT_MSG: case INTERCEPT_RESPONSE__TYPE_ACCEPT_MSG:
if (sudo_debug_needed(SUDO_DEBUG_INFO)) { if (sudo_debug_needed(SUDO_DEBUG_INFO)) {
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"run_command: %s", res->u.accept_msg->run_command); "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; nargv[len] = NULL;
// XXX - bogus cast // XXX - bogus cast
nenvp = sudo_preload_dso((char **)envp, sudo_conf_intercept_path(), nenvp = sudo_preload_dso((char **)envp, sudo_conf_intercept_path(),
intercept_sock); sock);
if (nenvp == NULL) if (nenvp == NULL)
goto oom; goto oom;
*ncmndp = ncmnd; *ncmndp = ncmnd;
@@ -368,11 +399,11 @@ command_allowed(const char *cmnd, char * const argv[], char * const envp[],
*nenvpp = nenvp; *nenvpp = nenvp;
ret = true; ret = true;
goto done; 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. */ /* Policy module displayed reject message but we are in raw mode. */
fputc('\r', stderr); fputc('\r', stderr);
goto done; 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. */ /* Policy module may display error message but we are in raw mode. */
fputc('\r', stderr); fputc('\r', stderr);
sudo_warnx("%s", res->u.error_msg->error_message); 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: default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected type_case value %d in %s from %s", "unexpected type_case value %d in %s from %s",
res->type_case, "PolicyCheckResult", "sudo"); res->type_case, "InterceptResponse", "sudo");
goto done; goto done;
} }
@@ -390,12 +421,10 @@ oom:
free(nargv[--len]); free(nargv[--len]);
done: done:
policy_check_result__free_unpacked(res, NULL); /* Keep socket open for ctor when we execute the command. */
if (sv[0] != -1) if (!ret && sock != -1)
close(sv[0]); close(sock);
if (sv[1] != -1) intercept_response__free_unpacked(res, NULL);
close(sv[1]);
free(buf);
debug_return_bool(ret); debug_return_bool(ret);
} }