| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 1 | #include <linux/stat.h> | 
|  | 2 | #include <linux/sysctl.h> | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 3 | #include "../fs/xfs/linux-2.6/xfs_sysctl.h" | 
|  | 4 | #include <linux/sunrpc/debug.h> | 
|  | 5 | #include <linux/string.h> | 
|  | 6 | #include <net/ip_vs.h> | 
|  | 7 |  | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 8 |  | 
|  | 9 | static int sysctl_depth(struct ctl_table *table) | 
|  | 10 | { | 
|  | 11 | struct ctl_table *tmp; | 
|  | 12 | int depth; | 
|  | 13 |  | 
|  | 14 | depth = 0; | 
|  | 15 | for (tmp = table; tmp->parent; tmp = tmp->parent) | 
|  | 16 | depth++; | 
|  | 17 |  | 
|  | 18 | return depth; | 
|  | 19 | } | 
|  | 20 |  | 
|  | 21 | static struct ctl_table *sysctl_parent(struct ctl_table *table, int n) | 
|  | 22 | { | 
|  | 23 | int i; | 
|  | 24 |  | 
|  | 25 | for (i = 0; table && i < n; i++) | 
|  | 26 | table = table->parent; | 
|  | 27 |  | 
|  | 28 | return table; | 
|  | 29 | } | 
|  | 30 |  | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 31 |  | 
|  | 32 | static void sysctl_print_path(struct ctl_table *table) | 
|  | 33 | { | 
|  | 34 | struct ctl_table *tmp; | 
|  | 35 | int depth, i; | 
|  | 36 | depth = sysctl_depth(table); | 
|  | 37 | if (table->procname) { | 
|  | 38 | for (i = depth; i >= 0; i--) { | 
|  | 39 | tmp = sysctl_parent(table, i); | 
|  | 40 | printk("/%s", tmp->procname?tmp->procname:""); | 
|  | 41 | } | 
|  | 42 | } | 
|  | 43 | printk(" "); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 44 | } | 
|  | 45 |  | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 46 | static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces, | 
|  | 47 | struct ctl_table *table) | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 48 | { | 
|  | 49 | struct ctl_table_header *head; | 
|  | 50 | struct ctl_table *ref, *test; | 
|  | 51 | int depth, cur_depth; | 
|  | 52 |  | 
|  | 53 | depth = sysctl_depth(table); | 
|  | 54 |  | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 55 | for (head = __sysctl_head_next(namespaces, NULL); head; | 
|  | 56 | head = __sysctl_head_next(namespaces, head)) { | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 57 | cur_depth = depth; | 
|  | 58 | ref = head->ctl_table; | 
|  | 59 | repeat: | 
|  | 60 | test = sysctl_parent(table, cur_depth); | 
| Eric W. Biederman | 83ac201 | 2009-04-03 02:22:26 -0700 | [diff] [blame] | 61 | for (; ref->procname; ref++) { | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 62 | int match = 0; | 
|  | 63 | if (cur_depth && !ref->child) | 
|  | 64 | continue; | 
|  | 65 |  | 
|  | 66 | if (test->procname && ref->procname && | 
|  | 67 | (strcmp(test->procname, ref->procname) == 0)) | 
|  | 68 | match++; | 
|  | 69 |  | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 70 | if (match) { | 
|  | 71 | if (cur_depth != 0) { | 
|  | 72 | cur_depth--; | 
|  | 73 | ref = ref->child; | 
|  | 74 | goto repeat; | 
|  | 75 | } | 
|  | 76 | goto out; | 
|  | 77 | } | 
|  | 78 | } | 
|  | 79 | } | 
|  | 80 | ref = NULL; | 
|  | 81 | out: | 
|  | 82 | sysctl_head_finish(head); | 
|  | 83 | return ref; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | static void set_fail(const char **fail, struct ctl_table *table, const char *str) | 
|  | 87 | { | 
|  | 88 | if (*fail) { | 
|  | 89 | printk(KERN_ERR "sysctl table check failed: "); | 
|  | 90 | sysctl_print_path(table); | 
|  | 91 | printk(" %s\n", *fail); | 
| Alexey Dobriyan | 5db6a4d | 2007-11-05 14:50:52 -0800 | [diff] [blame] | 92 | dump_stack(); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 93 | } | 
|  | 94 | *fail = str; | 
|  | 95 | } | 
|  | 96 |  | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 97 | static void sysctl_check_leaf(struct nsproxy *namespaces, | 
|  | 98 | struct ctl_table *table, const char **fail) | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 99 | { | 
|  | 100 | struct ctl_table *ref; | 
|  | 101 |  | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 102 | ref = sysctl_check_lookup(namespaces, table); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 103 | if (ref && (ref != table)) | 
|  | 104 | set_fail(fail, table, "Sysctl already exists"); | 
|  | 105 | } | 
|  | 106 |  | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 107 | int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table) | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 108 | { | 
|  | 109 | int error = 0; | 
| Eric W. Biederman | 83ac201 | 2009-04-03 02:22:26 -0700 | [diff] [blame] | 110 | for (; table->procname; table++) { | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 111 | const char *fail = NULL; | 
|  | 112 |  | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 113 | if (table->parent) { | 
| Denis Kirjanov | 814ecf6 | 2011-03-23 16:43:08 -0700 | [diff] [blame] | 114 | if (!table->parent->procname) | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 115 | set_fail(&fail, table, "Parent without procname"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 116 | } | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 117 | if (table->child) { | 
|  | 118 | if (table->data) | 
|  | 119 | set_fail(&fail, table, "Directory with data?"); | 
|  | 120 | if (table->maxlen) | 
|  | 121 | set_fail(&fail, table, "Directory with maxlen?"); | 
|  | 122 | if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode) | 
|  | 123 | set_fail(&fail, table, "Writable sysctl directory"); | 
|  | 124 | if (table->proc_handler) | 
|  | 125 | set_fail(&fail, table, "Directory with proc_handler"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 126 | if (table->extra1) | 
|  | 127 | set_fail(&fail, table, "Directory with extra1"); | 
|  | 128 | if (table->extra2) | 
|  | 129 | set_fail(&fail, table, "Directory with extra2"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 130 | } else { | 
| Eric W. Biederman | 83ac201 | 2009-04-03 02:22:26 -0700 | [diff] [blame] | 131 | if ((table->proc_handler == proc_dostring) || | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 132 | (table->proc_handler == proc_dointvec) || | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 133 | (table->proc_handler == proc_dointvec_minmax) || | 
|  | 134 | (table->proc_handler == proc_dointvec_jiffies) || | 
|  | 135 | (table->proc_handler == proc_dointvec_userhz_jiffies) || | 
|  | 136 | (table->proc_handler == proc_dointvec_ms_jiffies) || | 
|  | 137 | (table->proc_handler == proc_doulongvec_minmax) || | 
|  | 138 | (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) { | 
|  | 139 | if (!table->data) | 
|  | 140 | set_fail(&fail, table, "No data"); | 
|  | 141 | if (!table->maxlen) | 
|  | 142 | set_fail(&fail, table, "No maxlen"); | 
|  | 143 | } | 
| Alexey Dobriyan | 8c85dd8 | 2009-10-26 16:50:07 -0700 | [diff] [blame] | 144 | #ifdef CONFIG_PROC_SYSCTL | 
| Denis Kirjanov | 814ecf6 | 2011-03-23 16:43:08 -0700 | [diff] [blame] | 145 | if (!table->proc_handler) | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 146 | set_fail(&fail, table, "No proc_handler"); | 
| Eric W. Biederman | 49ffcf8 | 2007-10-18 03:05:57 -0700 | [diff] [blame] | 147 | #endif | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 148 | sysctl_check_leaf(namespaces, table, &fail); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 149 | } | 
| Alexey Dobriyan | 99541c2 | 2008-07-25 01:48:31 -0700 | [diff] [blame] | 150 | if (table->mode > 0777) | 
|  | 151 | set_fail(&fail, table, "bogus .mode"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 152 | if (fail) { | 
|  | 153 | set_fail(&fail, table, NULL); | 
|  | 154 | error = -EINVAL; | 
|  | 155 | } | 
|  | 156 | if (table->child) | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 157 | error |= sysctl_check_table(namespaces, table->child); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 158 | } | 
|  | 159 | return error; | 
|  | 160 | } |