diff options
Diffstat (limited to 'kernel/sysctl.c')
| -rw-r--r-- | kernel/sysctl.c | 176 |
1 files changed, 97 insertions, 79 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index fd3364827ccf..d7ffdc59816a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | #include <linux/writeback.h> | 38 | #include <linux/writeback.h> |
| 39 | #include <linux/hugetlb.h> | 39 | #include <linux/hugetlb.h> |
| 40 | #include <linux/initrd.h> | 40 | #include <linux/initrd.h> |
| 41 | #include <linux/key.h> | ||
| 41 | #include <linux/times.h> | 42 | #include <linux/times.h> |
| 42 | #include <linux/limits.h> | 43 | #include <linux/limits.h> |
| 43 | #include <linux/dcache.h> | 44 | #include <linux/dcache.h> |
| @@ -144,12 +145,6 @@ extern int no_unaligned_warning; | |||
| 144 | extern int max_lock_depth; | 145 | extern int max_lock_depth; |
| 145 | #endif | 146 | #endif |
| 146 | 147 | ||
| 147 | #ifdef CONFIG_SYSCTL_SYSCALL | ||
| 148 | static int parse_table(int __user *, int, void __user *, size_t __user *, | ||
| 149 | void __user *, size_t, struct ctl_table *); | ||
| 150 | #endif | ||
| 151 | |||
| 152 | |||
| 153 | #ifdef CONFIG_PROC_SYSCTL | 148 | #ifdef CONFIG_PROC_SYSCTL |
| 154 | static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp, | 149 | static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp, |
| 155 | void __user *buffer, size_t *lenp, loff_t *ppos); | 150 | void __user *buffer, size_t *lenp, loff_t *ppos); |
| @@ -809,6 +804,14 @@ static struct ctl_table kern_table[] = { | |||
| 809 | .proc_handler = &proc_dostring, | 804 | .proc_handler = &proc_dostring, |
| 810 | .strategy = &sysctl_string, | 805 | .strategy = &sysctl_string, |
| 811 | }, | 806 | }, |
| 807 | #ifdef CONFIG_KEYS | ||
| 808 | { | ||
| 809 | .ctl_name = CTL_UNNUMBERED, | ||
| 810 | .procname = "keys", | ||
| 811 | .mode = 0555, | ||
| 812 | .child = key_sysctls, | ||
| 813 | }, | ||
| 814 | #endif | ||
| 812 | /* | 815 | /* |
| 813 | * NOTE: do not add new entries to this table unless you have read | 816 | * NOTE: do not add new entries to this table unless you have read |
| 814 | * Documentation/sysctl/ctl_unnumbered.txt | 817 | * Documentation/sysctl/ctl_unnumbered.txt |
| @@ -1430,6 +1433,76 @@ void register_sysctl_root(struct ctl_table_root *root) | |||
| 1430 | } | 1433 | } |
| 1431 | 1434 | ||
| 1432 | #ifdef CONFIG_SYSCTL_SYSCALL | 1435 | #ifdef CONFIG_SYSCTL_SYSCALL |
| 1436 | /* Perform the actual read/write of a sysctl table entry. */ | ||
| 1437 | static int do_sysctl_strategy(struct ctl_table_root *root, | ||
| 1438 | struct ctl_table *table, | ||
| 1439 | int __user *name, int nlen, | ||
| 1440 | void __user *oldval, size_t __user *oldlenp, | ||
| 1441 | void __user *newval, size_t newlen) | ||
| 1442 | { | ||
| 1443 | int op = 0, rc; | ||
| 1444 | |||
| 1445 | if (oldval) | ||
| 1446 | op |= 004; | ||
| 1447 | if (newval) | ||
| 1448 | op |= 002; | ||
| 1449 | if (sysctl_perm(root, table, op)) | ||
| 1450 | return -EPERM; | ||
| 1451 | |||
| 1452 | if (table->strategy) { | ||
| 1453 | rc = table->strategy(table, name, nlen, oldval, oldlenp, | ||
| 1454 | newval, newlen); | ||
| 1455 | if (rc < 0) | ||
| 1456 | return rc; | ||
| 1457 | if (rc > 0) | ||
| 1458 | return 0; | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | /* If there is no strategy routine, or if the strategy returns | ||
| 1462 | * zero, proceed with automatic r/w */ | ||
| 1463 | if (table->data && table->maxlen) { | ||
| 1464 | rc = sysctl_data(table, name, nlen, oldval, oldlenp, | ||
| 1465 | newval, newlen); | ||
| 1466 | if (rc < 0) | ||
| 1467 | return rc; | ||
| 1468 | } | ||
| 1469 | return 0; | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | static int parse_table(int __user *name, int nlen, | ||
| 1473 | void __user *oldval, size_t __user *oldlenp, | ||
| 1474 | void __user *newval, size_t newlen, | ||
| 1475 | struct ctl_table_root *root, | ||
| 1476 | struct ctl_table *table) | ||
| 1477 | { | ||
| 1478 | int n; | ||
| 1479 | repeat: | ||
| 1480 | if (!nlen) | ||
| 1481 | return -ENOTDIR; | ||
| 1482 | if (get_user(n, name)) | ||
| 1483 | return -EFAULT; | ||
| 1484 | for ( ; table->ctl_name || table->procname; table++) { | ||
| 1485 | if (!table->ctl_name) | ||
| 1486 | continue; | ||
| 1487 | if (n == table->ctl_name) { | ||
| 1488 | int error; | ||
| 1489 | if (table->child) { | ||
| 1490 | if (sysctl_perm(root, table, 001)) | ||
| 1491 | return -EPERM; | ||
| 1492 | name++; | ||
| 1493 | nlen--; | ||
| 1494 | table = table->child; | ||
| 1495 | goto repeat; | ||
| 1496 | } | ||
| 1497 | error = do_sysctl_strategy(root, table, name, nlen, | ||
| 1498 | oldval, oldlenp, | ||
| 1499 | newval, newlen); | ||
| 1500 | return error; | ||
| 1501 | } | ||
| 1502 | } | ||
| 1503 | return -ENOTDIR; | ||
| 1504 | } | ||
| 1505 | |||
| 1433 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, | 1506 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, |
| 1434 | void __user *newval, size_t newlen) | 1507 | void __user *newval, size_t newlen) |
| 1435 | { | 1508 | { |
| @@ -1447,7 +1520,8 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol | |||
| 1447 | for (head = sysctl_head_next(NULL); head; | 1520 | for (head = sysctl_head_next(NULL); head; |
| 1448 | head = sysctl_head_next(head)) { | 1521 | head = sysctl_head_next(head)) { |
| 1449 | error = parse_table(name, nlen, oldval, oldlenp, | 1522 | error = parse_table(name, nlen, oldval, oldlenp, |
| 1450 | newval, newlen, head->ctl_table); | 1523 | newval, newlen, |
| 1524 | head->root, head->ctl_table); | ||
| 1451 | if (error != -ENOTDIR) { | 1525 | if (error != -ENOTDIR) { |
| 1452 | sysctl_head_finish(head); | 1526 | sysctl_head_finish(head); |
| 1453 | break; | 1527 | break; |
| @@ -1493,84 +1567,22 @@ static int test_perm(int mode, int op) | |||
| 1493 | return -EACCES; | 1567 | return -EACCES; |
| 1494 | } | 1568 | } |
| 1495 | 1569 | ||
| 1496 | int sysctl_perm(struct ctl_table *table, int op) | 1570 | int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) |
| 1497 | { | 1571 | { |
| 1498 | int error; | 1572 | int error; |
| 1573 | int mode; | ||
| 1574 | |||
| 1499 | error = security_sysctl(table, op); | 1575 | error = security_sysctl(table, op); |
| 1500 | if (error) | 1576 | if (error) |
| 1501 | return error; | 1577 | return error; |
| 1502 | return test_perm(table->mode, op); | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | #ifdef CONFIG_SYSCTL_SYSCALL | ||
| 1506 | static int parse_table(int __user *name, int nlen, | ||
| 1507 | void __user *oldval, size_t __user *oldlenp, | ||
| 1508 | void __user *newval, size_t newlen, | ||
| 1509 | struct ctl_table *table) | ||
| 1510 | { | ||
| 1511 | int n; | ||
| 1512 | repeat: | ||
| 1513 | if (!nlen) | ||
| 1514 | return -ENOTDIR; | ||
| 1515 | if (get_user(n, name)) | ||
| 1516 | return -EFAULT; | ||
| 1517 | for ( ; table->ctl_name || table->procname; table++) { | ||
| 1518 | if (!table->ctl_name) | ||
| 1519 | continue; | ||
| 1520 | if (n == table->ctl_name) { | ||
| 1521 | int error; | ||
| 1522 | if (table->child) { | ||
| 1523 | if (sysctl_perm(table, 001)) | ||
| 1524 | return -EPERM; | ||
| 1525 | name++; | ||
| 1526 | nlen--; | ||
| 1527 | table = table->child; | ||
| 1528 | goto repeat; | ||
| 1529 | } | ||
| 1530 | error = do_sysctl_strategy(table, name, nlen, | ||
| 1531 | oldval, oldlenp, | ||
| 1532 | newval, newlen); | ||
| 1533 | return error; | ||
| 1534 | } | ||
| 1535 | } | ||
| 1536 | return -ENOTDIR; | ||
| 1537 | } | ||
| 1538 | 1578 | ||
| 1539 | /* Perform the actual read/write of a sysctl table entry. */ | 1579 | if (root->permissions) |
| 1540 | int do_sysctl_strategy (struct ctl_table *table, | 1580 | mode = root->permissions(root, current->nsproxy, table); |
| 1541 | int __user *name, int nlen, | 1581 | else |
| 1542 | void __user *oldval, size_t __user *oldlenp, | 1582 | mode = table->mode; |
| 1543 | void __user *newval, size_t newlen) | ||
| 1544 | { | ||
| 1545 | int op = 0, rc; | ||
| 1546 | |||
| 1547 | if (oldval) | ||
| 1548 | op |= 004; | ||
| 1549 | if (newval) | ||
| 1550 | op |= 002; | ||
| 1551 | if (sysctl_perm(table, op)) | ||
| 1552 | return -EPERM; | ||
| 1553 | 1583 | ||
| 1554 | if (table->strategy) { | 1584 | return test_perm(mode, op); |
| 1555 | rc = table->strategy(table, name, nlen, oldval, oldlenp, | ||
| 1556 | newval, newlen); | ||
| 1557 | if (rc < 0) | ||
| 1558 | return rc; | ||
| 1559 | if (rc > 0) | ||
| 1560 | return 0; | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | /* If there is no strategy routine, or if the strategy returns | ||
| 1564 | * zero, proceed with automatic r/w */ | ||
| 1565 | if (table->data && table->maxlen) { | ||
| 1566 | rc = sysctl_data(table, name, nlen, oldval, oldlenp, | ||
| 1567 | newval, newlen); | ||
| 1568 | if (rc < 0) | ||
| 1569 | return rc; | ||
| 1570 | } | ||
| 1571 | return 0; | ||
| 1572 | } | 1585 | } |
| 1573 | #endif /* CONFIG_SYSCTL_SYSCALL */ | ||
| 1574 | 1586 | ||
| 1575 | 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) |
| 1576 | { | 1588 | { |
| @@ -1583,9 +1595,13 @@ static void sysctl_set_parent(struct ctl_table *parent, struct ctl_table *table) | |||
| 1583 | 1595 | ||
| 1584 | static __init int sysctl_init(void) | 1596 | static __init int sysctl_init(void) |
| 1585 | { | 1597 | { |
| 1586 | int err; | ||
| 1587 | sysctl_set_parent(NULL, root_table); | 1598 | sysctl_set_parent(NULL, root_table); |
| 1588 | err = sysctl_check_table(current->nsproxy, root_table); | 1599 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK |
| 1600 | { | ||
| 1601 | int err; | ||
| 1602 | err = sysctl_check_table(current->nsproxy, root_table); | ||
| 1603 | } | ||
| 1604 | #endif | ||
| 1589 | return 0; | 1605 | return 0; |
| 1590 | } | 1606 | } |
| 1591 | 1607 | ||
| @@ -1712,10 +1728,12 @@ struct ctl_table_header *__register_sysctl_paths( | |||
| 1712 | header->unregistering = NULL; | 1728 | header->unregistering = NULL; |
| 1713 | header->root = root; | 1729 | header->root = root; |
| 1714 | sysctl_set_parent(NULL, header->ctl_table); | 1730 | sysctl_set_parent(NULL, header->ctl_table); |
| 1731 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK | ||
| 1715 | if (sysctl_check_table(namespaces, header->ctl_table)) { | 1732 | if (sysctl_check_table(namespaces, header->ctl_table)) { |
| 1716 | kfree(header); | 1733 | kfree(header); |
| 1717 | return NULL; | 1734 | return NULL; |
| 1718 | } | 1735 | } |
| 1736 | #endif | ||
| 1719 | spin_lock(&sysctl_lock); | 1737 | spin_lock(&sysctl_lock); |
| 1720 | header_list = lookup_header_list(root, namespaces); | 1738 | header_list = lookup_header_list(root, namespaces); |
| 1721 | list_add_tail(&header->ctl_entry, header_list); | 1739 | list_add_tail(&header->ctl_entry, header_list); |
