logd: liblog: logcat: Add LOG_ID_SECURITY
- Largish commit, buffer and access controls done together
- Add LOG_ID_SECURITY binary content log
- Add "default" meta buffer
- allow LOG_ID_SECURITY only from AID_SYSTEM and AID_ROOT UID & GID
- Use __android_log_security() to gate logging
- Add __android_log_security_bwrite() native access to security
logging.
- Add liblog.__security_buffer end-to-end gTest
Bug: 26029733
Change-Id: Ibcf5b4660c17c1aa6902c0d93f8ffd29c93d9a93
diff --git a/liblog/log_read.c b/liblog/log_read.c
index fb86757..179f611 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -208,6 +208,7 @@
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
[LOG_ID_CRASH] = "crash",
+ [LOG_ID_SECURITY] = "security",
[LOG_ID_KERNEL] = "kernel",
};
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 11c6d9c..c2b0ec2 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -84,7 +84,7 @@
#endif /* !defined(_WIN32) */
#if FAKE_LOG_DEVICE
-static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
#else
static int logd_fd = -1;
static int pstore_fd = -1;
@@ -181,6 +181,7 @@
static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
static pid_t last_pid = (pid_t) -1;
static atomic_int_fast32_t dropped;
+ static atomic_int_fast32_t dropped_security;
if (!nr) {
return -EINVAL;
@@ -192,6 +193,23 @@
if (last_pid == (pid_t) -1) {
last_pid = getpid();
}
+ if (log_id == LOG_ID_SECURITY) {
+ if ((last_uid != AID_SYSTEM) && (last_uid != AID_ROOT)) {
+ uid_t uid = geteuid();
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT)) {
+ gid_t gid = getgid();
+ if ((gid != AID_SYSTEM) && (gid != AID_ROOT)) {
+ gid = getegid();
+ if ((gid != AID_SYSTEM) && (gid != AID_ROOT)) {
+ return -EPERM;
+ }
+ }
+ }
+ }
+ if (!__android_log_security()) {
+ return -EPERM;
+ }
+ }
/*
* struct {
* // what we provide to pstore
@@ -229,7 +247,26 @@
newVec[1].iov_len = sizeof(header);
if (logd_fd > 0) {
- int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ int32_t snapshot = atomic_exchange_explicit(&dropped_security, 0,
+ memory_order_relaxed);
+ if (snapshot) {
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_SECURITY;
+ buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+ buffer.payload.type = EVENT_TYPE_INT;
+ buffer.payload.data = htole32(snapshot);
+
+ newVec[2].iov_base = &buffer;
+ newVec[2].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&dropped_security, snapshot,
+ memory_order_relaxed);
+ }
+ }
+ snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
if (snapshot) {
android_log_event_int_t buffer;
@@ -243,7 +280,8 @@
ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+ atomic_fetch_add_explicit(&dropped, snapshot,
+ memory_order_relaxed);
}
}
}
@@ -315,6 +353,10 @@
ret -= sizeof(header);
} else if (ret == -EAGAIN) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ if (log_id == LOG_ID_SECURITY) {
+ atomic_fetch_add_explicit(&dropped_security, 1,
+ memory_order_relaxed);
+ }
}
#endif
@@ -328,6 +370,7 @@
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
[LOG_ID_CRASH] = "crash",
+ [LOG_ID_SECURITY] = "security",
[LOG_ID_KERNEL] = "kernel",
};
@@ -483,6 +526,18 @@
return write_to_log(LOG_ID_EVENTS, vec, 2);
}
+int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len)
+{
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_SECURITY, vec, 2);
+}
+
/*
* Like __android_log_bwrite, but takes the type as well. Doesn't work
* for the general case where we're generating lists of stuff, but very
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 597d8f6..1046d25 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -25,6 +25,7 @@
#include <log/logger.h>
#include <log/log_read.h>
#include <log/logprint.h>
+#include <private/android_logger.h>
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
@@ -201,6 +202,96 @@
property_set(persist_key, persist);
}
+TEST(liblog, __security_buffer) {
+ struct logger_list *logger_list;
+ android_event_long_t buffer;
+
+ static const char persist_key[] = "persist.logd.security";
+ char persist[PROP_VALUE_MAX];
+ bool set_persist = false;
+ bool allow_security = false;
+
+ if (__android_log_security()) {
+ allow_security = true;
+ } else {
+ property_get(persist_key, persist, "");
+ if (strcasecmp(persist, "true")) {
+ property_set(persist_key, "TRUE");
+ if (__android_log_security()) {
+ allow_security = true;
+ set_persist = true;
+ } else {
+ property_set(persist_key, persist);
+ }
+ }
+ }
+
+ if (!allow_security) {
+ fprintf(stderr, "WARNING: "
+ "security buffer disabled, bypassing end-to-end test\n");
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+ // expect failure!
+ ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+ return;
+ }
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_SECURITY, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+ 1000, pid)));
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t *>((void *)&ts));
+
+ ASSERT_LT(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != (4 + 1 + 8))
+ || (log_msg.id() != LOG_ID_SECURITY)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_LONG) {
+ continue;
+ }
+
+ log_time tx(eventData + 4 + 1);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ if (set_persist) {
+ property_set(persist_key, persist);
+ }
+
+ android_logger_list_close(logger_list);
+
+ EXPECT_EQ(1, count);
+
+}
+
static unsigned signaled;
log_time signal_time;
@@ -650,7 +741,8 @@
EXPECT_EQ(id, android_logger_get_id(logger));
EXPECT_LT(0, android_logger_get_log_size(logger));
/* crash buffer is allowed to be empty, that is actually healthy! */
- if (android_logger_get_log_readable_size(logger) || strcmp("crash", name)) {
+ if (android_logger_get_log_readable_size(logger) ||
+ (strcmp("crash", name) && strcmp("security", name))) {
EXPECT_LT(0, android_logger_get_log_readable_size(logger));
}
EXPECT_LT(0, android_logger_get_log_version(logger));