#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>
}
#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
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];