diff options
author | Karol Lewandowski <k.lewandowsk@samsung.com> | 2016-10-06 14:20:24 +0200 |
---|---|---|
committer | Seung-Woo Kim <sw0312.kim@samsung.com> | 2016-12-14 13:53:39 +0900 |
commit | d8934e9b05b82e73a3c78421f372197801c1785e (patch) | |
tree | 24981389ae0ca646a7997f6e32ea7a39aea147b7 /ipc/kdbus/metadata.c | |
parent | ea4654706ad61bc7afc47e6321c5cbee8c175688 (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.c | 1156 |
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)); } |