summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Kralevich <nnk@google.com>2019-04-09 10:59:39 -0700
committerNick Kralevich <nnk@google.com>2019-04-09 20:54:47 -0700
commitac3ca99b77c4944333649dda4670d509f9facc05 (patch)
tree7def753c3cefcf4714f14143c3d95732e011dac3
parent83e52ce905d2655f074d264069d7f49459c1912d (diff)
downloadsystem_core-ac3ca99b77c4944333649dda4670d509f9facc05.tar.gz
system_core-ac3ca99b77c4944333649dda4670d509f9facc05.tar.bz2
system_core-ac3ca99b77c4944333649dda4670d509f9facc05.zip
introduce auditctl and use it to configure SELinux throttling
In an effort to ensure that our development community does not introduce new code without corresponding SELinux changes, Android closely monitors the number of SELinux denials which occur during boot. This monitoring occurs both in treehugger, as well as various dashboards. If SELinux denials are dropped during early boot, this could result in non-determinism for the various SELinux treehugger tests. Introduce /system/bin/auditctl. This tool, model after https://linux.die.net/man/8/auditctl , allows for configuring the throttling rate for the kernel auditing system. Remove any throttling from early boot. This will hopefully reduce treehugger flakiness by making denial generation more predictible during early boot. Reapply the throttling at boot complete, to avoid denial of service attacks against the auditing subsystem. Delete pre-existing unittests for logd / SELinux integration. It's intended that all throttling decisions be made in the kernel, and shouldn't be a concern of logd. Bug: 118815957 Test: Perform an operation which generates lots of SELinux denials, and count how many occur before and after the time period. (cherry picked from commit be5e44679146d333c20e28bf99c52d168f422626) Change-Id: I283cd56151d199cd66f0d217b49115460c4a01e5
-rw-r--r--logd/Android.bp18
-rw-r--r--logd/auditctl.cpp74
-rw-r--r--logd/libaudit.c11
-rw-r--r--logd/libaudit.h13
-rw-r--r--logd/logd.rc11
-rw-r--r--logd/tests/logd_test.cpp143
-rw-r--r--shell_and_utilities/Android.bp1
7 files changed, 124 insertions, 147 deletions
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 <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;
+}
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",