summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2014-01-18 17:31:41 +0100
committerKay Sievers <kay@vrfy.org>2014-01-18 17:31:41 +0100
commit8e1dcbfc9654ada7a1deac571f0187fec8454571 (patch)
tree83c8e2206135e8f920ae313472158ef0c871e2d9
parenta4017447d1da830993fcb62b5ec45fd14b17c566 (diff)
downloadkdbus-bus-8e1dcbfc9654ada7a1deac571f0187fec8454571.tar.gz
kdbus-bus-8e1dcbfc9654ada7a1deac571f0187fec8454571.tar.bz2
kdbus-bus-8e1dcbfc9654ada7a1deac571f0187fec8454571.zip
limit the number of buses and connections per user
-rw-r--r--TODO3
-rw-r--r--bus.c19
-rw-r--r--bus.h2
-rw-r--r--connection.c22
-rw-r--r--connection.h2
-rw-r--r--defaults.h6
-rw-r--r--handle.c6
-rw-r--r--kdbus.h2
-rw-r--r--namespace.c58
-rw-r--r--namespace.h24
10 files changed, 140 insertions, 4 deletions
diff --git a/TODO b/TODO
index 62c257bc292..47273bb7ab4 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 757c260f7cb..fdc4a1c77e2 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 15c0af4648f..2a2a859ed5b 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 b952b4e4854..0bbdb182d41 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 f55b3b57ba8..624cbd0de4e 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 da6b83c986e..683b4e2c56b 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 4517cf5744c..c11195a6937 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 c0a539149f1..36b362d449a 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 56e32c734c2..66af3b6a875 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 a5dbb9b8ba6..e36ce939d64 100644
--- a/namespace.h
+++ b/namespace.h
@@ -13,6 +13,7 @@
#ifndef __KDBUS_NS_H
#define __KDBUS_NS_H
+#include <linux/hashtable.h>
#include <linux/idr.h>
/**
@@ -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