diff --git a/include/log/log.h b/include/log/log.h index 619bf230c..3d9240dbb 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -604,7 +604,8 @@ typedef enum log_id { LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, LOG_ID_CRASH = 4, - LOG_ID_KERNEL = 5, + LOG_ID_SECURITY = 5, + LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */ #endif LOG_ID_MAX diff --git a/include/log/logd.h b/include/log/logd.h index 0fe515f2a..b7aedaff6 100644 --- a/include/log/logd.h +++ b/include/log/logd.h @@ -44,6 +44,8 @@ int __android_log_btwrite(int32_t tag, char type, const void *payload, size_t len); int __android_log_bswrite(int32_t tag, const char *payload); +int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len); + #ifdef __cplusplus } #endif diff --git a/liblog/log_read.c b/liblog/log_read.c index 4503219d3..8aa887b21 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c @@ -208,6 +208,7 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [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 11c6d9cc8..c2b0ec2ea 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -84,7 +84,7 @@ static void unlock() #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 int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) 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 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) 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 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) 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 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) 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 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) 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 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [LOG_ID_EVENTS] = "events", [LOG_ID_SYSTEM] = "system", [LOG_ID_CRASH] = "crash", + [LOG_ID_SECURITY] = "security", [LOG_ID_KERNEL] = "kernel", }; @@ -483,6 +526,18 @@ int __android_log_bwrite(int32_t tag, const void *payload, size_t len) 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 c2bd82dd1..621101c25 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -25,6 +25,7 @@ #include #include #include +#include // 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 @@ TEST(liblog, __security) { 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((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((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; @@ -651,7 +742,8 @@ TEST(liblog, android_logger_get_) { 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)); diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 32bfd9c2c..ddc91caae 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -283,10 +283,12 @@ static void show_help(const char *cmd) " --buffer_size=\n" " -L dump logs from prior to last reboot\n" " --last\n" + // Leave security (Device Owner only installations) and + // kernel (userdebug and eng) buffers undocumented. " -b Request alternate ring buffer, 'main', 'system', 'radio',\n" - " --buffer= 'events', 'crash' or 'all'. Multiple -b parameters are\n" - " allowed and results are interleaved. The default is\n" - " -b main -b system -b crash.\n" + " --buffer= 'events', 'crash', 'default' or 'all'. Multiple -b\n" + " parameters are allowed and results are interleaved. The\n" + " default is -b main -b system -b crash.\n" " -B output the log in binary.\n" " --binary\n" " -S output statistics.\n" @@ -696,16 +698,20 @@ int main(int argc, char **argv) break; case 'b': { - if (strcmp(optarg, "all") == 0) { - while (devices) { - dev = devices; - devices = dev->next; - delete dev; - } + if (strcmp(optarg, "default") == 0) { + for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { + switch (i) { + case LOG_ID_SECURITY: + case LOG_ID_EVENTS: + continue; + case LOG_ID_MAIN: + case LOG_ID_SYSTEM: + case LOG_ID_CRASH: + break; + default: + continue; + } - devices = dev = NULL; - g_devCount = 0; - for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { const char *name = android_log_id_to_name((log_id_t)i); log_id_t log_id = android_name_to_log_id(name); @@ -713,7 +719,58 @@ int main(int argc, char **argv) continue; } - bool binary = strcmp(name, "events") == 0; + bool found = false; + for (dev = devices; dev; dev = dev->next) { + if (!strcmp(optarg, dev->device)) { + found = true; + break; + } + if (!dev->next) { + break; + } + } + if (found) { + break; + } + + log_device_t* d = new log_device_t(name, false); + + if (dev) { + dev->next = d; + dev = d; + } else { + devices = dev = d; + } + g_devCount++; + } + break; + } + + if (strcmp(optarg, "all") == 0) { + for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { + const char *name = android_log_id_to_name((log_id_t)i); + log_id_t log_id = android_name_to_log_id(name); + + if (log_id != (log_id_t)i) { + continue; + } + + bool found = false; + for (dev = devices; dev; dev = dev->next) { + if (!strcmp(optarg, dev->device)) { + found = true; + break; + } + if (!dev->next) { + break; + } + } + if (found) { + break; + } + + bool binary = !strcmp(name, "events") || + !strcmp(name, "security"); log_device_t* d = new log_device_t(name, binary); if (dev) { @@ -727,14 +784,21 @@ int main(int argc, char **argv) break; } - bool binary = strcmp(optarg, "events") == 0; + bool binary = !(strcmp(optarg, "events") && + strcmp(optarg, "security")); if (devices) { dev = devices; while (dev->next) { + if (!strcmp(optarg, dev->device)) { + dev = NULL; + break; + } dev = dev->next; } - dev->next = new log_device_t(optarg, binary); + if (dev) { + dev->next = new log_device_t(optarg, binary); + } } else { devices = new log_device_t(optarg, binary); } @@ -1004,7 +1068,7 @@ int main(int argc, char **argv) size_t len = 8192; char *buf; - for(int retry = 32; + for (int retry = 32; (retry >= 0) && ((buf = new char [len])); delete [] buf, buf = NULL, --retry) { if (getPruneList) { @@ -1094,7 +1158,7 @@ int main(int argc, char **argv) logcat_panic(false, "logcat read failure"); } - for(d = devices; d; d = d->next) { + for (d = devices; d; d = d->next) { if (android_name_to_log_id(d->device) == log_msg.id()) { break; } diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp index c45111aa5..e10335916 100644 --- a/logd/CommandListener.cpp +++ b/logd/CommandListener.cpp @@ -34,6 +34,7 @@ #include "CommandListener.h" #include "LogCommand.h" +#include "LogUtils.h" CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, LogListener * /*swl*/) : diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp index bf650cdaa..cb3d1c278 100644 --- a/logd/FlushCommand.cpp +++ b/logd/FlushCommand.cpp @@ -21,6 +21,7 @@ #include "LogCommand.h" #include "LogReader.h" #include "LogTimes.h" +#include "LogUtils.h" FlushCommand::FlushCommand(LogReader &reader, bool nonBlock, diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp index 45b9861bf..1e4f3b01e 100644 --- a/logd/LogBuffer.cpp +++ b/logd/LogBuffer.cpp @@ -199,22 +199,24 @@ int LogBuffer::log(log_id_t log_id, log_time realtime, LogBufferElement *elem = new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len); - int prio = ANDROID_LOG_INFO; - const char *tag = NULL; - if (log_id == LOG_ID_EVENTS) { - tag = android::tagToName(elem->getTag()); - } else { - prio = *msg; - tag = msg + 1; - } - if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) { - // Log traffic received to total - pthread_mutex_lock(&mLogElementsLock); - stats.add(elem); - stats.subtract(elem); - pthread_mutex_unlock(&mLogElementsLock); - delete elem; - return -EACCES; + if (log_id != LOG_ID_SECURITY) { + int prio = ANDROID_LOG_INFO; + const char *tag = NULL; + if (log_id == LOG_ID_EVENTS) { + tag = android::tagToName(elem->getTag()); + } else { + prio = *msg; + tag = msg + 1; + } + if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) { + // Log traffic received to total + pthread_mutex_lock(&mLogElementsLock); + stats.add(elem); + stats.subtract(elem); + pthread_mutex_unlock(&mLogElementsLock); + delete elem; + return -EACCES; + } } pthread_mutex_lock(&mLogElementsLock); @@ -484,7 +486,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) { } // prune by worst offender by uid - bool hasBlacklist = mPrune.naughty(); + bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty(); while (!clearAll && (pruneRows > 0)) { // recalculate the worst offender on every batched pass uid_t worst = (uid_t) -1; @@ -654,7 +656,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) { } bool whitelist = false; - bool hasWhitelist = mPrune.nice() && !clearAll; + bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll; it = mLogElements.begin(); while((pruneRows > 0) && (it != mLogElements.end())) { LogBufferElement *e = *it; diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp index e1d367f52..fde9ad7e3 100644 --- a/logd/LogBufferElement.cpp +++ b/logd/LogBufferElement.cpp @@ -51,7 +51,8 @@ LogBufferElement::~LogBufferElement() { } uint32_t LogBufferElement::getTag() const { - if ((mLogId != LOG_ID_EVENTS) || !mMsg || (mMsgLen < sizeof(uint32_t))) { + if (((mLogId != LOG_ID_EVENTS) && (mLogId != LOG_ID_SECURITY)) || + !mMsg || (mMsgLen < sizeof(uint32_t))) { return 0; } return le32toh(reinterpret_cast(mMsg)->tag); @@ -158,7 +159,9 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer, mDropped, (mDropped > 1) ? "s" : ""); size_t hdrLen; - if (mLogId == LOG_ID_EVENTS) { + // LOG_ID_SECURITY not strictly needed since spam filter not activated, + // but required for accuracy. + if ((mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY)) { hdrLen = sizeof(android_log_event_string_t); } else { hdrLen = 1 + sizeof(tag); @@ -172,7 +175,7 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer, } size_t retval = hdrLen + len; - if (mLogId == LOG_ID_EVENTS) { + if ((mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY)) { android_log_event_string_t *event = reinterpret_cast(buffer); diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp index 6d0e92e4e..3b1757656 100644 --- a/logd/LogCommand.cpp +++ b/logd/LogCommand.cpp @@ -22,6 +22,7 @@ #include #include "LogCommand.h" +#include "LogUtils.h" LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) { } @@ -56,20 +57,18 @@ static bool groupIsLog(char *buf) { return false; } -bool clientHasLogCredentials(SocketClient * cli) { - uid_t uid = cli->getUid(); - if (uid == AID_ROOT) { +bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid) { + if ((uid == AID_ROOT) || (uid == AID_SYSTEM) || (uid == AID_LOG)) { return true; } - gid_t gid = cli->getGid(); if ((gid == AID_ROOT) || (gid == AID_SYSTEM) || (gid == AID_LOG)) { return true; } // FYI We will typically be here for 'adb logcat' char filename[256]; - snprintf(filename, sizeof(filename), "/proc/%u/status", cli->getPid()); + snprintf(filename, sizeof(filename), "/proc/%u/status", pid); bool ret; bool foundLog = false; @@ -145,3 +144,7 @@ bool clientHasLogCredentials(SocketClient * cli) { return ret; } + +bool clientHasLogCredentials(SocketClient *cli) { + return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid()); +} diff --git a/logd/LogCommand.h b/logd/LogCommand.h index e3b96a2b7..c944478f8 100644 --- a/logd/LogCommand.h +++ b/logd/LogCommand.h @@ -26,6 +26,4 @@ public: virtual ~LogCommand() {} }; -bool clientHasLogCredentials(SocketClient * cli); - #endif diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp index b29f5ab92..9bbf9e84b 100644 --- a/logd/LogListener.cpp +++ b/logd/LogListener.cpp @@ -27,6 +27,7 @@ #include #include "LogListener.h" +#include "LogUtils.h" LogListener::LogListener(LogBuffer *buf, LogReader *reader) : SocketListener(getLogSocket(), false), @@ -92,6 +93,12 @@ bool LogListener::onDataAvailable(SocketClient *cli) { return false; } + if ((header->id == LOG_ID_SECURITY) && + (!__android_log_security() || + !clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) { + return false; + } + char *msg = ((char *)buffer) + sizeof(android_log_header_t); n -= sizeof(android_log_header_t); diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp index 416edd8a1..bf0e30b09 100644 --- a/logd/LogStatistics.cpp +++ b/logd/LogStatistics.cpp @@ -85,7 +85,11 @@ void LogStatistics::add(LogBufferElement *element) { uint32_t tag = element->getTag(); if (tag) { - tagTable.add(tag, element); + if (log_id == LOG_ID_SECURITY) { + securityTagTable.add(tag, element); + } else { + tagTable.add(tag, element); + } } } @@ -113,7 +117,11 @@ void LogStatistics::subtract(LogBufferElement *element) { uint32_t tag = element->getTag(); if (tag) { - tagTable.subtract(tag, element); + if (log_id == LOG_ID_SECURITY) { + securityTagTable.subtract(tag, element); + } else { + tagTable.subtract(tag, element); + } } } @@ -468,6 +476,11 @@ std::string LogStatistics::format(uid_t uid, unsigned int logMask) const { output += tagTable.format(*this, uid, name, LOG_ID_EVENTS); } + if (enable && (logMask & (1 << LOG_ID_SECURITY))) { + name = "Chattiest security log buffer TAGs:"; + output += securityTagTable.format(*this, uid, name, LOG_ID_SECURITY); + } + return output; } diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h index 28810d9d5..8558b0671 100644 --- a/logd/LogStatistics.h +++ b/logd/LogStatistics.h @@ -397,6 +397,9 @@ class LogStatistics { typedef LogHashtable tagTable_t; tagTable_t tagTable; + // security tag list + tagTable_t securityTagTable; + public: LogStatistics(); diff --git a/logd/LogUtils.h b/logd/LogUtils.h index 533eb1c5f..b591f2892 100644 --- a/logd/LogUtils.h +++ b/logd/LogUtils.h @@ -20,6 +20,7 @@ #include #include +#include // Hijack this header as a common include file used by most all sources // to report some utilities defined here and there. @@ -38,8 +39,12 @@ const char *tagToName(uint32_t tag); } +// Furnished in LogCommand.cpp +bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid); +bool clientHasLogCredentials(SocketClient *cli); + static inline bool worstUidEnabledForLogid(log_id_t id) { - return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS); + return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) || (id == LOG_ID_RADIO); } template