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.
        <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
 ===============================
 
 #include <linux/list.h>
 #include <linux/rbtree.h>
 #include <linux/rcupdate.h>
+#include <linux/sysctl.h>
 #include <asm/atomic.h>
 
 #ifdef __KERNEL__
 
 #define key_serial(key) ((key) ? (key)->serial : 0)
 
+#ifdef CONFIG_SYSCTL
+extern ctl_table key_sysctls[];
+#endif
+
 /*
  * the userspace interface
  */
 
 #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>
                .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
 
 
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
+obj-$(CONFIG_SYSCTL) += sysctl.o
 
        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;
 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;
 
 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);
 
        /* 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;
                }
 
 
        /* 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 {
 
 
                /* 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++;
 
 {
        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,
                   atomic_read(&user->nkeys),
                   atomic_read(&user->nikeys),
                   user->qnkeys,
-                  KEYQUOTA_MAX_KEYS,
+                  maxkeys,
                   user->qnbytes,
-                  KEYQUOTA_MAX_BYTES
-                  );
+                  maxbytes);
 
        return 0;
 
 
--- /dev/null
+/* 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 }
+};