diff options
-rw-r--r-- | ipc/kdbus/bus.c | 6 | ||||
-rw-r--r-- | ipc/kdbus/connection.c | 37 | ||||
-rw-r--r-- | ipc/kdbus/connection.h | 9 | ||||
-rw-r--r-- | ipc/kdbus/message.c | 2 | ||||
-rw-r--r-- | ipc/kdbus/metadata.c | 216 | ||||
-rw-r--r-- | ipc/kdbus/metadata.h | 43 | ||||
-rw-r--r-- | ipc/kdbus/queue.c | 2 |
7 files changed, 206 insertions, 109 deletions
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c index 8fffc2f594a8..dd1e600b8f01 100644 --- a/ipc/kdbus/bus.c +++ b/ipc/kdbus/bus.c @@ -484,7 +484,7 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp) attach_flags &= bus->attach_flags_owner; - ret = kdbus_meta_export_prepare(bus->creator_meta, NULL, + ret = kdbus_meta_export_prepare(bus->creator_meta, NULL, NULL, &attach_flags, &meta_size); if (ret < 0) goto exit; @@ -507,8 +507,8 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp) goto exit; } - ret = kdbus_meta_export(bus->creator_meta, NULL, conn, attach_flags, - slice, hdr_size, &meta_size); + ret = kdbus_meta_export(bus->creator_meta, NULL, NULL, conn, + attach_flags, slice, hdr_size, &meta_size); if (ret < 0) goto exit; diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c index ca241fcf19f7..8976bb32ab69 100644 --- a/ipc/kdbus/connection.c +++ b/ipc/kdbus/connection.c @@ -172,22 +172,28 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, BUILD_BUG_ON(sizeof(bus->id128) != sizeof(hello->id128)); memcpy(hello->id128, bus->id128, sizeof(hello->id128)); - conn->meta = kdbus_meta_proc_new(); - if (IS_ERR(conn->meta)) { - ret = PTR_ERR(conn->meta); - conn->meta = NULL; - goto exit_unref; - } - /* privileged processes can impersonate somebody else */ if (creds || pids || seclabel) { - ret = kdbus_meta_proc_fake(conn->meta, creds, pids, seclabel); - if (ret < 0) + conn->meta_fake = kdbus_meta_fake_new(); + if (IS_ERR(conn->meta_fake)) { + ret = PTR_ERR(conn->meta_fake); + conn->meta_fake = NULL; goto exit_unref; + } - conn->faked_meta = true; + ret = kdbus_meta_fake_collect(conn->meta_fake, + creds, pids, seclabel); + if (ret < 0) + goto exit_unref; } else { - ret = kdbus_meta_proc_collect(conn->meta, + conn->meta_proc = kdbus_meta_proc_new(); + if (IS_ERR(conn->meta_proc)) { + ret = PTR_ERR(conn->meta_proc); + conn->meta_proc = NULL; + goto exit_unref; + } + + ret = kdbus_meta_proc_collect(conn->meta_proc, KDBUS_ATTACH_CREDS | KDBUS_ATTACH_PIDS | KDBUS_ATTACH_AUXGROUPS | @@ -270,7 +276,8 @@ static void __kdbus_conn_free(struct kref *kref) kdbus_user_unref(conn->user); } - kdbus_meta_proc_unref(conn->meta); + kdbus_meta_fake_free(conn->meta_fake); + kdbus_meta_proc_unref(conn->meta_proc); kdbus_match_db_free(conn->match_db); kdbus_pool_free(conn->pool); kdbus_ep_unref(conn->ep); @@ -1785,7 +1792,8 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp) if (ret < 0) goto exit; - ret = kdbus_meta_export_prepare(owner_conn->meta, conn_meta, + ret = kdbus_meta_export_prepare(owner_conn->meta_proc, + owner_conn->meta_fake, conn_meta, &attach_flags, &meta_size); if (ret < 0) goto exit; @@ -1798,7 +1806,8 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp) goto exit; } - ret = kdbus_meta_export(owner_conn->meta, conn_meta, conn, attach_flags, + ret = kdbus_meta_export(owner_conn->meta_proc, owner_conn->meta_fake, + conn_meta, conn, attach_flags, slice, sizeof(info), &meta_size); if (ret < 0) goto exit; diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h index 226f3ff60e31..0eb3d2e70a5e 100644 --- a/ipc/kdbus/connection.h +++ b/ipc/kdbus/connection.h @@ -54,8 +54,8 @@ struct kdbus_kmsg; * @work: Delayed work to handle timeouts * activator for * @match_db: Subscription filter to broadcast messages - * @meta: Active connection creator's metadata/credentials, - * either from the handle or from HELLO + * @meta_proc: Process metadata of connection creator, or NULL + * @meta_fake: Faked metadata, or NULL * @pool: The user's buffer to receive messages * @user: Owner of the connection * @cred: The credentials of the connection at creation time @@ -75,7 +75,6 @@ struct kdbus_kmsg; * @names_list: List of well-known names * @names_queue_list: Well-known names this connection waits for * @privileged: Whether this connection is privileged on the bus - * @faked_meta: Whether the metadata was faked on HELLO */ struct kdbus_conn { struct kref kref; @@ -96,7 +95,8 @@ struct kdbus_conn { struct list_head reply_list; struct delayed_work work; struct kdbus_match_db *match_db; - struct kdbus_meta_proc *meta; + struct kdbus_meta_proc *meta_proc; + struct kdbus_meta_fake *meta_fake; struct kdbus_pool *pool; struct kdbus_user *user; const struct cred *cred; @@ -118,7 +118,6 @@ struct kdbus_conn { struct list_head names_queue_list; bool privileged:1; - bool faked_meta:1; }; struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn); diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c index e9da67229eb1..55e432397b93 100644 --- a/ipc/kdbus/message.c +++ b/ipc/kdbus/message.c @@ -626,7 +626,7 @@ int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src, int ret; attach = kdbus_meta_calc_attach_flags(src, dst); - if (!src->faked_meta) { + if (!src->meta_fake) { ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach); if (ret < 0) return ret; diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c index 79f0e8c7f4a3..ac4135c507c4 100644 --- a/ipc/kdbus/metadata.c +++ b/ipc/kdbus/metadata.c @@ -494,101 +494,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 (!mp) - return 0; - - mutex_lock(&mp->lock); + if (mf->valid) + return -EALREADY; - 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; } /** @@ -768,6 +783,7 @@ exit_unlock: /* * kdbus_meta_export_prepare() - Prepare metadata for export * @mp: Process metadata, or NULL + * @mf: Fake 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 @@ -783,6 +799,7 @@ exit_unlock: * Return: 0 on success, negative error number otherwise. */ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, + struct kdbus_meta_fake *mf, struct kdbus_meta_conn *mc, u64 *mask, size_t *sz) { @@ -792,6 +809,12 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, u64 valid = 0; int ret = 0; + if (WARN_ON(mf && mp)) + mp = NULL; + + if (mf) + valid |= mf->valid; + if (mp) { mutex_lock(&mp->lock); valid |= mp->valid; @@ -811,10 +834,10 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, /* process metadata */ - if (mp && (*mask & KDBUS_ATTACH_CREDS)) + if ((mp || mf) && (*mask & KDBUS_ATTACH_CREDS)) size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds)); - if (mp && (*mask & KDBUS_ATTACH_PIDS)) + if ((mp || mf) && (*mask & KDBUS_ATTACH_PIDS)) size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids)); if (mp && (*mask & KDBUS_ATTACH_AUXGROUPS)) @@ -852,8 +875,9 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, if (mp && (*mask & KDBUS_ATTACH_CAPS)) size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_meta_caps)); - if (mp && (*mask & KDBUS_ATTACH_SECLABEL)) - size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1); + if ((mp || mf) && (*mask & KDBUS_ATTACH_SECLABEL)) + size += KDBUS_ITEM_SIZE(strlen(mp ? mp->seclabel + : mf->seclabel) + 1); if (mp && (*mask & KDBUS_ATTACH_AUDIT)) size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_audit)); @@ -966,6 +990,7 @@ static gid_t kdbus_from_kgid_keep(struct user_namespace *ns, kgid_t gid) /** * kdbus_meta_export() - export information from metadata into a slice * @mp: Process metadata, or NULL + * @mf: Fake metadata, or NULL * @mc: Connection metadata, or NULL * @conn: Target connection to translate metadata into * @mask: Mask of KDBUS_ATTACH_* flags to export @@ -988,6 +1013,7 @@ static gid_t kdbus_from_kgid_keep(struct user_namespace *ns, kgid_t gid) * Return: 0 on success, negative error number otherwise. */ int kdbus_meta_export(struct kdbus_meta_proc *mp, + struct kdbus_meta_fake *mf, struct kdbus_meta_conn *mc, struct kdbus_conn *conn, u64 mask, @@ -1007,6 +1033,9 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp, u64 size = 0; int ret = 0; + if (WARN_ON(mf && mp)) + mp = NULL; + hdr = &item_hdr[0]; if (mask == 0) { @@ -1016,7 +1045,19 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp, /* process metadata */ - if (mp && (mask & KDBUS_ATTACH_CREDS)) { + if (mf && (mask & KDBUS_ATTACH_CREDS)) { + creds.uid = kdbus_from_kuid_keep(user_ns, mf->uid); + creds.euid = kdbus_from_kuid_keep(user_ns, mf->euid); + creds.suid = kdbus_from_kuid_keep(user_ns, mf->suid); + creds.fsuid = kdbus_from_kuid_keep(user_ns, mf->fsuid); + creds.gid = kdbus_from_kgid_keep(user_ns, mf->gid); + creds.egid = kdbus_from_kgid_keep(user_ns, mf->egid); + creds.sgid = kdbus_from_kgid_keep(user_ns, mf->sgid); + creds.fsgid = kdbus_from_kgid_keep(user_ns, mf->fsgid); + + cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS, + &creds, sizeof(creds), &size); + } else if (mp && (mask & KDBUS_ATTACH_CREDS)) { creds.uid = kdbus_from_kuid_keep(user_ns, mp->uid); creds.euid = kdbus_from_kuid_keep(user_ns, mp->euid); creds.suid = kdbus_from_kuid_keep(user_ns, mp->suid); @@ -1030,7 +1071,14 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp, &creds, sizeof(creds), &size); } - if (mp && (mask & KDBUS_ATTACH_PIDS)) { + if (mf && (mask & KDBUS_ATTACH_PIDS)) { + pids.pid = pid_nr_ns(mf->tgid, conn->pid_ns); + pids.tid = pid_nr_ns(mf->pid, conn->pid_ns); + pids.ppid = pid_nr_ns(mf->ppid, conn->pid_ns); + + cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS, + &pids, sizeof(pids), &size); + } else if (mp && (mask & KDBUS_ATTACH_PIDS)) { pids.pid = pid_nr_ns(mp->tgid, conn->pid_ns); pids.tid = pid_nr_ns(mp->pid, conn->pid_ns); pids.ppid = pid_nr_ns(mp->ppid, conn->pid_ns); @@ -1124,7 +1172,11 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp, sizeof(caps), &size); } - if (mp && (mask & KDBUS_ATTACH_SECLABEL)) + if (mf && (mask & KDBUS_ATTACH_SECLABEL)) + cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, + KDBUS_ITEM_SECLABEL, mf->seclabel, + strlen(mf->seclabel) + 1, &size); + else if (mp && (mask & KDBUS_ATTACH_SECLABEL)) cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_SECLABEL, mp->seclabel, strlen(mp->seclabel) + 1, &size); diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h index 2dbbb3d78d97..2fb04dad38d4 100644 --- a/ipc/kdbus/metadata.h +++ b/ipc/kdbus/metadata.h @@ -24,14 +24,47 @@ struct kdbus_pool_slice; struct kdbus_meta_proc; struct kdbus_meta_conn; +/** + * struct kdbus_meta_fake - Fake metadata + * @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 + * @seclabel: Seclabel + */ +struct kdbus_meta_fake { + u64 valid; + + /* KDBUS_ITEM_CREDS */ + kuid_t uid, euid, suid, fsuid; + kgid_t gid, egid, sgid, fsgid; + + /* KDBUS_ITEM_PIDS */ + struct pid *pid, *tgid, *ppid; + + /* KDBUS_ITEM_SECLABEL */ + char *seclabel; +}; + 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_fake *kdbus_meta_fake_new(void); +struct kdbus_meta_fake *kdbus_meta_fake_free(struct kdbus_meta_fake *mf); +int kdbus_meta_fake_collect(struct kdbus_meta_fake *mf, + 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); @@ -42,9 +75,11 @@ int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc, u64 what); int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp, + struct kdbus_meta_fake *mf, struct kdbus_meta_conn *mc, u64 *mask, size_t *sz); int kdbus_meta_export(struct kdbus_meta_proc *mp, + struct kdbus_meta_fake *mf, struct kdbus_meta_conn *mc, struct kdbus_conn *conn, u64 mask, diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c index 6650b7804d87..1e7916155036 100644 --- a/ipc/kdbus/queue.c +++ b/ipc/kdbus/queue.c @@ -251,6 +251,7 @@ struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_dst, atomic64_read(&conn_dst->attach_flags_recv); ret = kdbus_meta_export_prepare(entry->proc_meta, + NULL, entry->conn_meta, &entry->attach_flags, &meta_size); @@ -478,6 +479,7 @@ int kdbus_queue_entry_install(struct kdbus_queue_entry *entry, size_t meta_size; ret = kdbus_meta_export(entry->proc_meta, + NULL, entry->conn_meta, conn_dst, entry->attach_flags, |