diff options
-rw-r--r-- | fs/proc/proc_sysctl.c | 4 | ||||
-rw-r--r-- | include/linux/sysctl.h | 7 | ||||
-rw-r--r-- | kernel/sysctl.c | 25 |
3 files changed, 26 insertions, 10 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 5e31585292a0..5acc001d49f6 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -190,7 +190,7 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, | |||
190 | * and won't be until we finish. | 190 | * and won't be until we finish. |
191 | */ | 191 | */ |
192 | error = -EPERM; | 192 | error = -EPERM; |
193 | if (sysctl_perm(table, write ? MAY_WRITE : MAY_READ)) | 193 | if (sysctl_perm(head->root, table, write ? MAY_WRITE : MAY_READ)) |
194 | goto out; | 194 | goto out; |
195 | 195 | ||
196 | /* careful: calling conventions are nasty here */ | 196 | /* careful: calling conventions are nasty here */ |
@@ -388,7 +388,7 @@ static int proc_sys_permission(struct inode *inode, int mask, struct nameidata * | |||
388 | goto out; | 388 | goto out; |
389 | 389 | ||
390 | /* Use the permissions on the sysctl table entry */ | 390 | /* Use the permissions on the sysctl table entry */ |
391 | error = sysctl_perm(table, mask); | 391 | error = sysctl_perm(head->root, table, mask); |
392 | out: | 392 | out: |
393 | sysctl_head_finish(head); | 393 | sysctl_head_finish(head); |
394 | return error; | 394 | return error; |
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 39eafd8f97a3..24141b4d1a11 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
@@ -945,11 +945,14 @@ enum | |||
945 | /* For the /proc/sys support */ | 945 | /* For the /proc/sys support */ |
946 | struct ctl_table; | 946 | struct ctl_table; |
947 | struct nsproxy; | 947 | struct nsproxy; |
948 | struct ctl_table_root; | ||
949 | |||
948 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); | 950 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); |
949 | extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | 951 | extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, |
950 | struct ctl_table_header *prev); | 952 | struct ctl_table_header *prev); |
951 | extern void sysctl_head_finish(struct ctl_table_header *prev); | 953 | extern void sysctl_head_finish(struct ctl_table_header *prev); |
952 | extern int sysctl_perm(struct ctl_table *table, int op); | 954 | extern int sysctl_perm(struct ctl_table_root *root, |
955 | struct ctl_table *table, int op); | ||
953 | 956 | ||
954 | typedef struct ctl_table ctl_table; | 957 | typedef struct ctl_table ctl_table; |
955 | 958 | ||
@@ -1049,6 +1052,8 @@ struct ctl_table_root { | |||
1049 | struct list_head header_list; | 1052 | struct list_head header_list; |
1050 | struct list_head *(*lookup)(struct ctl_table_root *root, | 1053 | struct list_head *(*lookup)(struct ctl_table_root *root, |
1051 | struct nsproxy *namespaces); | 1054 | struct nsproxy *namespaces); |
1055 | int (*permissions)(struct ctl_table_root *root, | ||
1056 | struct nsproxy *namespaces, struct ctl_table *table); | ||
1052 | }; | 1057 | }; |
1053 | 1058 | ||
1054 | /* struct ctl_table_header is used to maintain dynamic lists of | 1059 | /* struct ctl_table_header is used to maintain dynamic lists of |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 874e813e40c8..d7ffdc59816a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -1434,7 +1434,8 @@ void register_sysctl_root(struct ctl_table_root *root) | |||
1434 | 1434 | ||
1435 | #ifdef CONFIG_SYSCTL_SYSCALL | 1435 | #ifdef CONFIG_SYSCTL_SYSCALL |
1436 | /* Perform the actual read/write of a sysctl table entry. */ | 1436 | /* Perform the actual read/write of a sysctl table entry. */ |
1437 | static int do_sysctl_strategy(struct ctl_table *table, | 1437 | static int do_sysctl_strategy(struct ctl_table_root *root, |
1438 | struct ctl_table *table, | ||
1438 | int __user *name, int nlen, | 1439 | int __user *name, int nlen, |
1439 | void __user *oldval, size_t __user *oldlenp, | 1440 | void __user *oldval, size_t __user *oldlenp, |
1440 | void __user *newval, size_t newlen) | 1441 | void __user *newval, size_t newlen) |
@@ -1445,7 +1446,7 @@ static int do_sysctl_strategy(struct ctl_table *table, | |||
1445 | op |= 004; | 1446 | op |= 004; |
1446 | if (newval) | 1447 | if (newval) |
1447 | op |= 002; | 1448 | op |= 002; |
1448 | if (sysctl_perm(table, op)) | 1449 | if (sysctl_perm(root, table, op)) |
1449 | return -EPERM; | 1450 | return -EPERM; |
1450 | 1451 | ||
1451 | if (table->strategy) { | 1452 | if (table->strategy) { |
@@ -1471,6 +1472,7 @@ static int do_sysctl_strategy(struct ctl_table *table, | |||
1471 | static int parse_table(int __user *name, int nlen, | 1472 | static int parse_table(int __user *name, int nlen, |
1472 | void __user *oldval, size_t __user *oldlenp, | 1473 | void __user *oldval, size_t __user *oldlenp, |
1473 | void __user *newval, size_t newlen, | 1474 | void __user *newval, size_t newlen, |
1475 | struct ctl_table_root *root, | ||
1474 | struct ctl_table *table) | 1476 | struct ctl_table *table) |
1475 | { | 1477 | { |
1476 | int n; | 1478 | int n; |
@@ -1485,14 +1487,14 @@ repeat: | |||
1485 | if (n == table->ctl_name) { | 1487 | if (n == table->ctl_name) { |
1486 | int error; | 1488 | int error; |
1487 | if (table->child) { | 1489 | if (table->child) { |
1488 | if (sysctl_perm(table, 001)) | 1490 | if (sysctl_perm(root, table, 001)) |
1489 | return -EPERM; | 1491 | return -EPERM; |
1490 | name++; | 1492 | name++; |
1491 | nlen--; | 1493 | nlen--; |
1492 | table = table->child; | 1494 | table = table->child; |
1493 | goto repeat; | 1495 | goto repeat; |
1494 | } | 1496 | } |
1495 | error = do_sysctl_strategy(table, name, nlen, | 1497 | error = do_sysctl_strategy(root, table, name, nlen, |
1496 | oldval, oldlenp, | 1498 | oldval, oldlenp, |
1497 | newval, newlen); | 1499 | newval, newlen); |
1498 | return error; | 1500 | return error; |
@@ -1518,7 +1520,8 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol | |||
1518 | for (head = sysctl_head_next(NULL); head; | 1520 | for (head = sysctl_head_next(NULL); head; |
1519 | head = sysctl_head_next(head)) { | 1521 | head = sysctl_head_next(head)) { |
1520 | error = parse_table(name, nlen, oldval, oldlenp, | 1522 | error = parse_table(name, nlen, oldval, oldlenp, |
1521 | newval, newlen, head->ctl_table); | 1523 | newval, newlen, |
1524 | head->root, head->ctl_table); | ||
1522 | if (error != -ENOTDIR) { | 1525 | if (error != -ENOTDIR) { |
1523 | sysctl_head_finish(head); | 1526 | sysctl_head_finish(head); |
1524 | break; | 1527 | break; |
@@ -1564,13 +1567,21 @@ static int test_perm(int mode, int op) | |||
1564 | return -EACCES; | 1567 | return -EACCES; |
1565 | } | 1568 | } |
1566 | 1569 | ||
1567 | int sysctl_perm(struct ctl_table *table, int op) | 1570 | int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) |
1568 | { | 1571 | { |
1569 | int error; | 1572 | int error; |
1573 | int mode; | ||
1574 | |||
1570 | error = security_sysctl(table, op); | 1575 | error = security_sysctl(table, op); |
1571 | if (error) | 1576 | if (error) |
1572 | return error; | 1577 | return error; |
1573 | return test_perm(table->mode, op); | 1578 | |
1579 | if (root->permissions) | ||
1580 | mode = root->permissions(root, current->nsproxy, table); | ||
1581 | else | ||
1582 | mode = table->mode; | ||
1583 | |||
1584 | return test_perm(mode, op); | ||
1574 | } | 1585 | } |
1575 | 1586 | ||
1576 | static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) | 1587 | static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) |