Merge "introduce auditctl and use it to configure SELinux throttling"
This commit is contained in:
commit
3458bb6ce1
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
11
logd/logd.rc
11
logd/logd.rc
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ phony {
|
|||
phony {
|
||||
name: "shell_and_utilities_system",
|
||||
required: [
|
||||
"auditctl",
|
||||
"awk",
|
||||
"bzip2",
|
||||
"grep",
|
||||
|
|
Loading…
Reference in New Issue