Check necessary privilege for correct behaviour 18/217918/4
authorMichal Bloch <m.bloch@samsung.com>
Fri, 15 Nov 2019 13:35:36 +0000 (14:35 +0100)
committerMichal Bloch <m.bloch@samsung.com>
Mon, 9 Dec 2019 13:31:33 +0000 (14:31 +0100)
Change-Id: I10a5796292e4e166df86b4f49b7f47c01a914d21
Signed-off-by: Michal Bloch <m.bloch@samsung.com>
Makefile.am
include/logcommon.h
packaging/dlog.spec
src/logutil/logutil.c

index 087b003..608e2e0 100644 (file)
@@ -44,6 +44,7 @@ dlogutil_CFLAGS =  \
 
 dlogutil_LDFLAGS = \
        $(AM_LDFLAGS) \
+       -lcap \
        -pie
 
 dlogutil_SOURCES = \
index 42d16e1..7c4ced1 100644 (file)
@@ -39,6 +39,7 @@
 #define DBG(...) do { } while (0)
 #endif
 #define ERR(...) fprintf(stderr, __VA_ARGS__)
+#define FAIL_ERR(...) do { ERR(__VA_ARGS__); return false; } while (0)
 
 #define gettid() syscall(SYS_gettid)
 
index 7a122f0..e2c0052 100644 (file)
@@ -20,6 +20,7 @@ Source502:  packaging/logger-devices.conf
 
 BuildRequires: autoconf
 BuildRequires: automake
+BuildRequires: libcap-devel
 BuildRequires: libtool
 BuildRequires: pkgconfig(capi-base-common)
 BuildRequires: pkgconfig(libtzplatform-config)
index 4fe5b29..2d90db5 100644 (file)
 
 #include <fcntl.h>
 #include <getopt.h>
+#include <grp.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/capability.h>
 #include <sys/epoll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include <fd_info.h>
 #include <fdi_pipe.h>
@@ -192,6 +198,94 @@ enomem:
 }
 
 #ifndef UNIT_TEST
+static inline void cap_free_ptr(cap_t *cap)
+{
+       if (!cap)
+               return;
+       cap_free(*cap);
+}
+
+static bool has_cap_syslog()
+{
+       __attribute__((cleanup(cap_free_ptr))) cap_t cap = cap_get_proc();
+       if (!cap)
+               FAIL_ERR("couldn't get caps: %m");
+
+       cap_flag_value_t val;
+       if (cap_get_flag(cap, CAP_SYSLOG, CAP_EFFECTIVE, &val) < 0)
+               FAIL_ERR("couldn't get cap flags: %m");
+
+       return val == CAP_SET;
+}
+
+static bool file_is_in_group(int fd, gid_t *groups, int ngroups)
+{
+       struct stat stat_buf;
+       if (fstat(fd, &stat_buf) < 0)
+               FAIL_ERR("fstat failed: %m");
+
+       for (int i = 0; i < ngroups; ++i)
+               if (stat_buf.st_gid == groups[i])
+                       return true;
+
+       return false;
+}
+
+static bool has_matching_gid(struct fd_info **fdi_ptrs, int fdi_cnt)
+{
+       long pw_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+       if (pw_size <= 0)
+               FAIL_ERR("sysconf(_SC_GETPW_R_SIZE_MAX) failure");
+
+       char namebuf[pw_size];
+       struct passwd pw, *pw_ptr = NULL;
+
+       if (getpwuid_r(geteuid(), &pw, namebuf, sizeof namebuf, &pw_ptr) < 0)
+               FAIL_ERR("getpwuid_r failure: %m");
+       if (!pw_ptr)
+               FAIL_ERR("getpwuid_r couldn't find our user data");
+
+       int ngroups = 0;
+       getgrouplist(pw.pw_name, pw.pw_gid, NULL, &ngroups); // returns -1
+       if (ngroups <= 0)
+               FAIL_ERR("getgrouplist internal failure: %m");
+
+       gid_t groups[ngroups];
+       if (getgrouplist(pw.pw_name, pw.pw_gid, groups, &ngroups) < 0)
+               FAIL_ERR("getgrouplist internal failure: %m");
+
+       for (int i = 0; i < fdi_cnt; ++i)
+               if (!file_is_in_group(fdi_ptrs[i]->fd, groups, ngroups))
+                       return false;
+
+       return true;
+}
+
+static bool has_required_privilege(action_e action, bool is_pipe, size_t mode, struct fd_info **fdi_ptrs, int fdi_cnt)
+{
+       /* Pipe-backend checks privilege on socket connection correctly,
+        * only Android Logger mishandles it (see `fdi_logger.c`) */
+       if (is_pipe)
+               return true;
+
+       /* Lack of privilege increases the total amount of logs,
+        * so for an infinite amount it does not matter.
+        *
+        * Keep in mind the dump mode is not an infinite amount,
+        * but an arbitrarily large finite amount (it only dumps
+        * as much as the buffer contains at invocation and will
+        * not go further). Only the continuous mode is infinite. */
+       if (mode == DLOGUTIL_MODE_CONTINUOUS)
+               return true;
+
+       /* Only reading is affected by the privilege bug.
+        * It's OK to be write. */
+       if (action != ACTION_PRINT)
+               return true;
+
+       return has_cap_syslog() || has_matching_gid(fdi_ptrs, fdi_cnt);
+}
+
 int main(int argc, char **argv)
 {
        int enabled_buffers = 0; // bitset
@@ -264,6 +358,11 @@ int main(int argc, char **argv)
                return 1;
        }
 
+       if (!has_required_privilege(action, is_pipe, opt.logs_dump, fdi_ptrs, fdi_cnt)) {
+               ERR("dlogutil requires CAP_SYSLOG. Check your privilege!\n");
+               return 1;
+       }
+
        for (i = 0; i < fdi_cnt; ++i) {
                struct fd_info *const fdi = fdi_ptrs[i];