| 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) { | 
 | 114 | 			if (table->procname && !table->parent->procname) | 
 | 115 | 				set_fail(&fail, table, "Parent without procname"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 116 | 		} | 
 | 117 | 		if (!table->procname) | 
 | 118 | 			set_fail(&fail, table, "No procname"); | 
 | 119 | 		if (table->child) { | 
 | 120 | 			if (table->data) | 
 | 121 | 				set_fail(&fail, table, "Directory with data?"); | 
 | 122 | 			if (table->maxlen) | 
 | 123 | 				set_fail(&fail, table, "Directory with maxlen?"); | 
 | 124 | 			if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode) | 
 | 125 | 				set_fail(&fail, table, "Writable sysctl directory"); | 
 | 126 | 			if (table->proc_handler) | 
 | 127 | 				set_fail(&fail, table, "Directory with proc_handler"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 128 | 			if (table->extra1) | 
 | 129 | 				set_fail(&fail, table, "Directory with extra1"); | 
 | 130 | 			if (table->extra2) | 
 | 131 | 				set_fail(&fail, table, "Directory with extra2"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 132 | 		} else { | 
| Eric W. Biederman | 83ac201 | 2009-04-03 02:22:26 -0700 | [diff] [blame] | 133 | 			if ((table->proc_handler == proc_dostring) || | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 134 | 			    (table->proc_handler == proc_dointvec) || | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 135 | 			    (table->proc_handler == proc_dointvec_minmax) || | 
 | 136 | 			    (table->proc_handler == proc_dointvec_jiffies) || | 
 | 137 | 			    (table->proc_handler == proc_dointvec_userhz_jiffies) || | 
 | 138 | 			    (table->proc_handler == proc_dointvec_ms_jiffies) || | 
 | 139 | 			    (table->proc_handler == proc_doulongvec_minmax) || | 
 | 140 | 			    (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) { | 
 | 141 | 				if (!table->data) | 
 | 142 | 					set_fail(&fail, table, "No data"); | 
 | 143 | 				if (!table->maxlen) | 
 | 144 | 					set_fail(&fail, table, "No maxlen"); | 
 | 145 | 			} | 
| Alexey Dobriyan | 8c85dd8 | 2009-10-26 16:50:07 -0700 | [diff] [blame] | 146 | #ifdef CONFIG_PROC_SYSCTL | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 147 | 			if (table->procname && !table->proc_handler) | 
 | 148 | 				set_fail(&fail, table, "No proc_handler"); | 
| Eric W. Biederman | 49ffcf8 | 2007-10-18 03:05:57 -0700 | [diff] [blame] | 149 | #endif | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 150 | #if 0 | 
 | 151 | 			if (!table->procname && table->proc_handler) | 
 | 152 | 				set_fail(&fail, table, "proc_handler without procname"); | 
 | 153 | #endif | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 154 | 			sysctl_check_leaf(namespaces, table, &fail); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 155 | 		} | 
| Alexey Dobriyan | 99541c2 | 2008-07-25 01:48:31 -0700 | [diff] [blame] | 156 | 		if (table->mode > 0777) | 
 | 157 | 			set_fail(&fail, table, "bogus .mode"); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 158 | 		if (fail) { | 
 | 159 | 			set_fail(&fail, table, NULL); | 
 | 160 | 			error = -EINVAL; | 
 | 161 | 		} | 
 | 162 | 		if (table->child) | 
| Eric W. Biederman | e51b6ba | 2007-11-30 23:54:00 +1100 | [diff] [blame] | 163 | 			error |= sysctl_check_table(namespaces, table->child); | 
| Eric W. Biederman | fc6cd25 | 2007-10-18 03:05:54 -0700 | [diff] [blame] | 164 | 	} | 
 | 165 | 	return error; | 
 | 166 | } |