aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/proc/proc_sysctl.c4
-rw-r--r--include/linux/sysctl.h7
-rw-r--r--kernel/sysctl.c25
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);
392out: 392out:
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 */
946struct ctl_table; 946struct ctl_table;
947struct nsproxy; 947struct nsproxy;
948struct ctl_table_root;
949
948extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); 950extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev);
949extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, 951extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
950 struct ctl_table_header *prev); 952 struct ctl_table_header *prev);
951extern void sysctl_head_finish(struct ctl_table_header *prev); 953extern void sysctl_head_finish(struct ctl_table_header *prev);
952extern int sysctl_perm(struct ctl_table *table, int op); 954extern int sysctl_perm(struct ctl_table_root *root,
955 struct ctl_table *table, int op);
953 956
954typedef struct ctl_table ctl_table; 957typedef 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. */
1437static int do_sysctl_strategy(struct ctl_table *table, 1437static 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,
1471static int parse_table(int __user *name, int nlen, 1472static 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
1567int sysctl_perm(struct ctl_table *table, int op) 1570int 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
1576static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) 1587static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table)