keys: make the keyring quotas controllable through /proc/sys
authorDavid Howells <dhowells@redhat.com>
Tue, 29 Apr 2008 08:01:32 +0000 (01:01 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Apr 2008 15:06:17 +0000 (08:06 -0700)
Make the keyring quotas controllable through /proc/sys files:

 (*) /proc/sys/kernel/keys/root_maxkeys
     /proc/sys/kernel/keys/root_maxbytes

     Maximum number of keys that root may have and the maximum total number of
     bytes of data that root may have stored in those keys.

 (*) /proc/sys/kernel/keys/maxkeys
     /proc/sys/kernel/keys/maxbytes

     Maximum number of keys that each non-root user may have and the maximum
     total number of bytes of data that each of those users may have stored in
     their keys.

Also increase the quotas as a number of people have been complaining that it's
not big enough.  I'm not sure that it's big enough now either, but on the
other hand, it can now be set in /etc/sysctl.conf.

Signed-off-by: David Howells <dhowells@redhat.com>
Cc: <kwc@citi.umich.edu>
Cc: <arunsr@cse.iitk.ac.in>
Cc: <dwalsh@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/keys.txt
include/linux/key.h
kernel/sysctl.c
security/keys/Makefile
security/keys/internal.h
security/keys/key.c
security/keys/keyctl.c
security/keys/proc.c
security/keys/sysctl.c [new file with mode: 0644]

index be424b02437ddf975bc7d97512e05950d6083915..d5c7a57d17007fa0f43558b270b0e4c302d5f8aa 100644 (file)
@@ -170,7 +170,8 @@ The key service provides a number of features besides keys:
      amount of description and payload space that can be consumed.
 
      The user can view information on this and other statistics through procfs
-     files.
+     files.  The root user may also alter the quota limits through sysctl files
+     (see the section "New procfs files").
 
      Process-specific and thread-specific keyrings are not counted towards a
      user's quota.
@@ -329,6 +330,27 @@ about the status of the key service:
        <bytes>/<max>           Key size quota
 
 
+Four new sysctl files have been added also for the purpose of controlling the
+quota limits on keys:
+
+ (*) /proc/sys/kernel/keys/root_maxkeys
+     /proc/sys/kernel/keys/root_maxbytes
+
+     These files hold the maximum number of keys that root may have and the
+     maximum total number of bytes of data that root may have stored in those
+     keys.
+
+ (*) /proc/sys/kernel/keys/maxkeys
+     /proc/sys/kernel/keys/maxbytes
+
+     These files hold the maximum number of keys that each non-root user may
+     have and the maximum total number of bytes of data that each of those
+     users may have stored in their keys.
+
+Root may alter these by writing each new limit as a decimal number string to
+the appropriate file.
+
+
 ===============================
 USERSPACE SYSTEM CALL INTERFACE
 ===============================
index 2effd031a817db384ca6df1de31e44237111d7cc..ad02d9cfe170f3861b9ad93f0a6128d4f9631ebc 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/list.h>
 #include <linux/rbtree.h>
 #include <linux/rcupdate.h>
+#include <linux/sysctl.h>
 #include <asm/atomic.h>
 
 #ifdef __KERNEL__
@@ -265,6 +266,10 @@ extern struct key *key_lookup(key_serial_t id);
 
 #define key_serial(key) ((key) ? (key)->serial : 0)
 
+#ifdef CONFIG_SYSCTL
+extern ctl_table key_sysctls[];
+#endif
+
 /*
  * the userspace interface
  */
index fd3364827ccf0a838c79fe240016c93cedd508e4..0a1d2733cf4130028ab2f9d49c0cbfddd6f9a7e9 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/writeback.h>
 #include <linux/hugetlb.h>
 #include <linux/initrd.h>
+#include <linux/key.h>
 #include <linux/times.h>
 #include <linux/limits.h>
 #include <linux/dcache.h>
@@ -809,6 +810,14 @@ static struct ctl_table kern_table[] = {
                .proc_handler   = &proc_dostring,
                .strategy       = &sysctl_string,
        },
+#ifdef CONFIG_KEYS
+       {
+               .ctl_name       = CTL_UNNUMBERED,
+               .procname       = "keys",
+               .mode           = 0555,
+               .child          = key_sysctls,
+       },
+#endif
 /*
  * NOTE: do not add new entries to this table unless you have read
  * Documentation/sysctl/ctl_unnumbered.txt
index 5145adfb6a05cabec8864b81ee7c5f692dbf03cd..747a464943aff0ada556e903fa6750fc7b670b60 100644 (file)
@@ -14,3 +14,4 @@ obj-y := \
 
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
+obj-$(CONFIG_SYSCTL) += sysctl.o
index 2ab38854c47fafb7e46b1498fcc9738cc4e9cd51..8c05587f5018b49147bf44ed5f4ad846a173573b 100644 (file)
@@ -57,10 +57,6 @@ struct key_user {
        int                     qnbytes;        /* number of bytes allocated to this user */
 };
 
-#define KEYQUOTA_MAX_KEYS      100
-#define KEYQUOTA_MAX_BYTES     10000
-#define KEYQUOTA_LINK_BYTES    4               /* a link in a keyring is worth 4 bytes */
-
 extern struct rb_root  key_user_tree;
 extern spinlock_t      key_user_lock;
 extern struct key_user root_key_user;
@@ -68,6 +64,16 @@ extern struct key_user       root_key_user;
 extern struct key_user *key_user_lookup(uid_t uid);
 extern void key_user_put(struct key_user *user);
 
+/*
+ * key quota limits
+ * - root has its own separate limits to everyone else
+ */
+extern unsigned key_quota_root_maxkeys;
+extern unsigned key_quota_root_maxbytes;
+extern unsigned key_quota_maxkeys;
+extern unsigned key_quota_maxbytes;
+
+#define KEYQUOTA_LINK_BYTES    4               /* a link in a keyring is worth 4 bytes */
 
 
 extern struct rb_root key_serial_tree;
index 46f125aa7fa30713586ac8a3ba990739cae73faa..14948cf83ef6acc6ea19811465201ff5ae8660f0 100644 (file)
@@ -27,6 +27,11 @@ DEFINE_SPINLOCK(key_serial_lock);
 struct rb_root key_user_tree; /* tree of quota records indexed by UID */
 DEFINE_SPINLOCK(key_user_lock);
 
+unsigned int key_quota_root_maxkeys = 200;     /* root's key count quota */
+unsigned int key_quota_root_maxbytes = 20000;  /* root's key space quota */
+unsigned int key_quota_maxkeys = 200;          /* general key count quota */
+unsigned int key_quota_maxbytes = 20000;       /* general key space quota */
+
 static LIST_HEAD(key_types_list);
 static DECLARE_RWSEM(key_types_sem);
 
@@ -236,11 +241,16 @@ struct key *key_alloc(struct key_type *type, const char *desc,
        /* check that the user's quota permits allocation of another key and
         * its description */
        if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
+               unsigned maxkeys = (uid == 0) ?
+                       key_quota_root_maxkeys : key_quota_maxkeys;
+               unsigned maxbytes = (uid == 0) ?
+                       key_quota_root_maxbytes : key_quota_maxbytes;
+
                spin_lock(&user->lock);
                if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) {
-                       if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS ||
-                           user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES
-                           )
+                       if (user->qnkeys + 1 >= maxkeys ||
+                           user->qnbytes + quotalen >= maxbytes ||
+                           user->qnbytes + quotalen < user->qnbytes)
                                goto no_quota;
                }
 
@@ -345,11 +355,14 @@ int key_payload_reserve(struct key *key, size_t datalen)
 
        /* contemplate the quota adjustment */
        if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+               unsigned maxbytes = (key->user->uid == 0) ?
+                       key_quota_root_maxbytes : key_quota_maxbytes;
+
                spin_lock(&key->user->lock);
 
                if (delta > 0 &&
-                   key->user->qnbytes + delta > KEYQUOTA_MAX_BYTES
-                   ) {
+                   (key->user->qnbytes + delta >= maxbytes ||
+                    key->user->qnbytes + delta < key->user->qnbytes)) {
                        ret = -EDQUOT;
                }
                else {
index 993be634a5efcce1d5f95666570b206b505cfa71..acc9c89e40a8de6937cba2971c8805e09a4d0f6f 100644 (file)
@@ -731,10 +731,16 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
 
                /* transfer the quota burden to the new user */
                if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+                       unsigned maxkeys = (uid == 0) ?
+                               key_quota_root_maxkeys : key_quota_maxkeys;
+                       unsigned maxbytes = (uid == 0) ?
+                               key_quota_root_maxbytes : key_quota_maxbytes;
+
                        spin_lock(&newowner->lock);
-                       if (newowner->qnkeys + 1 >= KEYQUOTA_MAX_KEYS ||
-                           newowner->qnbytes + key->quotalen >=
-                           KEYQUOTA_MAX_BYTES)
+                       if (newowner->qnkeys + 1 >= maxkeys ||
+                           newowner->qnbytes + key->quotalen >= maxbytes ||
+                           newowner->qnbytes + key->quotalen <
+                           newowner->qnbytes)
                                goto quota_overrun;
 
                        newowner->qnkeys++;
index e54679b848cf80bc2500d3077212dc4f8d745add..f619170da760688f4d40c856ab2adb1a999fff26 100644 (file)
@@ -242,6 +242,10 @@ static int proc_key_users_show(struct seq_file *m, void *v)
 {
        struct rb_node *_p = v;
        struct key_user *user = rb_entry(_p, struct key_user, node);
+       unsigned maxkeys = (user->uid == 0) ?
+               key_quota_root_maxkeys : key_quota_maxkeys;
+       unsigned maxbytes = (user->uid == 0) ?
+               key_quota_root_maxbytes : key_quota_maxbytes;
 
        seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
                   user->uid,
@@ -249,10 +253,9 @@ static int proc_key_users_show(struct seq_file *m, void *v)
                   atomic_read(&user->nkeys),
                   atomic_read(&user->nikeys),
                   user->qnkeys,
-                  KEYQUOTA_MAX_KEYS,
+                  maxkeys,
                   user->qnbytes,
-                  KEYQUOTA_MAX_BYTES
-                  );
+                  maxbytes);
 
        return 0;
 
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
new file mode 100644 (file)
index 0000000..b611d49
--- /dev/null
@@ -0,0 +1,50 @@
+/* Key management controls
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/key.h>
+#include <linux/sysctl.h>
+#include "internal.h"
+
+ctl_table key_sysctls[] = {
+       {
+               .ctl_name = CTL_UNNUMBERED,
+               .procname = "maxkeys",
+               .data = &key_quota_maxkeys,
+               .maxlen = sizeof(unsigned),
+               .mode = 0644,
+               .proc_handler = &proc_dointvec,
+       },
+       {
+               .ctl_name = CTL_UNNUMBERED,
+               .procname = "maxbytes",
+               .data = &key_quota_maxbytes,
+               .maxlen = sizeof(unsigned),
+               .mode = 0644,
+               .proc_handler = &proc_dointvec,
+       },
+       {
+               .ctl_name = CTL_UNNUMBERED,
+               .procname = "root_maxkeys",
+               .data = &key_quota_root_maxkeys,
+               .maxlen = sizeof(unsigned),
+               .mode = 0644,
+               .proc_handler = &proc_dointvec,
+       },
+       {
+               .ctl_name = CTL_UNNUMBERED,
+               .procname = "root_maxbytes",
+               .data = &key_quota_root_maxbytes,
+               .maxlen = sizeof(unsigned),
+               .mode = 0644,
+               .proc_handler = &proc_dointvec,
+       },
+       { .ctl_name = 0 }
+};