Skip to content

Commit 17aac19

Browse files
committed
[WIP] executor: collect and return audit logs
Implemented the audit log extraction per program. This is available for VMs that run only one proc and add `audit` in the experimental config. The messages are appended to the output of the program and are printed during result processing.
1 parent 4d34627 commit 17aac19

File tree

11 files changed

+274
-56
lines changed

11 files changed

+274
-56
lines changed

executor/common_linux.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4415,7 +4415,6 @@ inline int symlink(const char* old_path, const char* new_path)
44154415
#define SYSTEM_UID 1000
44164416
#define SYSTEM_GID 1000
44174417

4418-
const char* const SELINUX_CONTEXT_UNTRUSTED_APP = "u:r:untrusted_app:s0:c512,c768";
44194418
const char* const SELINUX_LABEL_APP_DATA_FILE = "u:object_r:app_data_file:s0:c512,c768";
44204419
const char* const SELINUX_CONTEXT_FILE = "/proc/thread-self/attr/current";
44214420
const char* const SELINUX_XATTR_NAME = "security.selinux";
@@ -4567,8 +4566,6 @@ static int do_sandbox_android(uint64 sandbox_arg)
45674566
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
45684567

45694568
setfilecon(".", SELINUX_LABEL_APP_DATA_FILE);
4570-
if (uid == UNTRUSTED_APP_UID)
4571-
setcon(SELINUX_CONTEXT_UNTRUSTED_APP);
45724569

45734570
loop();
45744571
doexit(1);

executor/executor_runner.h

Lines changed: 158 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <signal.h>
66
#include <sys/mman.h>
77
#include <sys/resource.h>
8+
#include <sys/socket.h>
89
#include <unistd.h>
910

1011
#include <algorithm>
@@ -17,6 +18,21 @@
1718
#include <utility>
1819
#include <vector>
1920

21+
#include <linux/audit.h>
22+
#include <linux/netlink.h>
23+
24+
constexpr int NETLINK_BUF_SIZE = 4096;
25+
26+
ssize_t ReceiveNetlinkMessage(int fd, void* buf, size_t len)
27+
{
28+
return recv(fd, buf, len, 0);
29+
}
30+
31+
ssize_t SendNetlinkMessage(int fd, const void* buf, size_t len)
32+
{
33+
return send(fd, buf, len, 0);
34+
}
35+
2036
inline std::ostream& operator<<(std::ostream& ss, const rpc::ExecRequestRawT& req)
2137
{
2238
return ss << "id=" << req.id
@@ -109,7 +125,7 @@ class Proc
109125
{
110126
public:
111127
Proc(Connection& conn, const char* bin, ProcIDPool& proc_id_pool, int& restarting, const bool& corpus_triaged, int max_signal_fd,
112-
int cover_filter_fd, ProcOpts opts)
128+
int cover_filter_fd, ProcOpts opts, int audit_sock)
113129
: conn_(conn),
114130
bin_(bin),
115131
proc_id_pool_(proc_id_pool),
@@ -121,7 +137,8 @@ class Proc
121137
opts_(opts),
122138
req_shmem_(kMaxInput),
123139
resp_shmem_(kMaxOutput),
124-
resp_mem_(static_cast<OutputData*>(resp_shmem_.Mem()))
140+
resp_mem_(static_cast<OutputData*>(resp_shmem_.Mem())),
141+
audit_sock_(audit_sock)
125142
{
126143
Start();
127144
}
@@ -237,6 +254,7 @@ class Proc
237254
uint64 exec_start_ = 0;
238255
uint64 wait_start_ = 0;
239256
uint64 wait_end_ = 0;
257+
int audit_sock_ = 0;
240258

241259
friend std::ostream& operator<<(std::ostream& ss, const Proc& proc)
242260
{
@@ -251,6 +269,63 @@ class Proc
251269
return ss;
252270
}
253271

272+
ssize_t SendUserAuditMessage(const std::string_view message_text) {
273+
const size_t payload_len = message_text.length() + 1;
274+
const size_t buf_len = NLMSG_SPACE(payload_len);
275+
std::vector<char> buf(buf_len);
276+
memset(buf.data(), 0, buf_len);
277+
278+
auto* nlh = reinterpret_cast<struct nlmsghdr*>(buf.data());
279+
nlh->nlmsg_len = NLMSG_LENGTH((int)payload_len);
280+
nlh->nlmsg_type = AUDIT_USER_AVC;
281+
nlh->nlmsg_flags = NLM_F_REQUEST;
282+
283+
char* data = static_cast<char*>(NLMSG_DATA(nlh));
284+
strncpy(data, message_text.data(), payload_len);
285+
return SendNetlinkMessage(audit_sock_, nlh, nlh->nlmsg_len);
286+
}
287+
288+
void drainAuditBacklog(std::vector<uint8_t>* output)
289+
{
290+
bool prefixed = false;
291+
ssize_t slen = 0;
292+
char buf[NETLINK_BUF_SIZE];
293+
struct nlmsghdr* header;
294+
295+
if (SendUserAuditMessage("PROC END") < 0)
296+
return;
297+
298+
// Drain the audit backlog until there is no other message
299+
do {
300+
slen = ReceiveNetlinkMessage(audit_sock_, buf, sizeof(buf));
301+
if (errno == EINTR) {
302+
continue;
303+
}
304+
if (slen < NLMSG_LENGTH(0)) {
305+
fprintf(stderr, "audit: message too short\n");
306+
continue;
307+
}
308+
header = (struct nlmsghdr*)buf;
309+
if (header->nlmsg_type != AUDIT_AVC && header->nlmsg_type != AUDIT_USER_AVC) {
310+
continue;
311+
}
312+
if (header->nlmsg_type == AUDIT_AVC) {
313+
if (!prefixed) {
314+
char tmp[128];
315+
// Add prefix to the audit messages.
316+
snprintf(tmp, sizeof(tmp), "\nAudit messages:\n");
317+
output->insert(output->end(), tmp, tmp + strlen(tmp));
318+
prefixed = true;
319+
}
320+
std::string message((char*)NLMSG_DATA(header),
321+
(char*)NLMSG_DATA(header) +
322+
(slen - sizeof(*header)));
323+
message.append("\n");
324+
output->insert(output->end(), message.c_str(), message.c_str() + strlen(message.c_str()));
325+
}
326+
} while (header->nlmsg_type != AUDIT_USER_AVC);
327+
}
328+
254329
void ChangeState(State state)
255330
{
256331
if (state_ == State::Handshaking)
@@ -454,6 +529,10 @@ class Proc
454529
output_.insert(output_.end(), tmp, tmp + strlen(tmp));
455530
}
456531
}
532+
if (IsSet(msg_->flags, rpc::RequestFlag::ReturnAudit)) {
533+
output = &output_;
534+
drainAuditBacklog(output);
535+
}
457536
uint32 num_calls = 0;
458537
if (msg_->type == rpc::RequestType::Program)
459538
num_calls = read_input(&prog_data);
@@ -556,9 +635,13 @@ class Runner
556635
proc_id_pool_.emplace(num_procs);
557636
int max_signal_fd = max_signal_ ? max_signal_->FD() : -1;
558637
int cover_filter_fd = cover_filter_ ? cover_filter_->FD() : -1;
638+
int audit_sock = 0;
639+
if (audit) {
640+
audit_sock = registerForAudit();
641+
}
559642
for (int i = 0; i < num_procs; i++)
560643
procs_.emplace_back(new Proc(conn, bin, *proc_id_pool_, restarting_, corpus_triaged_,
561-
max_signal_fd, cover_filter_fd, proc_opts_));
644+
max_signal_fd, cover_filter_fd, proc_opts_, audit_sock));
562645

563646
for (;;)
564647
Loop();
@@ -574,6 +657,7 @@ class Runner
574657
std::deque<rpc::ExecRequestRawT> requests_;
575658
std::vector<std::string> leak_frames_;
576659
int restarting_ = 0;
660+
bool audit = false;
577661
bool corpus_triaged_ = false;
578662
ProcOpts proc_opts_{};
579663

@@ -595,6 +679,71 @@ class Runner
595679
return ss;
596680
}
597681

682+
// Helper function to open a Netlink socket for Audit
683+
int OpenNetlinkAuditSocket()
684+
{
685+
return socket(AF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
686+
}
687+
688+
int registerForAudit()
689+
{
690+
struct {
691+
struct nlmsghdr nlh;
692+
struct audit_status status;
693+
} req;
694+
memset(&req, 0, sizeof(req));
695+
696+
int fd = OpenNetlinkAuditSocket();
697+
if (fd < 0) {
698+
return -1;
699+
}
700+
701+
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status));
702+
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
703+
req.nlh.nlmsg_seq = 1;
704+
req.nlh.nlmsg_type = AUDIT_SET;
705+
req.status.pid = getpid();
706+
req.status.backlog_limit = 0;
707+
req.status.rate_limit = 0;
708+
req.status.backlog_wait_time = 0;
709+
req.status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_BACKLOG_LIMIT | AUDIT_STATUS_RATE_LIMIT | AUDIT_STATUS_BACKLOG_WAIT_TIME;
710+
711+
ssize_t sent = SendNetlinkMessage(fd, &req, req.nlh.nlmsg_len);
712+
if (sent != req.nlh.nlmsg_len) {
713+
close(fd);
714+
return -1;
715+
}
716+
717+
ssize_t slen = 0;
718+
char buf[NETLINK_BUF_SIZE];
719+
struct nlmsghdr* header;
720+
do {
721+
slen = ReceiveNetlinkMessage(fd, buf, sizeof(buf));
722+
if (errno == EAGAIN || errno == EINTR) {
723+
continue;
724+
}
725+
if (slen < NLMSG_LENGTH(0)) {
726+
fprintf(stderr, "audit: message too short\n");
727+
continue;
728+
}
729+
header = (struct nlmsghdr*)buf;
730+
} while (header->nlmsg_type != NLMSG_ERROR);
731+
732+
struct nlmsgerr* err;
733+
if ((size_t)slen < NLMSG_LENGTH(sizeof(*err))) {
734+
fprintf(stderr, "audit_listener: error message too short\n");
735+
close(fd);
736+
return -1;
737+
}
738+
err = (struct nlmsgerr*)NLMSG_DATA(header);
739+
if (err->error != 0) {
740+
fprintf(stderr, "audit_listener: received error %d\n", -err->error);
741+
close(fd);
742+
return -1;
743+
}
744+
return fd;
745+
}
746+
598747
void Loop()
599748
{
600749
Select select;
@@ -661,6 +810,12 @@ class Runner
661810
conn_.Recv(conn_reply);
662811
if (conn_reply.debug)
663812
flag_debug = true;
813+
if (conn_reply.audit) {
814+
if (conn_reply.procs > 1)
815+
debug("audit only supported with one proc");
816+
audit = conn_reply.procs == 1;
817+
}
818+
664819
debug("connected to manager: procs=%d cover_edges=%d kernel_64_bit=%d slowdown=%d syscall_timeout=%u"
665820
" program_timeout=%u features=0x%llx\n",
666821
conn_reply.procs, conn_reply.cover_edges, conn_reply.kernel_64_bit,

pkg/flatrpc/flatrpc.fbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ table ConnectRequestRaw {
5050

5151
table ConnectReplyRaw {
5252
debug :bool;
53+
audit :bool;
5354
cover :bool;
5455
cover_edges :bool;
5556
kernel_64_bit :bool;
@@ -128,6 +129,8 @@ enum RequestType : uint64 {
128129
enum RequestFlag : uint64 (bit_flags) {
129130
// If set, collect program output and return in output field.
130131
ReturnOutput,
132+
// If set, collect audit logs produced by the program at the end of the output.
133+
ReturnAudit,
131134
// If set, don't fail on program failures, instead return the error in error field.
132135
ReturnError,
133136
}

0 commit comments

Comments
 (0)