#include <linux/stat.h> #include <linux/sysctl.h> #include "../fs/xfs/xfs_sysctl.h" #include <linux/sunrpc/debug.h> #include <linux/string.h> #include <net/ip_vs.h> static int sysctl_depth(struct ctl_table *table) { struct ctl_table *tmp; int depth; depth = 0; for (tmp = table; tmp->parent; tmp = tmp->parent) depth++; return depth; } static struct ctl_table *sysctl_parent(struct ctl_table *table, int n) { int i; for (i = 0; table && i < n; i++) table = table->parent; return table; } static void sysctl_print_path(struct ctl_table *table) { struct ctl_table *tmp; int depth, i; depth = sysctl_depth(table); if (table->procname) { for (i = depth; i >= 0; i--) { tmp = sysctl_parent(table, i); printk("/%s", tmp->procname?tmp->procname:""); } } printk(" "); } static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces, struct ctl_table *table) { struct ctl_table_header *head; struct ctl_table *ref, *test; int depth, cur_depth; depth = sysctl_depth(table); for (head = __sysctl_head_next(namespaces, NULL); head; head = __sysctl_head_next(namespaces, head)) { cur_depth = depth; ref = head->ctl_table; repeat: test = sysctl_parent(table, cur_depth); for (; ref->procname; ref++) { int match = 0; if (cur_depth && !ref->child) continue; if (test->procname && ref->procname && (strcmp(test->procname, ref->procname) == 0)) match++; if (match) { if (cur_depth != 0) { cur_depth--; ref = ref->child; goto repeat; } goto out; } } } ref = NULL; out: sysctl_head_finish(head); return ref; } static void set_fail(const char **fail, struct ctl_table *table, const char *str) { if (*fail) { printk(KERN_ERR "sysctl table check failed: "); sysctl_print_path(table); printk(" %s\n", *fail); dump_stack(); } *fail = str; } static void sysctl_check_leaf(struct nsproxy *namespaces, struct ctl_table *table, const char **fail) { struct ctl_table *ref; ref = sysctl_check_lookup(namespaces, table); if (ref && (ref != table)) set_fail(fail, table, "Sysctl already exists"); } int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table) { int error = 0; for (; table->procname; table++) { const char *fail = NULL; if (table->parent) { if (!table->parent->procname) set_fail(&fail, table, "Parent without procname"); } if (table->child) { if (table->data) set_fail(&fail, table, "Directory with data?"); if (table->maxlen) set_fail(&fail, table, "Directory with maxlen?"); if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode) set_fail(&fail, table, "Writable sysctl directory"); if (table->proc_handler) set_fail(&fail, table, "Directory with proc_handler"); if (table->extra1) set_fail(&fail, table, "Directory with extra1"); if (table->extra2) set_fail(&fail, table, "Directory with extra2"); } else { if ((table->proc_handler == proc_dostring) || (table->proc_handler == proc_dointvec) || (table->proc_handler == proc_dointvec_minmax) || (table->proc_handler == proc_dointvec_jiffies) || (table->proc_handler == proc_dointvec_userhz_jiffies) || (table->proc_handler == proc_dointvec_ms_jiffies) || (table->proc_handler == proc_doulongvec_minmax) || (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) { if (!table->data) set_fail(&fail, table, "No data"); if (!table->maxlen) set_fail(&fail, table, "No maxlen"); } #ifdef CONFIG_PROC_SYSCTL if (!table->proc_handler) set_fail(&fail, table, "No proc_handler"); #endif sysctl_check_leaf(namespaces, table, &fail); } if (table->mode > 0777) set_fail(&fail, table, "bogus .mode"); if (fail) { set_fail(&fail, table, NULL); error = -EINVAL; } if (table->child) error |= sysctl_check_table(namespaces, table->child); } return error; }