1321 lines
36 KiB
Diff
1321 lines
36 KiB
Diff
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:58:45 +0200
|
|
Subject: [PATCH] kdbus: add code to gather metadata
|
|
|
|
A connection chooses which metadata it wants to have attached to each
|
|
message it receives with kdbus_cmd_hello.attach_flags. The metadata
|
|
will be attached as items to the messages. All metadata refers to
|
|
information about the sending task at sending time, unless otherwise
|
|
stated. Also, the metadata is copied, not referenced, so even if the
|
|
sending task doesn't exist anymore at the time the message is received,
|
|
the information is still preserved.
|
|
|
|
In traditional D-Bus, userspace tasks like polkit or journald make a
|
|
live lookup in procfs and sysfs to gain information about a sending
|
|
task. This is racy, of course, as in a a connection-less system like
|
|
D-Bus, the originating peer can go away immediately after sending the
|
|
message. As we're moving D-Bus prmitives into the kernel, we have to
|
|
provide the same semantics here, and inform the receiving peer on the
|
|
live credentials of the sending peer.
|
|
|
|
Metadata is collected at the following times.
|
|
|
|
* When a bus is created (KDBUS_CMD_MAKE), information about the
|
|
calling task is collected. This data is returned by the kernel
|
|
via the KDBUS_CMD_BUS_CREATOR_INFO call.
|
|
|
|
* When a connection is created (KDBUS_CMD_HELLO), information about
|
|
the calling task is collected. Alternatively, a privileged
|
|
connection may provide 'faked' information about credentials,
|
|
PIDs and security labels which will be stored instead. This data
|
|
is returned by the kernel as information on a connection
|
|
(KDBUS_CMD_CONN_INFO). Only metadata that a connection allowed to
|
|
be sent (by setting its bit in attach_flags_send) will be exported
|
|
in this way.
|
|
|
|
* When a message is sent (KDBUS_CMD_SEND), information about the
|
|
sending task and the sending connection are collected. This
|
|
metadata will be attached to the message when it arrives in the
|
|
receiver's pool. If the connection sending the message installed
|
|
faked credentials (see kdbus.connection(7)), the message will not
|
|
be augmented by any information about the currently sending task.
|
|
|
|
Which metadata items are actually delivered depends on the following
|
|
sets and masks:
|
|
|
|
(a) the system-wide kmod creds mask
|
|
(module parameter 'attach_flags_mask')
|
|
|
|
(b) the per-connection send creds mask, set by the connecting client
|
|
|
|
(c) the per-connection receive creds mask, set by the connecting client
|
|
|
|
(d) the per-bus minimal creds mask, set by the bus creator
|
|
|
|
(e) the per-bus owner creds mask, set by the bus creator
|
|
|
|
(f) the mask specified when querying creds of a bus peer
|
|
|
|
(g) the mask specified when querying creds of a bus owner
|
|
|
|
With the following rules:
|
|
|
|
[1] The creds attached to messages are determined as a & b & c.
|
|
|
|
[2] When connecting to a bus (KDBUS_CMD_HELLO), and ~b & d != 0,
|
|
the call will fail with, -1, and errno is set to ECONNREFUSED.
|
|
|
|
[3] When querying creds of a bus peer, the creds returned
|
|
are a & b & f.
|
|
|
|
[4] When querying creds of a bus owner, the creds returned
|
|
are a & e & g.
|
|
|
|
See kdbus.metadata(7) and kdbus.item(7) for more details on which
|
|
metadata can currently be attached to messages.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/metadata.c | 1164 ++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/metadata.h | 57 +++
|
|
2 files changed, 1221 insertions(+)
|
|
create mode 100644 ipc/kdbus/metadata.c
|
|
create mode 100644 ipc/kdbus/metadata.h
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
new file mode 100644
|
|
index 000000000000..06e0a54a276a
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -0,0 +1,1164 @@
|
|
+/*
|
|
+ * Copyright (C) 2013-2015 Kay Sievers
|
|
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
+ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org>
|
|
+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com>
|
|
+ * Copyright (C) 2013-2015 Linux Foundation
|
|
+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org>
|
|
+ *
|
|
+ * kdbus is free software; you can redistribute it and/or modify it under
|
|
+ * the terms of the GNU Lesser General Public License as published by the
|
|
+ * Free Software Foundation; either version 2.1 of the License, or (at
|
|
+ * your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/audit.h>
|
|
+#include <linux/capability.h>
|
|
+#include <linux/cgroup.h>
|
|
+#include <linux/cred.h>
|
|
+#include <linux/file.h>
|
|
+#include <linux/fs_struct.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/kref.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/security.h>
|
|
+#include <linux/sizes.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/uidgid.h>
|
|
+#include <linux/uio.h>
|
|
+#include <linux/user_namespace.h>
|
|
+#include <linux/version.h>
|
|
+
|
|
+#include "bus.h"
|
|
+#include "connection.h"
|
|
+#include "endpoint.h"
|
|
+#include "item.h"
|
|
+#include "message.h"
|
|
+#include "metadata.h"
|
|
+#include "names.h"
|
|
+
|
|
+/**
|
|
+ * struct kdbus_meta_proc - Process metadata
|
|
+ * @kref: Reference counting
|
|
+ * @lock: Object lock
|
|
+ * @collected: Bitmask of collected items
|
|
+ * @valid: Bitmask of collected and valid items
|
|
+ * @uid: UID of process
|
|
+ * @euid: EUID of process
|
|
+ * @suid: SUID of process
|
|
+ * @fsuid: FSUID of process
|
|
+ * @gid: GID of process
|
|
+ * @egid: EGID of process
|
|
+ * @sgid: SGID of process
|
|
+ * @fsgid: FSGID of process
|
|
+ * @pid: PID of process
|
|
+ * @tgid: TGID of process
|
|
+ * @ppid: PPID of process
|
|
+ * @auxgrps: Auxiliary groups
|
|
+ * @n_auxgrps: Number of items in @auxgrps
|
|
+ * @tid_comm: TID comm line
|
|
+ * @pid_comm: PID comm line
|
|
+ * @exe_path: Executable path
|
|
+ * @root_path: Root-FS path
|
|
+ * @cmdline: Command-line
|
|
+ * @cgroup: Full cgroup path
|
|
+ * @caps: Capabilities
|
|
+ * @caps_namespace: User-namespace of @caps
|
|
+ * @seclabel: Seclabel
|
|
+ * @audit_loginuid: Audit login-UID
|
|
+ * @audit_sessionid: Audit session-ID
|
|
+ */
|
|
+struct kdbus_meta_proc {
|
|
+ struct kref kref;
|
|
+ struct mutex lock;
|
|
+ u64 collected;
|
|
+ u64 valid;
|
|
+
|
|
+ /* KDBUS_ITEM_CREDS */
|
|
+ kuid_t uid, euid, suid, fsuid;
|
|
+ kgid_t gid, egid, sgid, fsgid;
|
|
+
|
|
+ /* KDBUS_ITEM_PIDS */
|
|
+ struct pid *pid;
|
|
+ struct pid *tgid;
|
|
+ struct pid *ppid;
|
|
+
|
|
+ /* KDBUS_ITEM_AUXGROUPS */
|
|
+ kgid_t *auxgrps;
|
|
+ size_t n_auxgrps;
|
|
+
|
|
+ /* KDBUS_ITEM_TID_COMM */
|
|
+ char tid_comm[TASK_COMM_LEN];
|
|
+ /* KDBUS_ITEM_PID_COMM */
|
|
+ char pid_comm[TASK_COMM_LEN];
|
|
+
|
|
+ /* KDBUS_ITEM_EXE */
|
|
+ struct path exe_path;
|
|
+ struct path root_path;
|
|
+
|
|
+ /* KDBUS_ITEM_CMDLINE */
|
|
+ char *cmdline;
|
|
+
|
|
+ /* KDBUS_ITEM_CGROUP */
|
|
+ char *cgroup;
|
|
+
|
|
+ /* KDBUS_ITEM_CAPS */
|
|
+ struct caps {
|
|
+ /* binary compatible to kdbus_caps */
|
|
+ u32 last_cap;
|
|
+ struct {
|
|
+ u32 caps[_KERNEL_CAPABILITY_U32S];
|
|
+ } set[4];
|
|
+ } caps;
|
|
+ struct user_namespace *caps_namespace;
|
|
+
|
|
+ /* KDBUS_ITEM_SECLABEL */
|
|
+ char *seclabel;
|
|
+
|
|
+ /* KDBUS_ITEM_AUDIT */
|
|
+ kuid_t audit_loginuid;
|
|
+ unsigned int audit_sessionid;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct kdbus_meta_conn
|
|
+ * @kref: Reference counting
|
|
+ * @lock: Object lock
|
|
+ * @collected: Bitmask of collected items
|
|
+ * @valid: Bitmask of collected and valid items
|
|
+ * @ts: Timestamp values
|
|
+ * @owned_names_items: Serialized items for owned names
|
|
+ * @owned_names_size: Size of @owned_names_items
|
|
+ * @conn_description: Connection description
|
|
+ */
|
|
+struct kdbus_meta_conn {
|
|
+ struct kref kref;
|
|
+ struct mutex lock;
|
|
+ u64 collected;
|
|
+ u64 valid;
|
|
+
|
|
+ /* KDBUS_ITEM_TIMESTAMP */
|
|
+ struct kdbus_timestamp ts;
|
|
+
|
|
+ /* KDBUS_ITEM_OWNED_NAME */
|
|
+ struct kdbus_item *owned_names_items;
|
|
+ size_t owned_names_size;
|
|
+
|
|
+ /* KDBUS_ITEM_CONN_DESCRIPTION */
|
|
+ char *conn_description;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_proc_new() - Create process metadata object
|
|
+ *
|
|
+ * Return: Pointer to new object on success, ERR_PTR on failure.
|
|
+ */
|
|
+struct kdbus_meta_proc *kdbus_meta_proc_new(void)
|
|
+{
|
|
+ struct kdbus_meta_proc *mp;
|
|
+
|
|
+ mp = kzalloc(sizeof(*mp), GFP_KERNEL);
|
|
+ if (!mp)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ kref_init(&mp->kref);
|
|
+ mutex_init(&mp->lock);
|
|
+
|
|
+ return mp;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_free(struct kref *kref)
|
|
+{
|
|
+ struct kdbus_meta_proc *mp = container_of(kref, struct kdbus_meta_proc,
|
|
+ kref);
|
|
+
|
|
+ path_put(&mp->exe_path);
|
|
+ path_put(&mp->root_path);
|
|
+ put_user_ns(mp->caps_namespace);
|
|
+ put_pid(mp->ppid);
|
|
+ put_pid(mp->tgid);
|
|
+ put_pid(mp->pid);
|
|
+
|
|
+ kfree(mp->seclabel);
|
|
+ kfree(mp->auxgrps);
|
|
+ kfree(mp->cmdline);
|
|
+ kfree(mp->cgroup);
|
|
+ kfree(mp);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_proc_ref() - Gain reference
|
|
+ * @mp: Process metadata object
|
|
+ *
|
|
+ * Return: @mp is returned
|
|
+ */
|
|
+struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ if (mp)
|
|
+ kref_get(&mp->kref);
|
|
+ return mp;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_proc_unref() - Drop reference
|
|
+ * @mp: Process metadata object
|
|
+ *
|
|
+ * Return: NULL
|
|
+ */
|
|
+struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ if (mp)
|
|
+ kref_put(&mp->kref, kdbus_meta_proc_free);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_collect_creds(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ mp->uid = current_uid();
|
|
+ mp->euid = current_euid();
|
|
+ mp->suid = current_suid();
|
|
+ mp->fsuid = current_fsuid();
|
|
+
|
|
+ mp->gid = current_gid();
|
|
+ mp->egid = current_egid();
|
|
+ mp->sgid = current_sgid();
|
|
+ mp->fsgid = current_fsgid();
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_CREDS;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ struct task_struct *parent;
|
|
+
|
|
+ mp->pid = get_pid(task_pid(current));
|
|
+ mp->tgid = get_pid(task_tgid(current));
|
|
+
|
|
+ rcu_read_lock();
|
|
+ parent = rcu_dereference(current->real_parent);
|
|
+ mp->ppid = get_pid(task_tgid(parent));
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_PIDS;
|
|
+}
|
|
+
|
|
+static int kdbus_meta_proc_collect_auxgroups(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ struct group_info *info;
|
|
+ size_t i;
|
|
+
|
|
+ info = get_current_groups();
|
|
+
|
|
+ if (info->ngroups > 0) {
|
|
+ mp->auxgrps = kmalloc_array(info->ngroups, sizeof(kgid_t),
|
|
+ GFP_KERNEL);
|
|
+ if (!mp->auxgrps) {
|
|
+ put_group_info(info);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < info->ngroups; i++)
|
|
+ mp->auxgrps[i] = GROUP_AT(info, i);
|
|
+ }
|
|
+
|
|
+ mp->n_auxgrps = info->ngroups;
|
|
+ put_group_info(info);
|
|
+ mp->valid |= KDBUS_ATTACH_AUXGROUPS;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_collect_tid_comm(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ get_task_comm(mp->tid_comm, current);
|
|
+ mp->valid |= KDBUS_ATTACH_TID_COMM;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ get_task_comm(mp->pid_comm, current->group_leader);
|
|
+ mp->valid |= KDBUS_ATTACH_PID_COMM;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ struct mm_struct *mm;
|
|
+
|
|
+ mm = get_task_mm(current);
|
|
+ if (!mm)
|
|
+ return;
|
|
+
|
|
+ down_read(&mm->mmap_sem);
|
|
+ if (mm->exe_file) {
|
|
+ mp->exe_path = mm->exe_file->f_path;
|
|
+ path_get(&mp->exe_path);
|
|
+ get_fs_root(current->fs, &mp->root_path);
|
|
+ mp->valid |= KDBUS_ATTACH_EXE;
|
|
+ }
|
|
+ up_read(&mm->mmap_sem);
|
|
+
|
|
+ mmput(mm);
|
|
+}
|
|
+
|
|
+static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ struct mm_struct *mm;
|
|
+ char *cmdline;
|
|
+
|
|
+ mm = get_task_mm(current);
|
|
+ if (!mm)
|
|
+ return 0;
|
|
+
|
|
+ if (!mm->arg_end) {
|
|
+ mmput(mm);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cmdline = strndup_user((const char __user *)mm->arg_start,
|
|
+ mm->arg_end - mm->arg_start);
|
|
+ mmput(mm);
|
|
+
|
|
+ if (IS_ERR(cmdline))
|
|
+ return PTR_ERR(cmdline);
|
|
+
|
|
+ mp->cmdline = cmdline;
|
|
+ mp->valid |= KDBUS_ATTACH_CMDLINE;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+#ifdef CONFIG_CGROUPS
|
|
+ void *page;
|
|
+ char *s;
|
|
+
|
|
+ page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
+ if (!page)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ s = task_cgroup_path(current, page, PAGE_SIZE);
|
|
+ if (s) {
|
|
+ mp->cgroup = kstrdup(s, GFP_KERNEL);
|
|
+ if (!mp->cgroup) {
|
|
+ free_page((unsigned long)page);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ free_page((unsigned long)page);
|
|
+ mp->valid |= KDBUS_ATTACH_CGROUP;
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_collect_caps(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ const struct cred *c = current_cred();
|
|
+ int i;
|
|
+
|
|
+ /* ABI: "last_cap" equals /proc/sys/kernel/cap_last_cap */
|
|
+ mp->caps.last_cap = CAP_LAST_CAP;
|
|
+ mp->caps_namespace = get_user_ns(current_user_ns());
|
|
+
|
|
+ CAP_FOR_EACH_U32(i) {
|
|
+ mp->caps.set[0].caps[i] = c->cap_inheritable.cap[i];
|
|
+ mp->caps.set[1].caps[i] = c->cap_permitted.cap[i];
|
|
+ mp->caps.set[2].caps[i] = c->cap_effective.cap[i];
|
|
+ mp->caps.set[3].caps[i] = c->cap_bset.cap[i];
|
|
+ }
|
|
+
|
|
+ /* clear unused bits */
|
|
+ for (i = 0; i < 4; i++)
|
|
+ mp->caps.set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &=
|
|
+ CAP_LAST_U32_VALID_MASK;
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_CAPS;
|
|
+}
|
|
+
|
|
+static int kdbus_meta_proc_collect_seclabel(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+#ifdef CONFIG_SECURITY
|
|
+ char *ctx = NULL;
|
|
+ u32 sid, len;
|
|
+ int ret;
|
|
+
|
|
+ security_task_getsecid(current, &sid);
|
|
+ ret = security_secid_to_secctx(sid, &ctx, &len);
|
|
+ if (ret < 0) {
|
|
+ /*
|
|
+ * EOPNOTSUPP means no security module is active,
|
|
+ * lets skip adding the seclabel then. This effectively
|
|
+ * drops the SECLABEL item.
|
|
+ */
|
|
+ return (ret == -EOPNOTSUPP) ? 0 : ret;
|
|
+ }
|
|
+
|
|
+ mp->seclabel = kstrdup(ctx, GFP_KERNEL);
|
|
+ security_release_secctx(ctx, len);
|
|
+ if (!mp->seclabel)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_SECLABEL;
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_proc_collect_audit(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+#ifdef CONFIG_AUDITSYSCALL
|
|
+ mp->audit_loginuid = audit_get_loginuid(current);
|
|
+ mp->audit_sessionid = audit_get_sessionid(current);
|
|
+ mp->valid |= KDBUS_ATTACH_AUDIT;
|
|
+#endif
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_proc_collect() - Collect process metadata
|
|
+ * @mp: Process metadata object
|
|
+ * @what: Attach flags to collect
|
|
+ *
|
|
+ * This collects process metadata from current and saves it in @mp.
|
|
+ *
|
|
+ * Return: 0 on success, negative error code on failure.
|
|
+ */
|
|
+int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!mp || !(what & (KDBUS_ATTACH_CREDS |
|
|
+ KDBUS_ATTACH_PIDS |
|
|
+ KDBUS_ATTACH_AUXGROUPS |
|
|
+ KDBUS_ATTACH_TID_COMM |
|
|
+ KDBUS_ATTACH_PID_COMM |
|
|
+ KDBUS_ATTACH_EXE |
|
|
+ KDBUS_ATTACH_CMDLINE |
|
|
+ KDBUS_ATTACH_CGROUP |
|
|
+ KDBUS_ATTACH_CAPS |
|
|
+ KDBUS_ATTACH_SECLABEL |
|
|
+ KDBUS_ATTACH_AUDIT)))
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&mp->lock);
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_CREDS) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_CREDS)) {
|
|
+ kdbus_meta_proc_collect_creds(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_CREDS;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_PIDS) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_PIDS)) {
|
|
+ kdbus_meta_proc_collect_pids(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_PIDS;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_AUXGROUPS) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_AUXGROUPS)) {
|
|
+ ret = kdbus_meta_proc_collect_auxgroups(mp);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ mp->collected |= KDBUS_ATTACH_AUXGROUPS;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_TID_COMM) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_TID_COMM)) {
|
|
+ kdbus_meta_proc_collect_tid_comm(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_TID_COMM;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_PID_COMM) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_PID_COMM)) {
|
|
+ kdbus_meta_proc_collect_pid_comm(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_PID_COMM;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_EXE) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_EXE)) {
|
|
+ kdbus_meta_proc_collect_exe(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_EXE;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_CMDLINE) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_CMDLINE)) {
|
|
+ ret = kdbus_meta_proc_collect_cmdline(mp);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ mp->collected |= KDBUS_ATTACH_CMDLINE;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_CGROUP) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_CGROUP)) {
|
|
+ ret = kdbus_meta_proc_collect_cgroup(mp);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ mp->collected |= KDBUS_ATTACH_CGROUP;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_CAPS) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_CAPS)) {
|
|
+ kdbus_meta_proc_collect_caps(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_CAPS;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_SECLABEL) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
|
|
+ ret = kdbus_meta_proc_collect_seclabel(mp);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ mp->collected |= KDBUS_ATTACH_SECLABEL;
|
|
+ }
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_AUDIT) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_AUDIT)) {
|
|
+ kdbus_meta_proc_collect_audit(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_AUDIT;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+exit_unlock:
|
|
+ mutex_unlock(&mp->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_proc_fake() - Fill process metadata from faked credentials
|
|
+ * @mp: Metadata
|
|
+ * @creds: Creds to set, may be %NULL
|
|
+ * @pids: PIDs to set, may be %NULL
|
|
+ * @seclabel: Seclabel to set, may be %NULL
|
|
+ *
|
|
+ * This function takes information stored in @creds, @pids and @seclabel and
|
|
+ * resolves them to kernel-representations, if possible. A call to this function
|
|
+ * is considered an alternative to calling kdbus_meta_add_current(), which
|
|
+ * derives the same information from the 'current' task.
|
|
+ *
|
|
+ * This call uses the current task's namespaces to resolve the given
|
|
+ * information.
|
|
+ *
|
|
+ * Return: 0 on success, negative error number otherwise.
|
|
+ */
|
|
+int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
|
|
+ const struct kdbus_creds *creds,
|
|
+ const struct kdbus_pids *pids,
|
|
+ const char *seclabel)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!mp)
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&mp->lock);
|
|
+
|
|
+ if (creds && !(mp->collected & KDBUS_ATTACH_CREDS)) {
|
|
+ struct user_namespace *ns = current_user_ns();
|
|
+
|
|
+ mp->uid = make_kuid(ns, creds->uid);
|
|
+ mp->euid = make_kuid(ns, creds->euid);
|
|
+ mp->suid = make_kuid(ns, creds->suid);
|
|
+ mp->fsuid = make_kuid(ns, creds->fsuid);
|
|
+
|
|
+ mp->gid = make_kgid(ns, creds->gid);
|
|
+ mp->egid = make_kgid(ns, creds->egid);
|
|
+ mp->sgid = make_kgid(ns, creds->sgid);
|
|
+ mp->fsgid = make_kgid(ns, creds->fsgid);
|
|
+
|
|
+ if ((creds->uid != (uid_t)-1 && !uid_valid(mp->uid)) ||
|
|
+ (creds->euid != (uid_t)-1 && !uid_valid(mp->euid)) ||
|
|
+ (creds->suid != (uid_t)-1 && !uid_valid(mp->suid)) ||
|
|
+ (creds->fsuid != (uid_t)-1 && !uid_valid(mp->fsuid)) ||
|
|
+ (creds->gid != (gid_t)-1 && !gid_valid(mp->gid)) ||
|
|
+ (creds->egid != (gid_t)-1 && !gid_valid(mp->egid)) ||
|
|
+ (creds->sgid != (gid_t)-1 && !gid_valid(mp->sgid)) ||
|
|
+ (creds->fsgid != (gid_t)-1 && !gid_valid(mp->fsgid))) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_CREDS;
|
|
+ mp->collected |= KDBUS_ATTACH_CREDS;
|
|
+ }
|
|
+
|
|
+ if (pids && !(mp->collected & KDBUS_ATTACH_PIDS)) {
|
|
+ mp->pid = get_pid(find_vpid(pids->tid));
|
|
+ mp->tgid = get_pid(find_vpid(pids->pid));
|
|
+ mp->ppid = get_pid(find_vpid(pids->ppid));
|
|
+
|
|
+ if ((pids->tid != 0 && !mp->pid) ||
|
|
+ (pids->pid != 0 && !mp->tgid) ||
|
|
+ (pids->ppid != 0 && !mp->ppid)) {
|
|
+ put_pid(mp->pid);
|
|
+ put_pid(mp->tgid);
|
|
+ put_pid(mp->ppid);
|
|
+ mp->pid = NULL;
|
|
+ mp->tgid = NULL;
|
|
+ mp->ppid = NULL;
|
|
+ ret = -EINVAL;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_PIDS;
|
|
+ mp->collected |= KDBUS_ATTACH_PIDS;
|
|
+ }
|
|
+
|
|
+ if (seclabel && !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
|
|
+ mp->seclabel = kstrdup(seclabel, GFP_KERNEL);
|
|
+ if (!mp->seclabel) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_SECLABEL;
|
|
+ mp->collected |= KDBUS_ATTACH_SECLABEL;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+exit_unlock:
|
|
+ mutex_unlock(&mp->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_conn_new() - Create connection metadata object
|
|
+ *
|
|
+ * Return: Pointer to new object on success, ERR_PTR on failure.
|
|
+ */
|
|
+struct kdbus_meta_conn *kdbus_meta_conn_new(void)
|
|
+{
|
|
+ struct kdbus_meta_conn *mc;
|
|
+
|
|
+ mc = kzalloc(sizeof(*mc), GFP_KERNEL);
|
|
+ if (!mc)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ kref_init(&mc->kref);
|
|
+ mutex_init(&mc->lock);
|
|
+
|
|
+ return mc;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_conn_free(struct kref *kref)
|
|
+{
|
|
+ struct kdbus_meta_conn *mc =
|
|
+ container_of(kref, struct kdbus_meta_conn, kref);
|
|
+
|
|
+ kfree(mc->conn_description);
|
|
+ kfree(mc->owned_names_items);
|
|
+ kfree(mc);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_conn_ref() - Gain reference
|
|
+ * @mc: Connection metadata object
|
|
+ */
|
|
+struct kdbus_meta_conn *kdbus_meta_conn_ref(struct kdbus_meta_conn *mc)
|
|
+{
|
|
+ if (mc)
|
|
+ kref_get(&mc->kref);
|
|
+ return mc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_conn_unref() - Drop reference
|
|
+ * @mc: Connection metadata object
|
|
+ */
|
|
+struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc)
|
|
+{
|
|
+ if (mc)
|
|
+ kref_put(&mc->kref, kdbus_meta_conn_free);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc,
|
|
+ struct kdbus_kmsg *kmsg)
|
|
+{
|
|
+ struct timespec ts;
|
|
+
|
|
+ ktime_get_ts(&ts);
|
|
+ mc->ts.monotonic_ns = timespec_to_ns(&ts);
|
|
+
|
|
+ ktime_get_real_ts(&ts);
|
|
+ mc->ts.realtime_ns = timespec_to_ns(&ts);
|
|
+
|
|
+ if (kmsg)
|
|
+ mc->ts.seqnum = kmsg->seq;
|
|
+
|
|
+ mc->valid |= KDBUS_ATTACH_TIMESTAMP;
|
|
+}
|
|
+
|
|
+static int kdbus_meta_conn_collect_names(struct kdbus_meta_conn *mc,
|
|
+ struct kdbus_conn *conn)
|
|
+{
|
|
+ const struct kdbus_name_entry *e;
|
|
+ struct kdbus_item *item;
|
|
+ size_t slen, size;
|
|
+
|
|
+ lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
|
|
+
|
|
+ size = 0;
|
|
+ list_for_each_entry(e, &conn->names_list, conn_entry)
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_name) +
|
|
+ strlen(e->name) + 1);
|
|
+
|
|
+ if (!size)
|
|
+ return 0;
|
|
+
|
|
+ item = kmalloc(size, GFP_KERNEL);
|
|
+ if (!item)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mc->owned_names_items = item;
|
|
+ mc->owned_names_size = size;
|
|
+
|
|
+ list_for_each_entry(e, &conn->names_list, conn_entry) {
|
|
+ slen = strlen(e->name) + 1;
|
|
+ kdbus_item_set(item, KDBUS_ITEM_OWNED_NAME, NULL,
|
|
+ sizeof(struct kdbus_name) + slen);
|
|
+ item->name.flags = e->flags;
|
|
+ memcpy(item->name.name, e->name, slen);
|
|
+ item = KDBUS_ITEM_NEXT(item);
|
|
+ }
|
|
+
|
|
+ /* sanity check: the buffer should be completely written now */
|
|
+ WARN_ON((u8 *)item != (u8 *)mc->owned_names_items + size);
|
|
+
|
|
+ mc->valid |= KDBUS_ATTACH_NAMES;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc,
|
|
+ struct kdbus_conn *conn)
|
|
+{
|
|
+ if (!conn->description)
|
|
+ return 0;
|
|
+
|
|
+ mc->conn_description = kstrdup(conn->description, GFP_KERNEL);
|
|
+ if (!mc->conn_description)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mc->valid |= KDBUS_ATTACH_CONN_DESCRIPTION;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_conn_collect() - Collect connection metadata
|
|
+ * @mc: Message metadata object
|
|
+ * @kmsg: Kmsg to collect data from
|
|
+ * @conn: Connection to collect data from
|
|
+ * @what: Attach flags to collect
|
|
+ *
|
|
+ * This collects connection metadata from @kmsg and @conn and saves it in @mc.
|
|
+ *
|
|
+ * If KDBUS_ATTACH_NAMES is set in @what and @conn is non-NULL, the caller must
|
|
+ * hold the name-registry read-lock of conn->ep->bus->registry.
|
|
+ *
|
|
+ * Return: 0 on success, negative error code on failure.
|
|
+ */
|
|
+int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
|
|
+ struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_conn *conn,
|
|
+ u64 what)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!mc || !(what & (KDBUS_ATTACH_TIMESTAMP |
|
|
+ KDBUS_ATTACH_NAMES |
|
|
+ KDBUS_ATTACH_CONN_DESCRIPTION)))
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&mc->lock);
|
|
+
|
|
+ if (kmsg && (what & KDBUS_ATTACH_TIMESTAMP) &&
|
|
+ !(mc->collected & KDBUS_ATTACH_TIMESTAMP)) {
|
|
+ kdbus_meta_conn_collect_timestamp(mc, kmsg);
|
|
+ mc->collected |= KDBUS_ATTACH_TIMESTAMP;
|
|
+ }
|
|
+
|
|
+ if (conn && (what & KDBUS_ATTACH_NAMES) &&
|
|
+ !(mc->collected & KDBUS_ATTACH_NAMES)) {
|
|
+ ret = kdbus_meta_conn_collect_names(mc, conn);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ mc->collected |= KDBUS_ATTACH_NAMES;
|
|
+ }
|
|
+
|
|
+ if (conn && (what & KDBUS_ATTACH_CONN_DESCRIPTION) &&
|
|
+ !(mc->collected & KDBUS_ATTACH_CONN_DESCRIPTION)) {
|
|
+ ret = kdbus_meta_conn_collect_description(mc, conn);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ mc->collected |= KDBUS_ATTACH_CONN_DESCRIPTION;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+exit_unlock:
|
|
+ mutex_unlock(&mc->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * kdbus_meta_export_prepare() - Prepare metadata for export
|
|
+ * @mp: Process metadata, or NULL
|
|
+ * @mc: Connection metadata, or NULL
|
|
+ * @mask: Pointer to mask of KDBUS_ATTACH_* flags to export
|
|
+ * @sz: Pointer to return the size needed by the metadata
|
|
+ *
|
|
+ * Does a conservative calculation of how much space metadata information
|
|
+ * will take up during export. It is 'conservative' because for string
|
|
+ * translations in namespaces, it will use the kernel namespaces, which is
|
|
+ * the longest possible version.
|
|
+ *
|
|
+ * The actual size consumed by kdbus_meta_export() may hence vary from the
|
|
+ * one reported here, but it is guaranteed never to be greater.
|
|
+ *
|
|
+ * Return: 0 on success, negative error number otherwise.
|
|
+ */
|
|
+int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 *mask, size_t *sz)
|
|
+{
|
|
+ char *exe_pathname = NULL;
|
|
+ void *exe_page = NULL;
|
|
+ size_t size = 0;
|
|
+ u64 valid = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (mp) {
|
|
+ mutex_lock(&mp->lock);
|
|
+ valid |= mp->valid;
|
|
+ mutex_unlock(&mp->lock);
|
|
+ }
|
|
+
|
|
+ if (mc) {
|
|
+ mutex_lock(&mc->lock);
|
|
+ valid |= mc->valid;
|
|
+ mutex_unlock(&mc->lock);
|
|
+ }
|
|
+
|
|
+ *mask &= valid;
|
|
+ *mask &= kdbus_meta_attach_mask;
|
|
+
|
|
+ if (!*mask)
|
|
+ goto exit;
|
|
+
|
|
+ /* process metadata */
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_CREDS))
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_PIDS))
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids));
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_AUXGROUPS))
|
|
+ size += KDBUS_ITEM_SIZE(mp->n_auxgrps * sizeof(u64));
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_TID_COMM))
|
|
+ size += KDBUS_ITEM_SIZE(strlen(mp->tid_comm) + 1);
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_PID_COMM))
|
|
+ size += KDBUS_ITEM_SIZE(strlen(mp->pid_comm) + 1);
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_EXE)) {
|
|
+ exe_page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
+ if (!exe_page) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ exe_pathname = d_path(&mp->exe_path, exe_page, PAGE_SIZE);
|
|
+ if (IS_ERR(exe_pathname)) {
|
|
+ ret = PTR_ERR(exe_pathname);
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ size += KDBUS_ITEM_SIZE(strlen(exe_pathname) + 1);
|
|
+ free_page((unsigned long)exe_page);
|
|
+ }
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_CMDLINE))
|
|
+ size += KDBUS_ITEM_SIZE(strlen(mp->cmdline) + 1);
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_CGROUP))
|
|
+ size += KDBUS_ITEM_SIZE(strlen(mp->cgroup) + 1);
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_CAPS))
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(mp->caps));
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_SECLABEL))
|
|
+ size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1);
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_AUDIT))
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_audit));
|
|
+
|
|
+ /* connection metadata */
|
|
+
|
|
+ if (mc && (*mask & KDBUS_ATTACH_NAMES))
|
|
+ size += mc->owned_names_size;
|
|
+
|
|
+ if (mc && (*mask & KDBUS_ATTACH_CONN_DESCRIPTION))
|
|
+ size += KDBUS_ITEM_SIZE(strlen(mc->conn_description) + 1);
|
|
+
|
|
+ if (mc && (*mask & KDBUS_ATTACH_TIMESTAMP))
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_timestamp));
|
|
+
|
|
+exit:
|
|
+ *sz = size;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int kdbus_meta_push_kvec(struct kvec *kvec,
|
|
+ struct kdbus_item_header *hdr,
|
|
+ u64 type, void *payload,
|
|
+ size_t payload_size, u64 *size)
|
|
+{
|
|
+ hdr->type = type;
|
|
+ hdr->size = KDBUS_ITEM_HEADER_SIZE + payload_size;
|
|
+ kdbus_kvec_set(kvec++, hdr, sizeof(*hdr), size);
|
|
+ kdbus_kvec_set(kvec++, payload, payload_size, size);
|
|
+ return 2 + !!kdbus_kvec_pad(kvec++, size);
|
|
+}
|
|
+
|
|
+/* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */
|
|
+static uid_t kdbus_from_kuid_keep(kuid_t uid)
|
|
+{
|
|
+ return uid_valid(uid) ?
|
|
+ from_kuid_munged(current_user_ns(), uid) : ((uid_t)-1);
|
|
+}
|
|
+
|
|
+/* This is equivalent to from_kgid_munged(), but maps INVALID_GID to itself */
|
|
+static gid_t kdbus_from_kgid_keep(kgid_t gid)
|
|
+{
|
|
+ return gid_valid(gid) ?
|
|
+ from_kgid_munged(current_user_ns(), gid) : ((gid_t)-1);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_export() - export information from metadata into a slice
|
|
+ * @mp: Process metadata, or NULL
|
|
+ * @mc: Connection metadata, or NULL
|
|
+ * @mask: Mask of KDBUS_ATTACH_* flags to export
|
|
+ * @slice: The slice to export to
|
|
+ * @offset: The offset inside @slice to write to
|
|
+ * @real_size: The real size the metadata consumed
|
|
+ *
|
|
+ * This function exports information from metadata into @slice at offset
|
|
+ * @offset inside that slice. Only information that is requested in @mask
|
|
+ * and that has been collected before is exported.
|
|
+ *
|
|
+ * In order to make sure not to write out of bounds, @mask must be the same
|
|
+ * value that was previously returned from kdbus_meta_export_prepare(). The
|
|
+ * function will, however, not necessarily write as many bytes as returned by
|
|
+ * kdbus_meta_export_prepare(); depending on the namespaces in question, it
|
|
+ * might use up less than that.
|
|
+ *
|
|
+ * All information will be translated using the current namespaces.
|
|
+ *
|
|
+ * Return: 0 on success, negative error number otherwise.
|
|
+ */
|
|
+int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 mask,
|
|
+ struct kdbus_pool_slice *slice,
|
|
+ off_t offset,
|
|
+ size_t *real_size)
|
|
+{
|
|
+ struct user_namespace *user_ns = current_user_ns();
|
|
+ struct kdbus_item_header item_hdr[13], *hdr;
|
|
+ char *exe_pathname = NULL;
|
|
+ struct kdbus_creds creds;
|
|
+ struct kdbus_pids pids;
|
|
+ void *exe_page = NULL;
|
|
+ struct kvec kvec[40];
|
|
+ u64 *auxgrps = NULL;
|
|
+ size_t cnt = 0;
|
|
+ u64 size = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ hdr = &item_hdr[0];
|
|
+
|
|
+ /*
|
|
+ * TODO: We currently have no sane way of translating a set of caps
|
|
+ * between different user namespaces. Until that changes, we have
|
|
+ * to drop such items.
|
|
+ */
|
|
+ if (mp && mp->caps_namespace != user_ns)
|
|
+ mask &= ~KDBUS_ATTACH_CAPS;
|
|
+
|
|
+ if (mask == 0) {
|
|
+ *real_size = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* process metadata */
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CREDS)) {
|
|
+ creds.uid = kdbus_from_kuid_keep(mp->uid);
|
|
+ creds.euid = kdbus_from_kuid_keep(mp->euid);
|
|
+ creds.suid = kdbus_from_kuid_keep(mp->suid);
|
|
+ creds.fsuid = kdbus_from_kuid_keep(mp->fsuid);
|
|
+ creds.gid = kdbus_from_kgid_keep(mp->gid);
|
|
+ creds.egid = kdbus_from_kgid_keep(mp->egid);
|
|
+ creds.sgid = kdbus_from_kgid_keep(mp->sgid);
|
|
+ creds.fsgid = kdbus_from_kgid_keep(mp->fsgid);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
+ &creds, sizeof(creds), &size);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_PIDS)) {
|
|
+ pids.pid = pid_vnr(mp->tgid);
|
|
+ pids.tid = pid_vnr(mp->pid);
|
|
+ pids.ppid = pid_vnr(mp->ppid);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
|
|
+ &pids, sizeof(pids), &size);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_AUXGROUPS)) {
|
|
+ size_t payload_size = mp->n_auxgrps * sizeof(u64);
|
|
+ int i;
|
|
+
|
|
+ auxgrps = kmalloc(payload_size, GFP_KERNEL);
|
|
+ if (!auxgrps) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < mp->n_auxgrps; i++)
|
|
+ auxgrps[i] = from_kgid_munged(user_ns, mp->auxgrps[i]);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_AUXGROUPS,
|
|
+ auxgrps, payload_size, &size);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_TID_COMM))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_TID_COMM, mp->tid_comm,
|
|
+ strlen(mp->tid_comm) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_PID_COMM))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_PID_COMM, mp->pid_comm,
|
|
+ strlen(mp->pid_comm) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_EXE)) {
|
|
+ struct path p;
|
|
+
|
|
+ /*
|
|
+ * TODO: We need access to __d_path() so we can write the path
|
|
+ * relative to conn->root_path. Once upstream, we need
|
|
+ * EXPORT_SYMBOL(__d_path) or an equivalent of d_path() that
|
|
+ * takes the root path directly. Until then, we drop this item
|
|
+ * if the root-paths differ.
|
|
+ */
|
|
+
|
|
+ get_fs_root(current->fs, &p);
|
|
+ if (path_equal(&p, &mp->root_path)) {
|
|
+ exe_page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
+ if (!exe_page) {
|
|
+ path_put(&p);
|
|
+ ret = -ENOMEM;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ exe_pathname = d_path(&mp->exe_path, exe_page,
|
|
+ PAGE_SIZE);
|
|
+ if (IS_ERR(exe_pathname)) {
|
|
+ path_put(&p);
|
|
+ ret = PTR_ERR(exe_pathname);
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_EXE,
|
|
+ exe_pathname,
|
|
+ strlen(exe_pathname) + 1,
|
|
+ &size);
|
|
+ }
|
|
+ path_put(&p);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CMDLINE))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CMDLINE, mp->cmdline,
|
|
+ strlen(mp->cmdline) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CGROUP))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CGROUP, mp->cgroup,
|
|
+ strlen(mp->cgroup) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CAPS))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CAPS, &mp->caps,
|
|
+ sizeof(mp->caps), &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_SECLABEL))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_SECLABEL, mp->seclabel,
|
|
+ strlen(mp->seclabel) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_AUDIT)) {
|
|
+ struct kdbus_audit a = {
|
|
+ .loginuid = from_kuid(user_ns, mp->audit_loginuid),
|
|
+ .sessionid = mp->audit_sessionid,
|
|
+ };
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_AUDIT,
|
|
+ &a, sizeof(a), &size);
|
|
+ }
|
|
+
|
|
+ /* connection metadata */
|
|
+
|
|
+ if (mc && (mask & KDBUS_ATTACH_NAMES))
|
|
+ kdbus_kvec_set(&kvec[cnt++], mc->owned_names_items,
|
|
+ mc->owned_names_size, &size);
|
|
+
|
|
+ if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CONN_DESCRIPTION,
|
|
+ mc->conn_description,
|
|
+ strlen(mc->conn_description) + 1,
|
|
+ &size);
|
|
+
|
|
+ if (mc && (mask & KDBUS_ATTACH_TIMESTAMP))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_TIMESTAMP, &mc->ts,
|
|
+ sizeof(mc->ts), &size);
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(slice, offset, kvec, cnt, size);
|
|
+ *real_size = size;
|
|
+
|
|
+exit:
|
|
+ kfree(auxgrps);
|
|
+
|
|
+ if (exe_page)
|
|
+ free_page((unsigned long)exe_page);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_calc_attach_flags() - calculate attach flags for a sender
|
|
+ * and a receiver
|
|
+ * @sender: Sending connection
|
|
+ * @receiver: Receiving connection
|
|
+ *
|
|
+ * Return: the attach flags both the sender and the receiver have opted-in
|
|
+ * for.
|
|
+ */
|
|
+u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender,
|
|
+ const struct kdbus_conn *receiver)
|
|
+{
|
|
+ return atomic64_read(&sender->attach_flags_send) &
|
|
+ atomic64_read(&receiver->attach_flags_recv);
|
|
+}
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
new file mode 100644
|
|
index 000000000000..42c942b34d2c
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -0,0 +1,57 @@
|
|
+/*
|
|
+ * Copyright (C) 2013-2015 Kay Sievers
|
|
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
+ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org>
|
|
+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com>
|
|
+ * Copyright (C) 2013-2015 Linux Foundation
|
|
+ * Copyright (C) 2014-2015 Djalal Harouni
|
|
+ *
|
|
+ * kdbus is free software; you can redistribute it and/or modify it under
|
|
+ * the terms of the GNU Lesser General Public License as published by the
|
|
+ * Free Software Foundation; either version 2.1 of the License, or (at
|
|
+ * your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef __KDBUS_METADATA_H
|
|
+#define __KDBUS_METADATA_H
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+
|
|
+struct kdbus_conn;
|
|
+struct kdbus_kmsg;
|
|
+struct kdbus_pool_slice;
|
|
+
|
|
+struct kdbus_meta_proc;
|
|
+struct kdbus_meta_conn;
|
|
+
|
|
+extern unsigned long long kdbus_meta_attach_mask;
|
|
+
|
|
+struct kdbus_meta_proc *kdbus_meta_proc_new(void);
|
|
+struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp);
|
|
+struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp);
|
|
+int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what);
|
|
+int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
|
|
+ const struct kdbus_creds *creds,
|
|
+ const struct kdbus_pids *pids,
|
|
+ const char *seclabel);
|
|
+
|
|
+struct kdbus_meta_conn *kdbus_meta_conn_new(void);
|
|
+struct kdbus_meta_conn *kdbus_meta_conn_ref(struct kdbus_meta_conn *mc);
|
|
+struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc);
|
|
+int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
|
|
+ struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_conn *conn,
|
|
+ u64 what);
|
|
+
|
|
+int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 *mask, size_t *sz);
|
|
+int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 mask,
|
|
+ struct kdbus_pool_slice *slice,
|
|
+ off_t offset, size_t *real_size);
|
|
+u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender,
|
|
+ const struct kdbus_conn *receiver);
|
|
+
|
|
+#endif
|