1 #include <linux/stat.h>
2 #include <linux/sysctl.h>
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 
8 
sysctl_depth(struct ctl_table * table)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 
sysctl_parent(struct ctl_table * table,int n)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 
31 
sysctl_print_path(struct ctl_table * table)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(" ");
44 }
45 
sysctl_check_lookup(struct nsproxy * namespaces,struct ctl_table * table)46 static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces,
47 						struct ctl_table *table)
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 
55 	for (head = __sysctl_head_next(namespaces, NULL); head;
56 	     head = __sysctl_head_next(namespaces, head)) {
57 		cur_depth = depth;
58 		ref = head->ctl_table;
59 repeat:
60 		test = sysctl_parent(table, cur_depth);
61 		for (; ref->procname; ref++) {
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 
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 
set_fail(const char ** fail,struct ctl_table * table,const char * str)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);
92 		dump_stack();
93 	}
94 	*fail = str;
95 }
96 
sysctl_check_leaf(struct nsproxy * namespaces,struct ctl_table * table,const char ** fail)97 static void sysctl_check_leaf(struct nsproxy *namespaces,
98 				struct ctl_table *table, const char **fail)
99 {
100 	struct ctl_table *ref;
101 
102 	ref = sysctl_check_lookup(namespaces, table);
103 	if (ref && (ref != table))
104 		set_fail(fail, table, "Sysctl already exists");
105 }
106 
sysctl_check_table(struct nsproxy * namespaces,struct ctl_table * table)107 int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table)
108 {
109 	int error = 0;
110 	for (; table->procname; table++) {
111 		const char *fail = NULL;
112 
113 		if (table->parent) {
114 			if (!table->parent->procname)
115 				set_fail(&fail, table, "Parent without procname");
116 		}
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");
126 			if (table->extra1)
127 				set_fail(&fail, table, "Directory with extra1");
128 			if (table->extra2)
129 				set_fail(&fail, table, "Directory with extra2");
130 		} else {
131 			if ((table->proc_handler == proc_dostring) ||
132 			    (table->proc_handler == proc_dointvec) ||
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 			}
144 #ifdef CONFIG_PROC_SYSCTL
145 			if (!table->proc_handler)
146 				set_fail(&fail, table, "No proc_handler");
147 #endif
148 			sysctl_check_leaf(namespaces, table, &fail);
149 		}
150 		if (table->mode > 0777)
151 			set_fail(&fail, table, "bogus .mode");
152 		if (fail) {
153 			set_fail(&fail, table, NULL);
154 			error = -EINVAL;
155 		}
156 		if (table->child)
157 			error |= sysctl_check_table(namespaces, table->child);
158 	}
159 	return error;
160 }
161