aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@openvz.org>2008-04-29 04:02:44 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 11:06:23 -0400
commitd7321cd62470b70d2717dae5a963e7a8fabff4d5 (patch)
tree1e3468e44239a310b03c3f8dabf754e64b6ad281
parent2c4c7155f25192da3511a6c911db4d08102d36c4 (diff)
sysctl: add the ->permissions callback on the ctl_table_root
When reading from/writing to some table, a root, which this table came from, may affect this table's permissions, depending on who is working with the table. The core hunk is at the bottom of this patch. All the rest is just pushing the ctl_table_root argument up to the sysctl_perm() function. This will be mostly (only?) used in the net sysctls. Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Acked-by: David S. Miller <davem@davemloft.net> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Alexey Dobriyan <adobriyan@sw.ru> Cc: Denis V. Lunev <den@openvz.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-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)