Merge "introduce auditctl and use it to configure SELinux throttling"

This commit is contained in:
Treehugger Robot 2019-04-10 02:29:24 +00:00 committed by Gerrit Code Review
commit 3458bb6ce1
7 changed files with 124 additions and 147 deletions

View File

@ -80,6 +80,24 @@ cc_binary {
cflags: ["-Werror"],
}
cc_binary {
name: "auditctl",
srcs: ["auditctl.cpp"],
static_libs: [
"liblogd",
],
shared_libs: ["libbase"],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wconversion"
],
}
prebuilt_etc {
name: "logtagd.rc",

74
logd/auditctl.cpp Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/parseint.h>
#include <error.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "libaudit.h"
static void usage(const char* cmdline) {
fprintf(stderr, "Usage: %s [-r rate]\n", cmdline);
}
static void do_update_rate(uint32_t rate) {
int fd = audit_open();
if (fd == -1) {
error(EXIT_FAILURE, errno, "Unable to open audit socket");
}
int result = audit_rate_limit(fd, rate);
close(fd);
if (result < 0) {
fprintf(stderr, "Can't update audit rate limit: %d\n", result);
exit(EXIT_FAILURE);
}
}
int main(int argc, char* argv[]) {
uint32_t rate = 0;
bool update_rate = false;
int opt;
while ((opt = getopt(argc, argv, "r:")) != -1) {
switch (opt) {
case 'r':
if (!android::base::ParseUint<uint32_t>(optarg, &rate)) {
error(EXIT_FAILURE, errno, "Invalid Rate");
}
update_rate = true;
break;
default: /* '?' */
usage(argv[0]);
exit(EXIT_FAILURE);
}
}
// In the future, we may add other options to auditctl
// so this if statement will expand.
// if (!update_rate && !update_backlog && !update_whatever) ...
if (!update_rate) {
fprintf(stderr, "Nothing to do\n");
usage(argv[0]);
exit(EXIT_FAILURE);
}
if (update_rate) {
do_update_rate(rate);
}
return 0;
}

View File

@ -160,8 +160,7 @@ int audit_setup(int fd, pid_t pid) {
* and the the mask set to AUDIT_STATUS_PID
*/
status.pid = pid;
status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
status.rate_limit = AUDIT_RATE_LIMIT; /* audit entries per second */
status.mask = AUDIT_STATUS_PID;
/* Let the kernel know this pid will be registering for audit events */
rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@ -188,6 +187,14 @@ int audit_open() {
return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
}
int audit_rate_limit(int fd, uint32_t limit) {
struct audit_status status;
memset(&status, 0, sizeof(status));
status.mask = AUDIT_STATUS_RATE_LIMIT;
status.rate_limit = limit; /* audit entries per second */
return audit_send(fd, AUDIT_SET, &status, sizeof(status));
}
int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
ssize_t len;
int flags;

View File

@ -89,8 +89,17 @@ extern int audit_get_reply(int fd, struct audit_message* rep, reply_t block,
*/
extern int audit_setup(int fd, pid_t pid);
/* Max audit messages per second */
#define AUDIT_RATE_LIMIT 5
/**
* Throttle kernel messages at the provided rate
* @param fd
* The fd returned by a call to audit_open()
* @param rate
* The rate, in messages per second, above which the kernel
* should drop audit messages.
* @return
* This function returns 0 on success, -errno on error.
*/
extern int audit_rate_limit(int fd, uint32_t limit);
__END_DECLS

View File

@ -16,8 +16,19 @@ service logd-reinit /system/bin/logd --reinit
group logd
writepid /dev/cpuset/system-background/tasks
# Limit SELinux denial generation to 5/second
service logd-auditctl /system/bin/auditctl -r 5
oneshot
disabled
user logd
group logd
capabilities AUDIT_CONTROL
on fs
write /dev/event-log-tags "# content owned by logd
"
chown logd logd /dev/event-log-tags
chmod 0644 /dev/event-log-tags
on property:sys.boot_completed=1
start logd-auditctl

View File

@ -39,7 +39,6 @@
#endif
#include "../LogReader.h" // pickup LOGD_SNDTIMEO
#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
@ -1065,145 +1064,3 @@ TEST(logd, multiple_test_3) {
TEST(logd, multiple_test_10) {
__android_log_btwrite_multiple__helper(10);
}
#ifdef __ANDROID__
// returns violating pid
static pid_t sepolicy_rate(unsigned rate, unsigned num) {
pid_t pid = fork();
if (pid) {
siginfo_t info = {};
if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return -1;
if (info.si_status) return -1;
return pid;
}
// We may have DAC, but let's not have MAC
if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
int save_errno = errno;
security_context_t context;
getcon(&context);
if (strcmp(context, "u:r:shell:s0")) {
fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
context, strerror(save_errno));
freecon(context);
_exit(-1);
// NOTREACHED
return -1;
}
}
// The key here is we are root, but we are in u:r:shell:s0,
// and the directory does not provide us DAC access
// (eg: 0700 system system) so we trigger the pair dac_override
// and dac_read_search on every try to get past the message
// de-duper. We will also rotate the file name in the directory
// as another measure.
static const char file[] = "/data/drm/cannot_access_directory_%u";
static const unsigned avc_requests_per_access = 2;
rate /= avc_requests_per_access;
useconds_t usec;
if (rate == 0) {
rate = 1;
usec = 2000000;
} else {
usec = (1000000 + (rate / 2)) / rate;
}
num = (num + (avc_requests_per_access / 2)) / avc_requests_per_access;
if (usec < 2) usec = 2;
while (num > 0) {
if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
_exit(-1);
// NOTREACHED
return -1;
}
usleep(usec);
--num;
}
_exit(0);
// NOTREACHED
return -1;
}
static constexpr int background_period = 10;
static int count_avc(pid_t pid) {
int count = 0;
// pid=-1 skip as pid is in error
if (pid == (pid_t)-1) return count;
// pid=0 means we want to report the background count of avc: activities
struct logger_list* logger_list =
pid ? android_logger_list_alloc(
ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)
: android_logger_list_alloc_time(
ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
log_time(android_log_clockid()) -
log_time(background_period, 0),
0);
if (!logger_list) return count;
struct logger* logger = android_logger_open(logger_list, LOG_ID_EVENTS);
if (!logger) {
android_logger_list_close(logger_list);
return count;
}
for (;;) {
log_msg log_msg;
if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
(log_msg.id() != LOG_ID_EVENTS))
continue;
char* eventData = log_msg.msg();
if (!eventData) continue;
uint32_t tag = get4LE(eventData);
if (tag != AUDITD_LOG_TAG) continue;
if (eventData[4] != EVENT_TYPE_STRING) continue;
// int len = get4LE(eventData + 4 + 1);
log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
const char* cp = strstr(eventData + 4 + 1 + 4, "): avc: denied");
if (!cp) continue;
++count;
}
android_logger_list_close(logger_list);
return count;
}
#endif
TEST(logd, sepolicy_rate_limiter) {
#ifdef __ANDROID__
int background_selinux_activity_too_high = count_avc(0);
if (background_selinux_activity_too_high > 2) {
GTEST_LOG_(ERROR) << "Too much background selinux activity "
<< background_selinux_activity_too_high * 60 /
background_period
<< "/minute on the device, this test\n"
<< "can not measure the functionality of the "
<< "sepolicy rate limiter. Expect test to\n"
<< "fail as this device is in a bad state, "
<< "but is not strictly a unit test failure.";
}
static const int rate = AUDIT_RATE_LIMIT;
static const int duration = 2;
// Two seconds of sustained denials. Depending on the overlap in the time
// window that the kernel is considering vs what this test is considering,
// allow some additional denials to prevent a flaky test.
EXPECT_LE(count_avc(sepolicy_rate(rate, rate * duration)),
rate * duration + rate);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}

View File

@ -10,6 +10,7 @@ phony {
phony {
name: "shell_and_utilities_system",
required: [
"auditctl",
"awk",
"bzip2",
"grep",