diff --git a/logd/Android.bp b/logd/Android.bp index 360f2fe53..9b8625821 100644 --- a/logd/Android.bp +++ b/logd/Android.bp @@ -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", diff --git a/logd/auditctl.cpp b/logd/auditctl.cpp new file mode 100644 index 000000000..98bb02dbe --- /dev/null +++ b/logd/auditctl.cpp @@ -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 +#include +#include +#include +#include +#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(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; +} diff --git a/logd/libaudit.c b/logd/libaudit.c index 9d9a85742..f452c71ab 100644 --- a/logd/libaudit.c +++ b/logd/libaudit.c @@ -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; diff --git a/logd/libaudit.h b/logd/libaudit.h index 2a93ea361..b4a92a8a3 100644 --- a/logd/libaudit.h +++ b/logd/libaudit.h @@ -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 diff --git a/logd/logd.rc b/logd/logd.rc index c740ecfce..438419ad6 100644 --- a/logd/logd.rc +++ b/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 diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp index 7d7a22f92..447b06731 100644 --- a/logd/tests/logd_test.cpp +++ b/logd/tests/logd_test.cpp @@ -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 -} diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp index f01a8c7ec..3bc3883a5 100644 --- a/shell_and_utilities/Android.bp +++ b/shell_and_utilities/Android.bp @@ -10,6 +10,7 @@ phony { phony { name: "shell_and_utilities_system", required: [ + "auditctl", "awk", "bzip2", "grep",