logd: don't drop user/groups/capabilities/priority in drop_privs()

On Android, unlike POSIX, groups and capabilities are able to be set
per thread.  This is useless however, since threads are not a security
boundary.  This change drops the logic to set groups and capabilities
per thread and instead leaves all threads running with the initial
user and groups.

This does still drop some capabilities if they're unneeded due to
features being disabled.

This also moves the setpriority() call from code into the init script.

Test: logd runs with the expected user/groups and with the expected
      capabilities and priority without any errors

Change-Id: Ibb0e529ea1574a2b8ec391a2678504ca9fbe19be
This commit is contained in:
Tom Cherry 2019-06-06 13:41:20 -07:00
parent 44cabca1ef
commit 0b2a011cc4
2 changed files with 27 additions and 111 deletions

View file

@ -6,7 +6,8 @@ service logd /system/bin/logd
file /dev/kmsg w
user logd
group logd system package_info readproc
capabilities SYSLOG AUDIT_CONTROL SETGID
capabilities SYSLOG AUDIT_CONTROL
priority 10
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit

View file

@ -17,6 +17,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/capability.h>
#include <poll.h>
#include <sched.h>
#include <semaphore.h>
@ -57,35 +58,10 @@
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
//
// The service is designed to be run by init, it does not respond well
// to starting up manually. When starting up manually the sockets will
// fail to open typically for one of the following reasons:
// EADDRINUSE if logger is running.
// EACCESS if started without precautions (below)
//
// Here is a cookbook procedure for starting up logd manually assuming
// init is out of the way, pedantically all permissions and SELinux
// security is put back in place:
//
// setenforce 0
// rm /dev/socket/logd*
// chmod 777 /dev/socket
// # here is where you would attach the debugger or valgrind for example
// runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 &
// sleep 1
// chmod 755 /dev/socket
// chown logd.logd /dev/socket/logd*
// restorecon /dev/socket/logd*
// setenforce 1
//
// If minimalism prevails, typical for debugging and security is not a concern:
//
// setenforce 0
// chmod 777 /dev/socket
// logd
//
// The service is designed to be run by init, it does not respond well to starting up manually. Init
// has a 'sigstop' feature that sends SIGSTOP to a service immediately before calling exec(). This
// allows debuggers, etc to be attached to logd at the very beginning, while still having init
// handle the user, groups, capabilities, files, etc setup.
static int drop_privs(bool klogd, bool auditd) {
sched_param param = {};
@ -99,11 +75,6 @@ static int drop_privs(bool klogd, bool auditd) {
return -1;
}
if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
android::prdebug("failed to set background cgroup");
return -1;
}
if (!__android_logger_property_get_bool("ro.debuggable",
BOOL_DEFAULT_FALSE) &&
prctl(PR_SET_DUMPABLE, 0) == -1) {
@ -111,52 +82,26 @@ static int drop_privs(bool klogd, bool auditd) {
return -1;
}
std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
cap_free);
if (cap_clear(caps.get()) < 0) return -1;
cap_value_t cap_value[] = { CAP_SETGID, // must be first for below
klogd ? CAP_SYSLOG : CAP_SETGID,
auditd ? CAP_AUDIT_CONTROL : CAP_SETGID };
if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(cap_value), cap_value,
CAP_SET) < 0) {
std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
if (cap_clear(caps.get()) < 0) {
return -1;
}
if (cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(cap_value), cap_value,
CAP_SET) < 0) {
std::vector<cap_value_t> cap_value;
if (klogd) {
cap_value.emplace_back(CAP_SYSLOG);
}
if (auditd) {
cap_value.emplace_back(CAP_AUDIT_CONTROL);
}
if (cap_set_flag(caps.get(), CAP_PERMITTED, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
return -1;
}
if (cap_set_flag(caps.get(), CAP_EFFECTIVE, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
return -1;
}
if (cap_set_proc(caps.get()) < 0) {
android::prdebug(
"failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
errno);
return -1;
}
gid_t groups[] = { AID_READPROC };
if (setgroups(arraysize(groups), groups) == -1) {
android::prdebug("failed to set AID_READPROC groups");
return -1;
}
if (setgid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD gid");
return -1;
}
if (setuid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD uid");
return -1;
}
if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
return -1;
}
if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR) < 0) {
return -1;
}
if (cap_set_proc(caps.get()) < 0) {
android::prdebug("failed to clear CAP_SETGID (%d)", errno);
android::prdebug("failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
return -1;
}
@ -227,30 +172,6 @@ static bool package_list_parser_cb(pkg_info* info, void* /* userdata */) {
static void* reinit_thread_start(void* /*obj*/) {
prctl(PR_SET_NAME, "logd.daemon");
set_sched_policy(0, SP_BACKGROUND);
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
// We should drop to AID_LOGD, if we are anything else, we have
// even lesser privileges and accept our fate.
gid_t groups[] = {
AID_SYSTEM, // search access to /data/system path
AID_PACKAGE_INFO, // readonly access to /data/system/packages.list
};
if (setgroups(arraysize(groups), groups) == -1) {
android::prdebug(
"logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
}
if (setgid(AID_LOGD) != 0) {
android::prdebug("logd.daemon: failed to set AID_LOGD gid");
}
if (setuid(AID_LOGD) != 0) {
android::prdebug("logd.daemon: failed to set AID_LOGD uid");
}
cap_t caps = cap_init();
(void)cap_clear(caps);
(void)cap_set_proc(caps);
(void)cap_free(caps);
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
// uidToName Privileged Worker
@ -373,11 +294,6 @@ static void readDmesg(LogAudit* al, LogKlog* kl) {
}
static int issueReinit() {
cap_t caps = cap_init();
(void)cap_clear(caps);
(void)cap_set_proc(caps);
(void)cap_free(caps);
int sock = TEMP_FAILURE_RETRY(socket_local_client(
"logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
if (sock < 0) return -errno;
@ -440,6 +356,11 @@ int main(int argc, char* argv[]) {
if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
}
bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
if (drop_privs(klogd, auditd) != 0) {
return EXIT_FAILURE;
}
// Reinit Thread
sem_init(&reinit, 0, 0);
sem_init(&uidName, 0, 0);
@ -461,12 +382,6 @@ int main(int argc, char* argv[]) {
pthread_attr_destroy(&attr);
}
bool auditd =
__android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
if (drop_privs(klogd, auditd) != 0) {
return EXIT_FAILURE;
}
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
// entries.