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
This commit is contained in:
Mark Salyzyn 2015-12-04 10:59:45 -08:00
parent cb3e6ef154
commit 083b037c07
16 changed files with 304 additions and 53 deletions

View File

@ -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

View File

@ -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

View File

@ -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",
};

View File

@ -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

View File

@ -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 @@ 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<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 @@ 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));

View File

@ -283,10 +283,12 @@ static void show_help(const char *cmd)
" --buffer_size=<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 <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n"
" --buffer=<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=<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;
}

View File

@ -34,6 +34,7 @@
#include "CommandListener.h"
#include "LogCommand.h"
#include "LogUtils.h"
CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
LogListener * /*swl*/) :

View File

@ -21,6 +21,7 @@
#include "LogCommand.h"
#include "LogReader.h"
#include "LogTimes.h"
#include "LogUtils.h"
FlushCommand::FlushCommand(LogReader &reader,
bool nonBlock,

View File

@ -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;

View File

@ -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<android_event_header_t *>(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<android_log_event_string_t *>(buffer);

View File

@ -22,6 +22,7 @@
#include <private/android_filesystem_config.h>
#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());
}

View File

@ -26,6 +26,4 @@ public:
virtual ~LogCommand() {}
};
bool clientHasLogCredentials(SocketClient * cli);
#endif

View File

@ -27,6 +27,7 @@
#include <private/android_logger.h>
#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);

View File

@ -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;
}

View File

@ -397,6 +397,9 @@ class LogStatistics {
typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
tagTable_t tagTable;
// security tag list
tagTable_t securityTagTable;
public:
LogStatistics();

View File

@ -20,6 +20,7 @@
#include <sys/types.h>
#include <log/log.h>
#include <sysutils/SocketClient.h>
// 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 <int (*cmp)(const char *l, const char *r, const size_t s)>