summaryrefslogtreecommitdiff
path: root/ipc/kdbus/metadata.c
diff options
context:
space:
mode:
authorKarol Lewandowski <k.lewandowsk@samsung.com>2016-10-06 14:20:24 +0200
committerSeung-Woo Kim <sw0312.kim@samsung.com>2016-12-14 13:53:39 +0900
commitd8934e9b05b82e73a3c78421f372197801c1785e (patch)
tree24981389ae0ca646a7997f6e32ea7a39aea147b7 /ipc/kdbus/metadata.c
parentea4654706ad61bc7afc47e6321c5cbee8c175688 (diff)
kdbus: Upgrade driver to newer upstream version
This commit upgrades kdbus ipc driver from v4 patchset, as posted on lkml for review by Greg Kroah-Hartman on Mar 09 2015 to commit 0c05fbdc82f from new upstream kdbus repository (git://github.com/systemd/kdbus). Summary of major changes: * message importer rewritten - considerably reduces internal message processing overhead, * name registration reworked to follow DBus Specification precisely, * attached metadata now follow /proc access checks * reduced in-kernel stack buffer to 256 bytes for small messages Change-Id: I6d849173b4289e1b684ed1a9b48e6e0b361e5d53
Diffstat (limited to 'ipc/kdbus/metadata.c')
-rw-r--r--ipc/kdbus/metadata.c1156
1 files changed, 672 insertions, 484 deletions
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
index 501bebda7d84..71ca475a80d5 100644
--- a/ipc/kdbus/metadata.c
+++ b/ipc/kdbus/metadata.c
@@ -29,7 +29,6 @@
#include <linux/uidgid.h>
#include <linux/uio.h>
#include <linux/user_namespace.h>
-#include <linux/version.h>
#include "bus.h"
#include "connection.h"
@@ -45,27 +44,16 @@
* @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
+ * @cred: Credentials
* @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
@@ -77,18 +65,15 @@ struct kdbus_meta_proc {
u64 valid;
/* KDBUS_ITEM_CREDS */
- kuid_t uid, euid, suid, fsuid;
- kgid_t gid, egid, sgid, fsgid;
+ /* KDBUS_ITEM_AUXGROUPS */
+ /* KDBUS_ITEM_CAPS */
+ const struct cred *cred;
/* 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 */
@@ -104,16 +89,6 @@ struct kdbus_meta_proc {
/* 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;
@@ -150,6 +125,14 @@ struct kdbus_meta_conn {
char *conn_description;
};
+/* fixed size equivalent of "kdbus_caps" */
+struct kdbus_meta_caps {
+ u32 last_cap;
+ struct {
+ u32 caps[_KERNEL_CAPABILITY_U32S];
+ } set[4];
+};
+
/**
* kdbus_meta_proc_new() - Create process metadata object
*
@@ -176,13 +159,13 @@ static void kdbus_meta_proc_free(struct kref *kref)
path_put(&mp->exe_path);
path_put(&mp->root_path);
- put_user_ns(mp->caps_namespace);
+ if (mp->cred)
+ put_cred(mp->cred);
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);
@@ -214,21 +197,6 @@ struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp)
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;
@@ -244,32 +212,6 @@ static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp)
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);
@@ -284,42 +226,29 @@ static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp)
static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp)
{
- struct mm_struct *mm;
-
- mm = get_task_mm(current);
- if (!mm)
- return;
+ struct file *exe_file;
- down_read(&mm->mmap_sem);
- if (mm->exe_file) {
- mp->exe_path = mm->exe_file->f_path;
+ rcu_read_lock();
+ exe_file = rcu_dereference(current->mm->exe_file);
+ if (exe_file) {
+ mp->exe_path = 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);
+ rcu_read_unlock();
}
static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp)
{
- struct mm_struct *mm;
+ struct mm_struct *mm = current->mm;
char *cmdline;
- mm = get_task_mm(current);
- if (!mm)
- return 0;
-
- if (mm->arg_start >= mm->arg_end) {
- mmput(mm);
+ if (!mm->arg_end)
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);
@@ -355,30 +284,6 @@ static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp)
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
@@ -445,10 +350,17 @@ int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
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;
+ /* creds, auxgrps and caps share "struct cred" as context */
+ {
+ const u64 m_cred = KDBUS_ATTACH_CREDS |
+ KDBUS_ATTACH_AUXGROUPS |
+ KDBUS_ATTACH_CAPS;
+
+ if ((what & m_cred) && !(mp->collected & m_cred)) {
+ mp->cred = get_current_cred();
+ mp->valid |= m_cred;
+ mp->collected |= m_cred;
+ }
}
if ((what & KDBUS_ATTACH_PIDS) &&
@@ -457,14 +369,6 @@ int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
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);
@@ -499,12 +403,6 @@ int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
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);
@@ -527,101 +425,116 @@ exit_unlock:
}
/**
- * kdbus_meta_proc_fake() - Fill process metadata from faked credentials
- * @mp: Metadata
+ * kdbus_meta_fake_new() - Create fake metadata object
+ *
+ * Return: Pointer to new object on success, ERR_PTR on failure.
+ */
+struct kdbus_meta_fake *kdbus_meta_fake_new(void)
+{
+ struct kdbus_meta_fake *mf;
+
+ mf = kzalloc(sizeof(*mf), GFP_KERNEL);
+ if (!mf)
+ return ERR_PTR(-ENOMEM);
+
+ return mf;
+}
+
+/**
+ * kdbus_meta_fake_free() - Free fake metadata object
+ * @mf: Fake metadata object
+ *
+ * Return: NULL
+ */
+struct kdbus_meta_fake *kdbus_meta_fake_free(struct kdbus_meta_fake *mf)
+{
+ if (mf) {
+ put_pid(mf->ppid);
+ put_pid(mf->tgid);
+ put_pid(mf->pid);
+ kfree(mf->seclabel);
+ kfree(mf);
+ }
+
+ return NULL;
+}
+
+/**
+ * kdbus_meta_fake_collect() - Fill fake metadata from faked credentials
+ * @mf: Fake metadata object
* @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.
+ * resolves them to kernel-representations, if possible. This call uses the
+ * current task's namespaces to resolve the given information.
*
- * Return: 0 on success, negative error number otherwise.
+ * Return: 0 on success, negative error code on failure.
*/
-int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
- const struct kdbus_creds *creds,
- const struct kdbus_pids *pids,
- const char *seclabel)
+int kdbus_meta_fake_collect(struct kdbus_meta_fake *mf,
+ const struct kdbus_creds *creds,
+ const struct kdbus_pids *pids,
+ const char *seclabel)
{
- int ret;
+ if (mf->valid)
+ return -EALREADY;
- if (!mp)
- return 0;
-
- mutex_lock(&mp->lock);
-
- if (creds && !(mp->collected & KDBUS_ATTACH_CREDS)) {
+ if (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;
+ mf->uid = make_kuid(ns, creds->uid);
+ mf->euid = make_kuid(ns, creds->euid);
+ mf->suid = make_kuid(ns, creds->suid);
+ mf->fsuid = make_kuid(ns, creds->fsuid);
+
+ mf->gid = make_kgid(ns, creds->gid);
+ mf->egid = make_kgid(ns, creds->egid);
+ mf->sgid = make_kgid(ns, creds->sgid);
+ mf->fsgid = make_kgid(ns, creds->fsgid);
+
+ if ((creds->uid != (uid_t)-1 && !uid_valid(mf->uid)) ||
+ (creds->euid != (uid_t)-1 && !uid_valid(mf->euid)) ||
+ (creds->suid != (uid_t)-1 && !uid_valid(mf->suid)) ||
+ (creds->fsuid != (uid_t)-1 && !uid_valid(mf->fsuid)) ||
+ (creds->gid != (gid_t)-1 && !gid_valid(mf->gid)) ||
+ (creds->egid != (gid_t)-1 && !gid_valid(mf->egid)) ||
+ (creds->sgid != (gid_t)-1 && !gid_valid(mf->sgid)) ||
+ (creds->fsgid != (gid_t)-1 && !gid_valid(mf->fsgid)))
+ return -EINVAL;
+
+ mf->valid |= 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;
+ if (pids) {
+ mf->pid = get_pid(find_vpid(pids->tid));
+ mf->tgid = get_pid(find_vpid(pids->pid));
+ mf->ppid = get_pid(find_vpid(pids->ppid));
+
+ if ((pids->tid != 0 && !mf->pid) ||
+ (pids->pid != 0 && !mf->tgid) ||
+ (pids->ppid != 0 && !mf->ppid)) {
+ put_pid(mf->pid);
+ put_pid(mf->tgid);
+ put_pid(mf->ppid);
+ mf->pid = NULL;
+ mf->tgid = NULL;
+ mf->ppid = NULL;
+ return -EINVAL;
}
- mp->valid |= KDBUS_ATTACH_PIDS;
- mp->collected |= KDBUS_ATTACH_PIDS;
+ mf->valid |= KDBUS_ATTACH_PIDS;
}
- if (seclabel && !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
- mp->seclabel = kstrdup(seclabel, GFP_KERNEL);
- if (!mp->seclabel) {
- ret = -ENOMEM;
- goto exit_unlock;
- }
+ if (seclabel) {
+ mf->seclabel = kstrdup(seclabel, GFP_KERNEL);
+ if (!mf->seclabel)
+ return -ENOMEM;
- mp->valid |= KDBUS_ATTACH_SECLABEL;
- mp->collected |= KDBUS_ATTACH_SECLABEL;
+ mf->valid |= KDBUS_ATTACH_SECLABEL;
}
- ret = 0;
-
-exit_unlock:
- mutex_unlock(&mp->lock);
- return ret;
+ return 0;
}
/**
@@ -676,13 +589,13 @@ struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc)
}
static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc,
- struct kdbus_kmsg *kmsg)
+ u64 msg_seqnum)
{
mc->ts.monotonic_ns = ktime_get_ns();
mc->ts.realtime_ns = ktime_get_real_ns();
- if (kmsg)
- mc->ts.seqnum = kmsg->seq;
+ if (msg_seqnum)
+ mc->ts.seqnum = msg_seqnum;
mc->valid |= KDBUS_ATTACH_TIMESTAMP;
}
@@ -690,38 +603,46 @@ static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc,
static int kdbus_meta_conn_collect_names(struct kdbus_meta_conn *mc,
struct kdbus_conn *conn)
{
- const struct kdbus_name_entry *e;
+ const struct kdbus_name_owner *owner;
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);
+ /* open-code length calculation to avoid final padding */
+ list_for_each_entry(owner, &conn->names_list, conn_entry)
+ if (!(owner->flags & KDBUS_NAME_IN_QUEUE))
+ size = KDBUS_ALIGN8(size) + KDBUS_ITEM_HEADER_SIZE +
+ sizeof(struct kdbus_name) +
+ strlen(owner->name->name) + 1;
if (!size)
return 0;
- item = kmalloc(size, GFP_KERNEL);
+ /* make sure we include zeroed padding for convenience helpers */
+ item = kmalloc(KDBUS_ALIGN8(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;
+ list_for_each_entry(owner, &conn->names_list, conn_entry) {
+ if (owner->flags & KDBUS_NAME_IN_QUEUE)
+ continue;
+
+ slen = strlen(owner->name->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->name.flags = owner->flags;
+ memcpy(item->name.name, owner->name->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);
+ WARN_ON((u8 *)item !=
+ (u8 *)mc->owned_names_items + KDBUS_ALIGN8(size));
mc->valid |= KDBUS_ATTACH_NAMES;
return 0;
@@ -744,11 +665,12 @@ static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc,
/**
* kdbus_meta_conn_collect() - Collect connection metadata
* @mc: Message metadata object
- * @kmsg: Kmsg to collect data from
* @conn: Connection to collect data from
+ * @msg_seqnum: Sequence number of the message to send
* @what: Attach flags to collect
*
- * This collects connection metadata from @kmsg and @conn and saves it in @mc.
+ * This collects connection metadata from @msg_seqnum 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.
@@ -756,9 +678,8 @@ static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc,
* 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)
+ u64 msg_seqnum, u64 what)
{
int ret;
@@ -769,9 +690,9 @@ int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
mutex_lock(&mc->lock);
- if (kmsg && (what & KDBUS_ATTACH_TIMESTAMP) &&
+ if (msg_seqnum && (what & KDBUS_ATTACH_TIMESTAMP) &&
!(mc->collected & KDBUS_ATTACH_TIMESTAMP)) {
- kdbus_meta_conn_collect_timestamp(mc, kmsg);
+ kdbus_meta_conn_collect_timestamp(mc, msg_seqnum);
mc->collected |= KDBUS_ATTACH_TIMESTAMP;
}
@@ -798,258 +719,392 @@ exit_unlock:
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)
+static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
+ const struct kdbus_meta_proc *mp,
+ struct user_namespace *user_ns)
{
- char *exe_pathname = NULL;
- void *exe_page = NULL;
- size_t size = 0;
- u64 valid = 0;
- int ret = 0;
+ struct user_namespace *iter;
+ const struct cred *cred = mp->cred;
+ bool parent = false, owner = false;
+ int i;
- if (mp) {
- mutex_lock(&mp->lock);
- valid |= mp->valid;
- mutex_unlock(&mp->lock);
+ /*
+ * This translates the effective capabilities of 'cred' into the given
+ * user-namespace. If the given user-namespace is a child-namespace of
+ * the user-namespace of 'cred', the mask can be copied verbatim. If
+ * not, the mask is cleared.
+ * There's one exception: If 'cred' is the owner of any user-namespace
+ * in the path between the given user-namespace and the user-namespace
+ * of 'cred', then it has all effective capabilities set. This means,
+ * the user who created a user-namespace always has all effective
+ * capabilities in any child namespaces. Note that this is based on the
+ * uid of the namespace creator, not the task hierarchy.
+ */
+ for (iter = user_ns; iter; iter = iter->parent) {
+ if (iter == cred->user_ns) {
+ parent = true;
+ break;
+ }
+
+ if (iter == &init_user_ns)
+ break;
+
+ if ((iter->parent == cred->user_ns) &&
+ uid_eq(iter->owner, cred->euid)) {
+ owner = true;
+ break;
+ }
}
- if (mc) {
- mutex_lock(&mc->lock);
- valid |= mc->valid;
- mutex_unlock(&mc->lock);
+ out->last_cap = CAP_LAST_CAP;
+
+ CAP_FOR_EACH_U32(i) {
+ if (parent) {
+ out->set[0].caps[i] = cred->cap_inheritable.cap[i];
+ out->set[1].caps[i] = cred->cap_permitted.cap[i];
+ out->set[2].caps[i] = cred->cap_effective.cap[i];
+ out->set[3].caps[i] = cred->cap_bset.cap[i];
+ } else if (owner) {
+ out->set[0].caps[i] = 0U;
+ out->set[1].caps[i] = ~0U;
+ out->set[2].caps[i] = ~0U;
+ out->set[3].caps[i] = ~0U;
+ } else {
+ out->set[0].caps[i] = 0U;
+ out->set[1].caps[i] = 0U;
+ out->set[2].caps[i] = 0U;
+ out->set[3].caps[i] = 0U;
+ }
}
- *mask &= valid;
- *mask &= kdbus_meta_attach_mask;
+ /* clear unused bits */
+ for (i = 0; i < 4; i++)
+ out->set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &=
+ CAP_LAST_U32_VALID_MASK;
+}
- if (!*mask)
- goto exit;
+/* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */
+static uid_t kdbus_from_kuid_keep(struct user_namespace *ns, kuid_t uid)
+{
+ return uid_valid(uid) ? from_kuid_munged(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(struct user_namespace *ns, kgid_t gid)
+{
+ return gid_valid(gid) ? from_kgid_munged(ns, gid) : ((gid_t)-1);
+}
+
+struct kdbus_meta_staging {
+ const struct kdbus_meta_proc *mp;
+ const struct kdbus_meta_fake *mf;
+ const struct kdbus_meta_conn *mc;
+ const struct kdbus_conn *conn;
+ u64 mask;
+
+ void *exe;
+ const char *exe_path;
+};
+
+static size_t kdbus_meta_measure(struct kdbus_meta_staging *staging)
+{
+ const struct kdbus_meta_proc *mp = staging->mp;
+ const struct kdbus_meta_fake *mf = staging->mf;
+ const struct kdbus_meta_conn *mc = staging->mc;
+ const u64 mask = staging->mask;
+ size_t size = 0;
/* process metadata */
- if (mp && (*mask & KDBUS_ATTACH_CREDS))
+ if (mf && (mask & KDBUS_ATTACH_CREDS))
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
+ else if (mp && (mask & KDBUS_ATTACH_CREDS))
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
- if (mp && (*mask & KDBUS_ATTACH_PIDS))
+ if (mf && (mask & KDBUS_ATTACH_PIDS))
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids));
+ else 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_AUXGROUPS))
+ size += KDBUS_ITEM_SIZE(mp->cred->group_info->ngroups *
+ sizeof(u64));
- if (mp && (*mask & KDBUS_ATTACH_TID_COMM))
+ if (mp && (mask & KDBUS_ATTACH_TID_COMM))
size += KDBUS_ITEM_SIZE(strlen(mp->tid_comm) + 1);
- if (mp && (*mask & KDBUS_ATTACH_PID_COMM))
+ 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;
- }
+ if (staging->exe_path && (mask & KDBUS_ATTACH_EXE))
+ size += KDBUS_ITEM_SIZE(strlen(staging->exe_path) + 1);
- size += KDBUS_ITEM_SIZE(strlen(exe_pathname) + 1);
- free_page((unsigned long)exe_page);
- }
-
- if (mp && (*mask & KDBUS_ATTACH_CMDLINE))
+ if (mp && (mask & KDBUS_ATTACH_CMDLINE))
size += KDBUS_ITEM_SIZE(strlen(mp->cmdline) + 1);
- if (mp && (*mask & KDBUS_ATTACH_CGROUP))
+ 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_CAPS))
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_meta_caps));
- if (mp && (*mask & KDBUS_ATTACH_SECLABEL))
+ if (mf && (mask & KDBUS_ATTACH_SECLABEL))
+ size += KDBUS_ITEM_SIZE(strlen(mf->seclabel) + 1);
+ else if (mp && (mask & KDBUS_ATTACH_SECLABEL))
size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1);
- if (mp && (*mask & KDBUS_ATTACH_AUDIT))
+ 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_NAMES))
+ size += KDBUS_ALIGN8(mc->owned_names_size);
- if (mc && (*mask & KDBUS_ATTACH_CONN_DESCRIPTION))
+ if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION))
size += KDBUS_ITEM_SIZE(strlen(mc->conn_description) + 1);
- if (mc && (*mask & KDBUS_ATTACH_TIMESTAMP))
+ if (mc && (mask & KDBUS_ATTACH_TIMESTAMP))
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_timestamp));
-exit:
- *sz = size;
-
- return ret;
+ return size;
}
-static int kdbus_meta_push_kvec(struct kvec *kvec,
- struct kdbus_item_header *hdr,
- u64 type, void *payload,
- size_t payload_size, u64 *size)
+static struct kdbus_item *kdbus_write_head(struct kdbus_item **iter,
+ u64 type, 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);
-}
+ struct kdbus_item *item = *iter;
+ size_t padding;
-/* 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);
+ item->type = type;
+ item->size = KDBUS_ITEM_HEADER_SIZE + size;
+
+ /* clear padding */
+ padding = KDBUS_ALIGN8(item->size) - item->size;
+ if (padding)
+ memset(item->data + size, 0, padding);
+
+ *iter = KDBUS_ITEM_NEXT(item);
+ return item;
}
-/* This is equivalent to from_kgid_munged(), but maps INVALID_GID to itself */
-static gid_t kdbus_from_kgid_keep(kgid_t gid)
+static struct kdbus_item *kdbus_write_full(struct kdbus_item **iter,
+ u64 type, u64 size, const void *data)
{
- return gid_valid(gid) ?
- from_kgid_munged(current_user_ns(), gid) : ((gid_t)-1);
+ struct kdbus_item *item;
+
+ item = kdbus_write_head(iter, type, size);
+ memcpy(item->data, data, size);
+ return item;
}
-/**
- * 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)
+static size_t kdbus_meta_write(struct kdbus_meta_staging *staging, void *mem,
+ size_t 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];
+ struct user_namespace *user_ns = staging->conn->cred->user_ns;
+ struct pid_namespace *pid_ns = ns_of_pid(staging->conn->pid);
+ struct kdbus_item *item = NULL, *items = mem;
+ u8 *end, *owned_names_end = NULL;
- /*
- * 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;
+ /* process metadata */
- if (mask == 0) {
- *real_size = 0;
- return 0;
+ if (staging->mf && (staging->mask & KDBUS_ATTACH_CREDS)) {
+ const struct kdbus_meta_fake *mf = staging->mf;
+
+ item = kdbus_write_head(&items, KDBUS_ITEM_CREDS,
+ sizeof(struct kdbus_creds));
+ item->creds = (struct kdbus_creds){
+ .uid = kdbus_from_kuid_keep(user_ns, mf->uid),
+ .euid = kdbus_from_kuid_keep(user_ns, mf->euid),
+ .suid = kdbus_from_kuid_keep(user_ns, mf->suid),
+ .fsuid = kdbus_from_kuid_keep(user_ns, mf->fsuid),
+ .gid = kdbus_from_kgid_keep(user_ns, mf->gid),
+ .egid = kdbus_from_kgid_keep(user_ns, mf->egid),
+ .sgid = kdbus_from_kgid_keep(user_ns, mf->sgid),
+ .fsgid = kdbus_from_kgid_keep(user_ns, mf->fsgid),
+ };
+ } else if (staging->mp && (staging->mask & KDBUS_ATTACH_CREDS)) {
+ const struct cred *c = staging->mp->cred;
+
+ item = kdbus_write_head(&items, KDBUS_ITEM_CREDS,
+ sizeof(struct kdbus_creds));
+ item->creds = (struct kdbus_creds){
+ .uid = kdbus_from_kuid_keep(user_ns, c->uid),
+ .euid = kdbus_from_kuid_keep(user_ns, c->euid),
+ .suid = kdbus_from_kuid_keep(user_ns, c->suid),
+ .fsuid = kdbus_from_kuid_keep(user_ns, c->fsuid),
+ .gid = kdbus_from_kgid_keep(user_ns, c->gid),
+ .egid = kdbus_from_kgid_keep(user_ns, c->egid),
+ .sgid = kdbus_from_kgid_keep(user_ns, c->sgid),
+ .fsgid = kdbus_from_kgid_keep(user_ns, c->fsgid),
+ };
}
- /* process metadata */
+ if (staging->mf && (staging->mask & KDBUS_ATTACH_PIDS)) {
+ item = kdbus_write_head(&items, KDBUS_ITEM_PIDS,
+ sizeof(struct kdbus_pids));
+ item->pids = (struct kdbus_pids){
+ .pid = pid_nr_ns(staging->mf->tgid, pid_ns),
+ .tid = pid_nr_ns(staging->mf->pid, pid_ns),
+ .ppid = pid_nr_ns(staging->mf->ppid, pid_ns),
+ };
+ } else if (staging->mp && (staging->mask & KDBUS_ATTACH_PIDS)) {
+ item = kdbus_write_head(&items, KDBUS_ITEM_PIDS,
+ sizeof(struct kdbus_pids));
+ item->pids = (struct kdbus_pids){
+ .pid = pid_nr_ns(staging->mp->tgid, pid_ns),
+ .tid = pid_nr_ns(staging->mp->pid, pid_ns),
+ .ppid = pid_nr_ns(staging->mp->ppid, pid_ns),
+ };
+ }
+
+ if (staging->mp && (staging->mask & KDBUS_ATTACH_AUXGROUPS)) {
+ const struct group_info *info = staging->mp->cred->group_info;
+ size_t i;
- 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);
+ item = kdbus_write_head(&items, KDBUS_ITEM_AUXGROUPS,
+ info->ngroups * sizeof(u64));
+ for (i = 0; i < info->ngroups; ++i)
+ item->data64[i] = from_kgid_munged(user_ns,
+ GROUP_AT(info, i));
}
- if (mp && (mask & KDBUS_ATTACH_PIDS)) {
- pids.pid = pid_vnr(mp->tgid);
- pids.tid = pid_vnr(mp->pid);
- pids.ppid = pid_vnr(mp->ppid);
+ if (staging->mp && (staging->mask & KDBUS_ATTACH_TID_COMM))
+ item = kdbus_write_full(&items, KDBUS_ITEM_TID_COMM,
+ strlen(staging->mp->tid_comm) + 1,
+ staging->mp->tid_comm);
+
+ if (staging->mp && (staging->mask & KDBUS_ATTACH_PID_COMM))
+ item = kdbus_write_full(&items, KDBUS_ITEM_PID_COMM,
+ strlen(staging->mp->pid_comm) + 1,
+ staging->mp->pid_comm);
+
+ if (staging->exe_path && (staging->mask & KDBUS_ATTACH_EXE))
+ item = kdbus_write_full(&items, KDBUS_ITEM_EXE,
+ strlen(staging->exe_path) + 1,
+ staging->exe_path);
+
+ if (staging->mp && (staging->mask & KDBUS_ATTACH_CMDLINE))
+ item = kdbus_write_full(&items, KDBUS_ITEM_CMDLINE,
+ strlen(staging->mp->cmdline) + 1,
+ staging->mp->cmdline);
+
+ if (staging->mp && (staging->mask & KDBUS_ATTACH_CGROUP))
+ item = kdbus_write_full(&items, KDBUS_ITEM_CGROUP,
+ strlen(staging->mp->cgroup) + 1,
+ staging->mp->cgroup);
+
+ if (staging->mp && (staging->mask & KDBUS_ATTACH_CAPS)) {
+ item = kdbus_write_head(&items, KDBUS_ITEM_CAPS,
+ sizeof(struct kdbus_meta_caps));
+ kdbus_meta_export_caps((void*)&item->caps, staging->mp,
+ user_ns);
+ }
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
- &pids, sizeof(pids), &size);
+ if (staging->mf && (staging->mask & KDBUS_ATTACH_SECLABEL))
+ item = kdbus_write_full(&items, KDBUS_ITEM_SECLABEL,
+ strlen(staging->mf->seclabel) + 1,
+ staging->mf->seclabel);
+ else if (staging->mp && (staging->mask & KDBUS_ATTACH_SECLABEL))
+ item = kdbus_write_full(&items, KDBUS_ITEM_SECLABEL,
+ strlen(staging->mp->seclabel) + 1,
+ staging->mp->seclabel);
+
+ if (staging->mp && (staging->mask & KDBUS_ATTACH_AUDIT)) {
+ item = kdbus_write_head(&items, KDBUS_ITEM_AUDIT,
+ sizeof(struct kdbus_audit));
+ item->audit = (struct kdbus_audit){
+ .loginuid = from_kuid(user_ns,
+ staging->mp->audit_loginuid),
+ .sessionid = staging->mp->audit_sessionid,
+ };
}
- if (mp && (mask & KDBUS_ATTACH_AUXGROUPS)) {
- size_t payload_size = mp->n_auxgrps * sizeof(u64);
- int i;
+ /* connection metadata */
- auxgrps = kmalloc(payload_size, GFP_KERNEL);
- if (!auxgrps) {
- ret = -ENOMEM;
- goto exit;
- }
+ if (staging->mc && (staging->mask & KDBUS_ATTACH_NAMES)) {
+ memcpy(items, staging->mc->owned_names_items,
+ KDBUS_ALIGN8(staging->mc->owned_names_size));
+ owned_names_end = (u8 *)items + staging->mc->owned_names_size;
+ items = (void *)KDBUS_ALIGN8((unsigned long)owned_names_end);
+ }
+
+ if (staging->mc && (staging->mask & KDBUS_ATTACH_CONN_DESCRIPTION))
+ item = kdbus_write_full(&items, KDBUS_ITEM_CONN_DESCRIPTION,
+ strlen(staging->mc->conn_description) + 1,
+ staging->mc->conn_description);
- for (i = 0; i < mp->n_auxgrps; i++)
- auxgrps[i] = from_kgid_munged(user_ns, mp->auxgrps[i]);
+ if (staging->mc && (staging->mask & KDBUS_ATTACH_TIMESTAMP))
+ item = kdbus_write_full(&items, KDBUS_ITEM_TIMESTAMP,
+ sizeof(staging->mc->ts),
+ &staging->mc->ts);
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
- KDBUS_ITEM_AUXGROUPS,
- auxgrps, payload_size, &size);
+ /*
+ * Return real size (minus trailing padding). In case of 'owned_names'
+ * we cannot deduce it from item->size, so treat it special.
+ */
+
+ if (items == (void *)KDBUS_ALIGN8((unsigned long)owned_names_end))
+ end = owned_names_end;
+ else if (item)
+ end = (u8 *)item + item->size;
+ else
+ end = mem;
+
+ WARN_ON((u8 *)items - (u8 *)mem != size);
+ WARN_ON((void *)KDBUS_ALIGN8((unsigned long)end) != (void *)items);
+
+ return end - (u8 *)mem;
+}
+
+int kdbus_meta_emit(struct kdbus_meta_proc *mp,
+ struct kdbus_meta_fake *mf,
+ struct kdbus_meta_conn *mc,
+ struct kdbus_conn *conn,
+ u64 mask,
+ struct kdbus_item **out_items,
+ size_t *out_size)
+{
+ struct kdbus_meta_staging staging = {};
+ struct kdbus_item *items = NULL;
+ size_t size = 0;
+ int ret;
+
+ if (WARN_ON(mf && mp))
+ mp = NULL;
+
+ staging.mp = mp;
+ staging.mf = mf;
+ staging.mc = mc;
+ staging.conn = conn;
+
+ /* get mask of valid items */
+ if (mf)
+ staging.mask |= mf->valid;
+ if (mp) {
+ mutex_lock(&mp->lock);
+ staging.mask |= mp->valid;
+ mutex_unlock(&mp->lock);
+ }
+ if (mc) {
+ mutex_lock(&mc->lock);
+ staging.mask |= mc->valid;
+ mutex_unlock(&mc->lock);
}
- 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);
+ staging.mask &= mask;
- 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 (!staging.mask) { /* bail out if nothing to do */
+ ret = 0;
+ goto exit;
+ }
- if (mp && (mask & KDBUS_ATTACH_EXE)) {
+ /* EXE is special as it needs a temporary page to assemble */
+ if (mp && (staging.mask & KDBUS_ATTACH_EXE)) {
struct path p;
/*
- * TODO: We need access to __d_path() so we can write the path
+ * XXX: 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
@@ -1057,103 +1112,236 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
*/
get_fs_root(current->fs, &p);
- if (path_equal(&p, &mp->root_path)) {
- exe_page = (void *)__get_free_page(GFP_TEMPORARY);
- if (!exe_page) {
+ if (path_equal(&p, &conn->root_path)) {
+ staging.exe = (void *)__get_free_page(GFP_TEMPORARY);
+ if (!staging.exe) {
path_put(&p);
ret = -ENOMEM;
goto exit;
}
- exe_pathname = d_path(&mp->exe_path, exe_page,
- PAGE_SIZE);
- if (IS_ERR(exe_pathname)) {
+ staging.exe_path = d_path(&mp->exe_path, staging.exe,
+ PAGE_SIZE);
+ if (IS_ERR(staging.exe_path)) {
path_put(&p);
- ret = PTR_ERR(exe_pathname);
+ ret = PTR_ERR(staging.exe_path);
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);
+ size = kdbus_meta_measure(&staging);
+ if (!size) { /* bail out if nothing to do */
+ ret = 0;
+ goto exit;
+ }
- if (mp && (mask & KDBUS_ATTACH_CGROUP))
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
- KDBUS_ITEM_CGROUP, mp->cgroup,
- strlen(mp->cgroup) + 1, &size);
+ items = kmalloc(size, GFP_KERNEL);
+ if (!items) {
+ ret = -ENOMEM;
+ goto exit;
+ }
- 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,
- };
+ size = kdbus_meta_write(&staging, items, size);
+ if (!size) {
+ kfree(items);
+ items = NULL;
+ }
+
+ ret = 0;
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_AUDIT,
- &a, sizeof(a), &size);
+exit:
+ if (staging.exe)
+ free_page((unsigned long)staging.exe);
+ if (ret >= 0) {
+ *out_items = items;
+ *out_size = size;
}
+ return ret;
+}
- /* connection metadata */
+enum {
+ KDBUS_META_PROC_NONE,
+ KDBUS_META_PROC_NORMAL,
+};
- if (mc && (mask & KDBUS_ATTACH_NAMES))
- kdbus_kvec_set(&kvec[cnt++], mc->owned_names_items,
- mc->owned_names_size, &size);
+/**
+ * kdbus_proc_permission() - check /proc permissions on target pid
+ * @pid_ns: namespace we operate in
+ * @cred: credentials of requestor
+ * @target: target process
+ *
+ * This checks whether a process with credentials @cred can access information
+ * of @target in the namespace @pid_ns. This tries to follow /proc permissions,
+ * but is slightly more restrictive.
+ *
+ * Return: The /proc access level (KDBUS_META_PROC_*) is returned.
+ */
+static unsigned int kdbus_proc_permission(const struct pid_namespace *pid_ns,
+ const struct cred *cred,
+ struct pid *target)
+{
+ if (pid_ns->hide_pid < 1)
+ return KDBUS_META_PROC_NORMAL;
- 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);
+ /* XXX: we need groups_search() exported for aux-groups */
+ if (gid_eq(cred->egid, pid_ns->pid_gid))
+ return KDBUS_META_PROC_NORMAL;
- if (mc && (mask & KDBUS_ATTACH_TIMESTAMP))
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
- KDBUS_ITEM_TIMESTAMP, &mc->ts,
- sizeof(mc->ts), &size);
+ /*
+ * XXX: If ptrace_may_access(PTRACE_MODE_READ) is granted, you can
+ * overwrite hide_pid. However, ptrace_may_access() only supports
+ * checking 'current', hence, we cannot use this here. But we
+ * simply decide to not support this override, so no need to worry.
+ */
- ret = kdbus_pool_slice_copy_kvec(slice, offset, kvec, cnt, size);
- *real_size = size;
+ return KDBUS_META_PROC_NONE;
+}
-exit:
- kfree(auxgrps);
+/**
+ * kdbus_meta_proc_mask() - calculate which metadata would be visible to
+ * a connection via /proc
+ * @prv_pid: pid of metadata provider
+ * @req_pid: pid of metadata requestor
+ * @req_cred: credentials of metadata reqeuestor
+ * @wanted: metadata that is requested
+ *
+ * This checks which metadata items of @prv_pid can be read via /proc by the
+ * requestor @req_pid.
+ *
+ * Return: Set of metadata flags the requestor can see (limited by @wanted).
+ */
+static u64 kdbus_meta_proc_mask(struct pid *prv_pid,
+ struct pid *req_pid,
+ const struct cred *req_cred,
+ u64 wanted)
+{
+ struct pid_namespace *prv_ns, *req_ns;
+ unsigned int proc;
- if (exe_page)
- free_page((unsigned long)exe_page);
+ prv_ns = ns_of_pid(prv_pid);
+ req_ns = ns_of_pid(req_pid);
- return ret;
+ /*
+ * If the sender is not visible in the receiver namespace, then the
+ * receiver cannot access the sender via its own procfs. Hence, we do
+ * not attach any additional metadata.
+ */
+ if (!pid_nr_ns(prv_pid, req_ns))
+ return 0;
+
+ /*
+ * If the pid-namespace of the receiver has hide_pid set, it cannot see
+ * any process but its own. We shortcut this /proc permission check if
+ * provider and requestor are the same. If not, we perform rather
+ * expensive /proc permission checks.
+ */
+ if (prv_pid == req_pid)
+ proc = KDBUS_META_PROC_NORMAL;
+ else
+ proc = kdbus_proc_permission(req_ns, req_cred, prv_pid);
+
+ /* you need /proc access to read standard process attributes */
+ if (proc < KDBUS_META_PROC_NORMAL)
+ wanted &= ~(KDBUS_ATTACH_TID_COMM |
+ KDBUS_ATTACH_PID_COMM |
+ KDBUS_ATTACH_SECLABEL |
+ KDBUS_ATTACH_CMDLINE |
+ KDBUS_ATTACH_CGROUP |
+ KDBUS_ATTACH_AUDIT |
+ KDBUS_ATTACH_CAPS |
+ KDBUS_ATTACH_EXE);
+
+ /* clear all non-/proc flags */
+ return wanted & (KDBUS_ATTACH_TID_COMM |
+ KDBUS_ATTACH_PID_COMM |
+ KDBUS_ATTACH_SECLABEL |
+ KDBUS_ATTACH_CMDLINE |
+ KDBUS_ATTACH_CGROUP |
+ KDBUS_ATTACH_AUDIT |
+ KDBUS_ATTACH_CAPS |
+ KDBUS_ATTACH_EXE);
}
/**
- * kdbus_meta_calc_attach_flags() - calculate attach flags for a sender
- * and a receiver
- * @sender: Sending connection
- * @receiver: Receiving connection
+ * kdbus_meta_get_mask() - calculate attach flags mask for metadata request
+ * @prv_pid: pid of metadata provider
+ * @prv_mask: mask of metadata the provide grants unchecked
+ * @req_pid: pid of metadata requestor
+ * @req_cred: credentials of metadata requestor
+ * @req_mask: mask of metadata that is requested
*
- * Return: the attach flags both the sender and the receiver have opted-in
- * for.
+ * This calculates the metadata items that the requestor @req_pid can access
+ * from the metadata provider @prv_pid. This permission check consists of
+ * several different parts:
+ * - Providers can grant metadata items unchecked. Regardless of their type,
+ * they're always granted to the requestor. This mask is passed as @prv_mask.
+ * - Basic items (credentials and connection metadata) are granted implicitly
+ * to everyone. They're publicly available to any bus-user that can see the
+ * provider.
+ * - Process credentials that are not granted implicitly follow the same
+ * permission checks as /proc. This means, we always assume a requestor
+ * process has access to their *own* /proc mount, if they have access to
+ * kdbusfs.
+ *
+ * Return: Mask of metadata that is granted.
+ */
+static u64 kdbus_meta_get_mask(struct pid *prv_pid, u64 prv_mask,
+ struct pid *req_pid,
+ const struct cred *req_cred, u64 req_mask)
+{
+ u64 missing, impl_mask, proc_mask = 0;
+
+ /*
+ * Connection metadata and basic unix process credentials are
+ * transmitted implicitly, and cannot be suppressed. Both are required
+ * to perform user-space policies on the receiver-side. Furthermore,
+ * connection metadata is public state, anyway, and unix credentials
+ * are needed for UDS-compatibility. We extend them slightly by
+ * auxiliary groups and additional uids/gids/pids.
+ */
+ impl_mask = /* connection metadata */
+ KDBUS_ATTACH_CONN_DESCRIPTION |
+ KDBUS_ATTACH_TIMESTAMP |
+ KDBUS_ATTACH_NAMES |
+ /* credentials and pids */
+ KDBUS_ATTACH_AUXGROUPS |
+ KDBUS_ATTACH_CREDS |
+ KDBUS_ATTACH_PIDS;
+
+ /*
+ * Calculate the set of metadata that is not granted implicitly nor by
+ * the sender, but still requested by the receiver. If any are left,
+ * perform rather expensive /proc access checks for them.
+ */
+ missing = req_mask & ~((prv_mask | impl_mask) & req_mask);
+ if (missing)
+ proc_mask = kdbus_meta_proc_mask(prv_pid, req_pid, req_cred,
+ missing);
+
+ return (prv_mask | impl_mask | proc_mask) & req_mask;
+}
+
+/**
+ */
+u64 kdbus_meta_info_mask(const struct kdbus_conn *conn, u64 mask)
+{
+ return kdbus_meta_get_mask(conn->pid,
+ atomic64_read(&conn->attach_flags_send),
+ task_pid(current),
+ current_cred(),
+ mask);
+}
+
+/**
*/
-u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender,
- const struct kdbus_conn *receiver)
+u64 kdbus_meta_msg_mask(const struct kdbus_conn *snd,
+ const struct kdbus_conn *rcv)
{
- return atomic64_read(&sender->attach_flags_send) &
- atomic64_read(&receiver->attach_flags_recv);
+ return kdbus_meta_get_mask(task_pid(current),
+ atomic64_read(&snd->attach_flags_send),
+ rcv->pid,
+ rcv->cred,
+ atomic64_read(&rcv->attach_flags_recv));
}