diff options
Diffstat (limited to 'ipc/kdbus')
-rw-r--r-- | ipc/kdbus/bus.h | 2 | ||||
-rw-r--r-- | ipc/kdbus/connection.c | 39 | ||||
-rw-r--r-- | ipc/kdbus/connection.h | 6 | ||||
-rw-r--r-- | ipc/kdbus/endpoint.c | 28 | ||||
-rw-r--r-- | ipc/kdbus/endpoint.h | 3 | ||||
-rw-r--r-- | ipc/kdbus/handle.c | 30 | ||||
-rw-r--r-- | ipc/kdbus/handle.h | 6 | ||||
-rw-r--r-- | ipc/kdbus/message.c | 2 |
8 files changed, 75 insertions, 41 deletions
diff --git a/ipc/kdbus/bus.h b/ipc/kdbus/bus.h index 238986eff..8c2acaed6 100644 --- a/ipc/kdbus/bus.h +++ b/ipc/kdbus/bus.h @@ -44,6 +44,7 @@ struct kdbus_user; * @domain: Domain of this bus * @creator: Creator of the bus * @creator_meta: Meta information about the bus creator + * @last_message_id: Last used message id * @policy_db: Policy database for this bus * @name_registry: Name registry of this bus * @conn_rwlock: Read/Write lock for all lists of child connections @@ -67,6 +68,7 @@ struct kdbus_bus { struct kdbus_meta_proc *creator_meta; /* protected by own locks */ + atomic64_t last_message_id; struct kdbus_policy_db policy_db; struct kdbus_name_registry *name_registry; diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c index d94b417e0..aa3296ea4 100644 --- a/ipc/kdbus/connection.c +++ b/ipc/kdbus/connection.c @@ -52,7 +52,8 @@ #define KDBUS_CONN_ACTIVE_BIAS (INT_MIN + 2) #define KDBUS_CONN_ACTIVE_NEW (INT_MIN + 1) -static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, +static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, + struct file *file, struct kdbus_cmd_hello *hello, const char *name, const struct kdbus_creds *creds, @@ -72,6 +73,8 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, bool is_policy_holder; bool is_activator; bool is_monitor; + bool privileged; + bool owner; struct kvec kvec; int ret; @@ -81,6 +84,9 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, struct kdbus_bloom_parameter bloom; } bloom_item; + privileged = kdbus_ep_is_privileged(ep, file); + owner = kdbus_ep_is_owner(ep, file); + is_monitor = hello->flags & KDBUS_HELLO_MONITOR; is_activator = hello->flags & KDBUS_HELLO_ACTIVATOR; is_policy_holder = hello->flags & KDBUS_HELLO_POLICY_HOLDER; @@ -97,9 +103,9 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, return ERR_PTR(-EINVAL); if (is_monitor && ep->user) return ERR_PTR(-EOPNOTSUPP); - if (!privileged && (is_activator || is_policy_holder || is_monitor)) + if (!owner && (is_activator || is_policy_holder || is_monitor)) return ERR_PTR(-EPERM); - if ((creds || pids || seclabel) && !privileged) + if (!owner && (creds || pids || seclabel)) return ERR_PTR(-EPERM); ret = kdbus_sanitize_attach_flags(hello->attach_flags_send, @@ -129,12 +135,13 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, atomic_set(&conn->request_count, 0); atomic_set(&conn->lost_count, 0); INIT_DELAYED_WORK(&conn->work, kdbus_reply_list_scan_work); - conn->cred = get_current_cred(); + conn->cred = get_cred(file->f_cred); conn->pid = get_pid(task_pid(current)); get_fs_root(current->fs, &conn->root_path); init_waitqueue_head(&conn->wait); kdbus_queue_init(&conn->queue); conn->privileged = privileged; + conn->owner = owner; conn->ep = kdbus_ep_ref(ep); conn->id = atomic64_inc_return(&bus->domain->last_id); conn->flags = hello->flags; @@ -214,11 +221,21 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged, * Note that limits are always accounted against the real UID, not * the effective UID (cred->user always points to the accounting of * cred->uid, not cred->euid). + * In case the caller is privileged, we allow changing the accounting + * to the faked user. */ if (ep->user) { conn->user = kdbus_user_ref(ep->user); } else { - conn->user = kdbus_user_lookup(ep->bus->domain, current_uid()); + kuid_t uid; + + if (conn->meta_fake && uid_valid(conn->meta_fake->uid) && + conn->privileged) + uid = conn->meta_fake->uid; + else + uid = conn->cred->uid; + + conn->user = kdbus_user_lookup(ep->bus->domain, uid); if (IS_ERR(conn->user)) { ret = PTR_ERR(conn->user); conn->user = NULL; @@ -1123,7 +1140,7 @@ static int kdbus_conn_reply(struct kdbus_conn *src, mutex_unlock(&dst->lock); if (!reply) { - ret = -EPERM; + ret = -EBADSLT; goto exit; } @@ -1418,7 +1435,7 @@ bool kdbus_conn_policy_own_name(struct kdbus_conn *conn, return false; } - if (conn->privileged) + if (conn->owner) return true; res = kdbus_policy_query(&conn->ep->bus->policy_db, conn_creds, @@ -1448,7 +1465,7 @@ bool kdbus_conn_policy_talk(struct kdbus_conn *conn, to, KDBUS_POLICY_TALK)) return false; - if (conn->privileged) + if (conn->owner) return true; if (uid_eq(conn_creds->euid, to->cred->uid)) return true; @@ -1567,12 +1584,12 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn, /** * kdbus_cmd_hello() - handle KDBUS_CMD_HELLO * @ep: Endpoint to operate on - * @privileged: Whether the caller is privileged + * @file: File this connection is opened on * @argp: Command payload * * Return: NULL or newly created connection on success, ERR_PTR on failure. */ -struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged, +struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, struct file *file, void __user *argp) { struct kdbus_cmd_hello *cmd; @@ -1607,7 +1624,7 @@ struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged, item_name = argv[1].item ? argv[1].item->str : NULL; - c = kdbus_conn_new(ep, privileged, cmd, item_name, + c = kdbus_conn_new(ep, file, cmd, item_name, argv[2].item ? &argv[2].item->creds : NULL, argv[3].item ? &argv[3].item->pids : NULL, argv[4].item ? argv[4].item->str : NULL, diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h index 5ee864eb0..8e0180ace 100644 --- a/ipc/kdbus/connection.h +++ b/ipc/kdbus/connection.h @@ -73,7 +73,8 @@ struct kdbus_staging; * @activator_of: Well-known name entry this connection acts as an * @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 + * @privileged: Whether this connection is privileged on the domain + * @owner: Owned by the same user as the bus owner */ struct kdbus_conn { struct kref kref; @@ -116,6 +117,7 @@ struct kdbus_conn { struct list_head names_queue_list; bool privileged:1; + bool owner:1; }; struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn); @@ -154,7 +156,7 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn, const struct kdbus_msg *msg); /* command dispatcher */ -struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged, +struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, struct file *file, void __user *argp); int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp); int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp); diff --git a/ipc/kdbus/endpoint.c b/ipc/kdbus/endpoint.c index 977964dbb..44e7a20de 100644 --- a/ipc/kdbus/endpoint.c +++ b/ipc/kdbus/endpoint.c @@ -184,6 +184,34 @@ struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep) } /** + * kdbus_ep_is_privileged() - check whether a file is privileged + * @ep: endpoint to operate on + * @file: file to test + * + * Return: True if @file is privileged in the domain of @ep. + */ +bool kdbus_ep_is_privileged(struct kdbus_ep *ep, struct file *file) +{ + return !ep->user && + file_ns_capable(file, ep->bus->domain->user_namespace, + CAP_IPC_OWNER); +} + +/** + * kdbus_ep_is_owner() - check whether a file should be treated as bus owner + * @ep: endpoint to operate on + * @file: file to test + * + * Return: True if @file should be treated as bus owner on @ep + */ +bool kdbus_ep_is_owner(struct kdbus_ep *ep, struct file *file) +{ + return !ep->user && + (uid_eq(file->f_cred->euid, ep->bus->node.uid) || + kdbus_ep_is_privileged(ep, file)); +} + +/** * kdbus_cmd_ep_make() - handle KDBUS_CMD_ENDPOINT_MAKE * @bus: bus to operate on * @argp: command payload diff --git a/ipc/kdbus/endpoint.h b/ipc/kdbus/endpoint.h index bc1b94a70..e0da59f01 100644 --- a/ipc/kdbus/endpoint.h +++ b/ipc/kdbus/endpoint.h @@ -61,6 +61,9 @@ struct kdbus_ep *kdbus_ep_new(struct kdbus_bus *bus, const char *name, struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep); struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep); +bool kdbus_ep_is_privileged(struct kdbus_ep *ep, struct file *file); +bool kdbus_ep_is_owner(struct kdbus_ep *ep, struct file *file); + struct kdbus_ep *kdbus_cmd_ep_make(struct kdbus_bus *bus, void __user *argp); int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp); diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c index e0e06b0e1..fc60932d6 100644 --- a/ipc/kdbus/handle.c +++ b/ipc/kdbus/handle.c @@ -264,7 +264,6 @@ enum kdbus_handle_type { * @bus_owner: bus this handle owns * @ep_owner: endpoint this handle owns * @conn: connection this handle owns - * @privileged: Flag to mark a handle as privileged */ struct kdbus_handle { struct mutex lock; @@ -275,8 +274,6 @@ struct kdbus_handle { struct kdbus_ep *ep_owner; struct kdbus_conn *conn; }; - - bool privileged:1; }; static int kdbus_handle_open(struct inode *inode, struct file *file) @@ -298,23 +295,6 @@ static int kdbus_handle_open(struct inode *inode, struct file *file) mutex_init(&handle->lock); handle->type = KDBUS_HANDLE_NONE; - if (node->type == KDBUS_NODE_ENDPOINT) { - struct kdbus_ep *ep = kdbus_ep_from_node(node); - struct kdbus_bus *bus = ep->bus; - - /* - * A connection is privileged if it is opened on an endpoint - * without custom policy and either: - * * the user has CAP_IPC_OWNER in the domain user namespace - * or - * * the callers euid matches the uid of the bus creator - */ - if (!ep->user && - (ns_capable(bus->domain->user_namespace, CAP_IPC_OWNER) || - uid_eq(file->f_cred->euid, bus->node.uid))) - handle->privileged = true; - } - file->private_data = handle; ret = 0; @@ -406,6 +386,7 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, struct kdbus_handle *handle = file->private_data; struct kdbus_node *node = file_inode(file)->i_private; struct kdbus_ep *ep, *file_ep = kdbus_ep_from_node(node); + struct kdbus_bus *bus = file_ep->bus; struct kdbus_conn *conn; int ret = 0; @@ -413,14 +394,14 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, return -ESHUTDOWN; switch (cmd) { - case KDBUS_CMD_ENDPOINT_MAKE: + case KDBUS_CMD_ENDPOINT_MAKE: { /* creating custom endpoints is a privileged operation */ - if (!handle->privileged) { + if (!kdbus_ep_is_owner(file_ep, file)) { ret = -EPERM; break; } - ep = kdbus_cmd_ep_make(file_ep->bus, buf); + ep = kdbus_cmd_ep_make(bus, buf); if (IS_ERR_OR_NULL(ep)) { ret = PTR_ERR_OR_ZERO(ep); break; @@ -429,9 +410,10 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, handle->ep_owner = ep; ret = KDBUS_HANDLE_EP_OWNER; break; + } case KDBUS_CMD_HELLO: - conn = kdbus_cmd_hello(file_ep, handle->privileged, buf); + conn = kdbus_cmd_hello(file_ep, file, buf); if (IS_ERR_OR_NULL(conn)) { ret = PTR_ERR_OR_ZERO(conn); break; diff --git a/ipc/kdbus/handle.h b/ipc/kdbus/handle.h index 8a36c0595..5dde2c10b 100644 --- a/ipc/kdbus/handle.h +++ b/ipc/kdbus/handle.h @@ -45,7 +45,7 @@ struct kdbus_arg { * @argv: array of items this command supports * @user: set by parser to user-space location of current command * @cmd: set by parser to kernel copy of command payload - * @cmd_buf: 512 bytes inline buf to avoid kmalloc() on small cmds + * @cmd_buf: inline buf to avoid kmalloc() on small cmds * @items: points to item array in @cmd * @items_size: size of @items in bytes * @is_cmd: whether this is a command-payload or msg-payload @@ -55,7 +55,7 @@ struct kdbus_arg { * the object to kdbus_args_parse(). The parser will copy the command payload * into kernel-space and verify the correctness of the data. * - * We use a 512 bytes buffer for small command payloads, to be allocated on + * We use a 256 bytes buffer for small command payloads, to be allocated on * stack on syscall entrance. */ struct kdbus_args { @@ -65,7 +65,7 @@ struct kdbus_args { struct kdbus_cmd __user *user; struct kdbus_cmd *cmd; - u8 cmd_buf[512]; + u8 cmd_buf[256]; struct kdbus_item *items; size_t items_size; diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c index 432dba4dc..ae565cd34 100644 --- a/ipc/kdbus/message.c +++ b/ipc/kdbus/message.c @@ -671,7 +671,7 @@ static struct kdbus_staging *kdbus_staging_new(struct kdbus_bus *bus, if (!staging) return ERR_PTR(-ENOMEM); - staging->msg_seqnum = atomic64_inc_return(&bus->domain->last_id); + staging->msg_seqnum = atomic64_inc_return(&bus->last_message_id); staging->n_parts = 0; /* we reserve n_parts, but don't enforce them */ staging->parts = (void *)(staging + 1); |