Commit d7321cd62470b70d2717dae5a963e7a8fabff4d5

Authored by Pavel Emelyanov
Committed by Linus Torvalds
1 parent 2c4c7155f2

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>

Showing 3 changed files with 26 additions and 10 deletions Side-by-side Diff

fs/proc/proc_sysctl.c
... ... @@ -190,7 +190,7 @@
190 190 * and won't be until we finish.
191 191 */
192 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 194 goto out;
195 195  
196 196 /* careful: calling conventions are nasty here */
... ... @@ -388,7 +388,7 @@
388 388 goto out;
389 389  
390 390 /* Use the permissions on the sysctl table entry */
391   - error = sysctl_perm(table, mask);
  391 + error = sysctl_perm(head->root, table, mask);
392 392 out:
393 393 sysctl_head_finish(head);
394 394 return error;
include/linux/sysctl.h
... ... @@ -945,11 +945,14 @@
945 945 /* For the /proc/sys support */
946 946 struct ctl_table;
947 947 struct nsproxy;
  948 +struct ctl_table_root;
  949 +
948 950 extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev);
949 951 extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
950 952 struct ctl_table_header *prev);
951 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 957 typedef struct ctl_table ctl_table;
955 958  
... ... @@ -1049,6 +1052,8 @@
1049 1052 struct list_head header_list;
1050 1053 struct list_head *(*lookup)(struct ctl_table_root *root,
1051 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 1059 /* struct ctl_table_header is used to maintain dynamic lists of
... ... @@ -1434,7 +1434,8 @@
1434 1434  
1435 1435 #ifdef CONFIG_SYSCTL_SYSCALL
1436 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 1439 int __user *name, int nlen,
1439 1440 void __user *oldval, size_t __user *oldlenp,
1440 1441 void __user *newval, size_t newlen)
... ... @@ -1445,7 +1446,7 @@
1445 1446 op |= 004;
1446 1447 if (newval)
1447 1448 op |= 002;
1448   - if (sysctl_perm(table, op))
  1449 + if (sysctl_perm(root, table, op))
1449 1450 return -EPERM;
1450 1451  
1451 1452 if (table->strategy) {
... ... @@ -1471,6 +1472,7 @@
1471 1472 static int parse_table(int __user *name, int nlen,
1472 1473 void __user *oldval, size_t __user *oldlenp,
1473 1474 void __user *newval, size_t newlen,
  1475 + struct ctl_table_root *root,
1474 1476 struct ctl_table *table)
1475 1477 {
1476 1478 int n;
1477 1479  
... ... @@ -1485,14 +1487,14 @@
1485 1487 if (n == table->ctl_name) {
1486 1488 int error;
1487 1489 if (table->child) {
1488   - if (sysctl_perm(table, 001))
  1490 + if (sysctl_perm(root, table, 001))
1489 1491 return -EPERM;
1490 1492 name++;
1491 1493 nlen--;
1492 1494 table = table->child;
1493 1495 goto repeat;
1494 1496 }
1495   - error = do_sysctl_strategy(table, name, nlen,
  1497 + error = do_sysctl_strategy(root, table, name, nlen,
1496 1498 oldval, oldlenp,
1497 1499 newval, newlen);
1498 1500 return error;
... ... @@ -1518,7 +1520,8 @@
1518 1520 for (head = sysctl_head_next(NULL); head;
1519 1521 head = sysctl_head_next(head)) {
1520 1522 error = parse_table(name, nlen, oldval, oldlenp,
1521   - newval, newlen, head->ctl_table);
  1523 + newval, newlen,
  1524 + head->root, head->ctl_table);
1522 1525 if (error != -ENOTDIR) {
1523 1526 sysctl_head_finish(head);
1524 1527 break;
1525 1528  
1526 1529  
... ... @@ -1564,13 +1567,21 @@
1564 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 1572 int error;
  1573 + int mode;
  1574 +
1570 1575 error = security_sysctl(table, op);
1571 1576 if (error)
1572 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 1587 static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table)