From 8e1dcbfc9654ada7a1deac571f0187fec8454571 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 18 Jan 2014 17:31:41 +0100 Subject: [PATCH] limit the number of buses and connections per user --- TODO | 3 --- bus.c | 19 +++++++++++++++++ bus.h | 2 ++ connection.c | 22 +++++++++++++++++++- connection.h | 2 ++ defaults.h | 6 ++++++ handle.c | 6 ++++++ kdbus.h | 2 ++ namespace.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ namespace.h | 24 ++++++++++++++++++++++ 10 files changed, 140 insertions(+), 4 deletions(-) diff --git a/TODO b/TODO index 62c257b..47273bb 100644 --- a/TODO +++ b/TODO @@ -21,9 +21,6 @@ Features: for another connection, like a connection can have a maximum of 100 messages in-flight, but only 10 of them for the same connection - - limit the number of buses an ordinary user can create - - limit the number of connections per uid - - allow to update the metadata subscription bit mask - support the creation of anonymous buses diff --git a/bus.c b/bus.c index 757c260..fdc4a1c 100644 --- a/bus.c +++ b/bus.c @@ -57,6 +57,10 @@ static void __kdbus_bus_free(struct kref *kref) struct kdbus_bus *bus = container_of(kref, struct kdbus_bus, kref); kdbus_bus_disconnect(bus); + + atomic_dec(&bus->user->buses); + kdbus_ns_user_unref(bus->user); + if (bus->name_registry) kdbus_name_registry_free(bus->name_registry); kdbus_ns_unref(bus->ns); @@ -237,6 +241,21 @@ int kdbus_bus_new(struct kdbus_ns *ns, if (ret < 0) goto exit; + /* account the bus against the user */ + b->user = kdbus_ns_user_ref(ns, uid); + if (!b->user) { + ret = -ENOMEM; + goto exit; + } + + if (!capable(CAP_IPC_OWNER) && + atomic_inc_return(&b->user->buses) > KDBUS_USER_MAX_BUSES) { + atomic_dec(&b->user->buses); + b->user = kdbus_ns_user_unref(b->user); + ret = -EMFILE; + goto exit; + } + /* link into namespace */ mutex_lock(&ns->lock); b->id = ++ns->bus_seq_last; diff --git a/bus.h b/bus.h index 15c0af4..2a2a859 100644 --- a/bus.h +++ b/bus.h @@ -38,6 +38,7 @@ * @ns_entry: Namespace's list of buses * @monitors_list: Connections that monitor this bus * @id128: Unique random 128 bit ID of this bus + * @user: Owner of the connection; * * A bus provides a "bus" endpoint / device node. * @@ -64,6 +65,7 @@ struct kdbus_bus { struct list_head ns_entry; struct list_head monitors_list; u8 id128[16]; + struct kdbus_ns_user *user; }; int kdbus_bus_make_user(void __user *buf, struct kdbus_cmd_make **make, diff --git a/connection.c b/connection.c index b952b4e..0bbdb18 100644 --- a/connection.c +++ b/connection.c @@ -1359,6 +1359,10 @@ static void __kdbus_conn_free(struct kref *kref) struct kdbus_conn_reply_entry *reply, *reply_tmp; kdbus_conn_disconnect(conn, false); + + atomic_dec(&conn->user->connections); + kdbus_ns_user_unref(conn->user); + if (conn->ep->policy_db) kdbus_policy_db_remove_conn(conn->ep->policy_db, conn); @@ -1824,6 +1828,20 @@ int kdbus_conn_new(struct kdbus_ep *ep, conn->meta = meta; } + /* account the connection against the user */ + conn->user = kdbus_ns_user_ref(ep->bus->ns, ep->bus->uid_owner); + if (!conn->user) { + ret = -ENOMEM; + goto exit_free_meta; + } + + if (!capable(CAP_IPC_OWNER) && + atomic_inc_return(&conn->user->connections) > KDBUS_USER_MAX_CONN) { + atomic_dec(&conn->user->connections); + ret = -EMFILE; + goto exit_unref_user; + } + /* link into bus */ mutex_lock(&bus->lock); hash_add(bus->conn_hash, &conn->hentry, conn->id); @@ -1832,6 +1850,8 @@ int kdbus_conn_new(struct kdbus_ep *ep, *c = conn; return 0; +exit_unref_user: + kdbus_ns_user_unref(conn->user); exit_free_meta: kdbus_meta_free(conn->owner_meta); exit_release_names: @@ -1850,7 +1870,7 @@ exit_free_conn: } /** - * kdbus_conn_has_name() - check if a connection owns a name + * kdbus_conn_has_name() - check if a connection owns r name * @conn: Connection * @name: Well-know name to check for * diff --git a/connection.h b/connection.h index f55b3b5..624cbd0 100644 --- a/connection.h +++ b/connection.h @@ -49,6 +49,7 @@ * HELLO * @msg_count: Number of queued messages * @pool: The user's buffer to receive messages + * @user: Owner of the connection; */ struct kdbus_conn { struct kref kref; @@ -76,6 +77,7 @@ struct kdbus_conn { struct kdbus_meta *owner_meta; unsigned int msg_count; struct kdbus_pool *pool; + struct kdbus_ns_user *user; }; struct kdbus_kmsg; diff --git a/defaults.h b/defaults.h index da6b83c..683b4e2 100644 --- a/defaults.h +++ b/defaults.h @@ -52,4 +52,10 @@ /* maximum number of queud requests waiting ot a reply */ #define KDBUS_CONN_MAX_REQUESTS_PENDING 64 +/* maximum number of connections per user in one namespace */ +#define KDBUS_USER_MAX_CONN 256 + +/* maximum number of buses per user in one namespace */ +#define KDBUS_USER_MAX_BUSES 16 + #endif diff --git a/handle.c b/handle.c index 4517cf5..c11195a 100644 --- a/handle.c +++ b/handle.c @@ -401,6 +401,12 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, kgid_t gid = KGIDT_INIT(0); char *name; + /* creating custom endpoints is a privileged operation */ + if (!kdbus_bus_uid_is_privileged(handle->ep->bus)) { + ret = -EFAULT; + break; + } + if (!KDBUS_IS_ALIGNED8((uintptr_t)buf)) { ret = -EFAULT; break; diff --git a/kdbus.h b/kdbus.h index c0a5391..36b362d 100644 --- a/kdbus.h +++ b/kdbus.h @@ -864,6 +864,8 @@ enum kdbus_ioctl_type { * refused to send as KDBUS_MSG_PAYLOAD_MEMFD. * @EMFILE: Too many file descriptors have been supplied with a * message. + * Too many connections or buses are created for a given + * user. * @EMLINK: Too many requests from this connection to other peers * are queued and waiting for a reply * @EMSGSIZE: The supplied data is larger than the allowed maximum diff --git a/namespace.c b/namespace.c index 56e32c7..66af3b6 100644 --- a/namespace.c +++ b/namespace.c @@ -392,3 +392,61 @@ exit: kfree(m); return ret; } + +struct kdbus_ns_user *kdbus_ns_user_ref(struct kdbus_ns *ns, kuid_t uid) +{ + struct kdbus_ns_user *u; + + /* find uid and reference it */ + mutex_lock(&ns->lock); + hash_for_each_possible(ns->user_hash, u, hentry, uid) { + if (u->uid != uid) + continue; + + kref_get(&u->kref); + mutex_unlock(&ns->lock); + return u; + } + mutex_unlock(&ns->lock); + + /* allocate a new user */ + u = kzalloc(GFP_KERNEL, sizeof(struct kdbus_ns_user)); + if (!u) + return NULL; + + kref_init(&u->kref); + u->ns = kdbus_ns_ref(ns); + u->uid = uid; + atomic_set(&u->buses, 0); + atomic_set(&u->connections, 0); + + /* link into namespace */ + mutex_lock(&ns->lock); + hash_add(ns->user_hash, &u->hentry, u->uid); + mutex_unlock(&ns->lock); + +printk("new user %u\n", u->uid); + return u; +} + +static void __kdbus_ns_user_free(struct kref *kref) +{ + struct kdbus_ns_user *user = container_of(kref, struct kdbus_ns_user, + kref); + + BUG_ON(atomic_read(&user->buses) > 0); + BUG_ON(atomic_read(&user->connections) > 0); + + mutex_lock(&user->ns->lock); + hash_del(&user->hentry); + mutex_unlock(&user->ns->lock); + kdbus_ns_unref(user->ns); +printk("user %u done\n", user->uid); + kfree(user); +} + +struct kdbus_ns_user *kdbus_ns_user_unref(struct kdbus_ns_user *user) +{ + kref_put(&user->kref, __kdbus_ns_user_free); + return NULL; +} diff --git a/namespace.h b/namespace.h index a5dbb9b..e36ce93 100644 --- a/namespace.h +++ b/namespace.h @@ -13,6 +13,7 @@ #ifndef __KDBUS_NS_H #define __KDBUS_NS_H +#include #include /** @@ -33,6 +34,7 @@ * @msg_seq_last: Last used message id sequence number * @ns_entry: Entry in parent namespace * @bus_list: Buses in this namespace + * @user_hash: Accounting of user resources * * A namespace provides a "control" device node. Every namespace has its * own major number for its endpoint device nodes. @@ -61,6 +63,25 @@ struct kdbus_ns { atomic64_t msg_seq_last; struct list_head ns_entry; struct list_head bus_list; + DECLARE_HASHTABLE(user_hash, 6); +}; + +/** + * struct kdbus_ns_user - resource accounting for users + * @kref: Reference counter + * @ns: Namespace of the user + * @hentry: Entry in namespace user map + * @uid: UID of the user + * @buses: Number of buses the user has created + * @connections: Number of connections the user has created + */ +struct kdbus_ns_user { + struct kref kref; + struct kdbus_ns *ns; + struct hlist_node hentry; + kuid_t uid; + atomic_t buses; + atomic_t connections; }; extern struct kdbus_ns *kdbus_ns_init; @@ -74,4 +95,7 @@ int kdbus_ns_new(struct kdbus_ns *parent, const char *name, int kdbus_ns_make_user(void __user *buf, struct kdbus_cmd_make **make, char **name); struct kdbus_ns *kdbus_ns_find_by_major(unsigned int major); + +struct kdbus_ns_user *kdbus_ns_user_ref(struct kdbus_ns *ns, kuid_t uid); +struct kdbus_ns_user *kdbus_ns_user_unref(struct kdbus_ns_user *user); #endif -- 2.34.1