diff options
author | Rafal Krypa <r.krypa@samsung.com> | 2015-06-02 05:23:48 -0400 |
---|---|---|
committer | Casey Schaufler <casey@schaufler-ca.com> | 2015-06-02 14:53:42 -0400 |
commit | c0d77c884461fc0dec0411e49797dc3f3651c31b (patch) | |
tree | c526c2ae841b0fc358d29af69cddcdb63ae72431 /security/smack | |
parent | 01fa8474fba7e80f6a2ac31d0790385a993cb7ba (diff) |
Smack: allow multiple labels in onlycap
Smack onlycap allows limiting of CAP_MAC_ADMIN and CAP_MAC_OVERRIDE to
processes running with the configured label. But having single privileged
label is not enough in some real use cases. On a complex system like Tizen,
there maybe few programs that need to configure Smack policy in run-time
and running them all with a single label is not always practical.
This patch extends onlycap feature for multiple labels. They are configured
in the same smackfs "onlycap" interface, separated by spaces.
Signed-off-by: Rafal Krypa <r.krypa@samsung.com>
Diffstat (limited to 'security/smack')
-rw-r--r-- | security/smack/smack.h | 25 | ||||
-rw-r--r-- | security/smack/smack_access.c | 41 | ||||
-rw-r--r-- | security/smack/smackfs.c | 163 |
3 files changed, 160 insertions, 69 deletions
diff --git a/security/smack/smack.h b/security/smack/smack.h index b8c1a869d85e..244e035e5a99 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h | |||
@@ -138,6 +138,11 @@ struct smk_port_label { | |||
138 | struct smack_known *smk_out; /* outgoing label */ | 138 | struct smack_known *smk_out; /* outgoing label */ |
139 | }; | 139 | }; |
140 | 140 | ||
141 | struct smack_onlycap { | ||
142 | struct list_head list; | ||
143 | struct smack_known *smk_label; | ||
144 | }; | ||
145 | |||
141 | /* | 146 | /* |
142 | * Mount options | 147 | * Mount options |
143 | */ | 148 | */ |
@@ -249,6 +254,7 @@ int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); | |||
249 | struct smack_known *smk_import_entry(const char *, int); | 254 | struct smack_known *smk_import_entry(const char *, int); |
250 | void smk_insert_entry(struct smack_known *skp); | 255 | void smk_insert_entry(struct smack_known *skp); |
251 | struct smack_known *smk_find_entry(const char *); | 256 | struct smack_known *smk_find_entry(const char *); |
257 | int smack_privileged(int cap); | ||
252 | 258 | ||
253 | /* | 259 | /* |
254 | * Shared data. | 260 | * Shared data. |
@@ -257,7 +263,6 @@ extern int smack_enabled; | |||
257 | extern int smack_cipso_direct; | 263 | extern int smack_cipso_direct; |
258 | extern int smack_cipso_mapped; | 264 | extern int smack_cipso_mapped; |
259 | extern struct smack_known *smack_net_ambient; | 265 | extern struct smack_known *smack_net_ambient; |
260 | extern struct smack_known *smack_onlycap; | ||
261 | extern struct smack_known *smack_syslog_label; | 266 | extern struct smack_known *smack_syslog_label; |
262 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP | 267 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP |
263 | extern struct smack_known *smack_unconfined; | 268 | extern struct smack_known *smack_unconfined; |
@@ -276,6 +281,9 @@ extern struct mutex smack_known_lock; | |||
276 | extern struct list_head smack_known_list; | 281 | extern struct list_head smack_known_list; |
277 | extern struct list_head smk_netlbladdr_list; | 282 | extern struct list_head smk_netlbladdr_list; |
278 | 283 | ||
284 | extern struct mutex smack_onlycap_lock; | ||
285 | extern struct list_head smack_onlycap_list; | ||
286 | |||
279 | #define SMACK_HASH_SLOTS 16 | 287 | #define SMACK_HASH_SLOTS 16 |
280 | extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; | 288 | extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; |
281 | 289 | ||
@@ -332,21 +340,6 @@ static inline struct smack_known *smk_of_current(void) | |||
332 | } | 340 | } |
333 | 341 | ||
334 | /* | 342 | /* |
335 | * Is the task privileged and allowed to be privileged | ||
336 | * by the onlycap rule. | ||
337 | */ | ||
338 | static inline int smack_privileged(int cap) | ||
339 | { | ||
340 | struct smack_known *skp = smk_of_current(); | ||
341 | |||
342 | if (!capable(cap)) | ||
343 | return 0; | ||
344 | if (smack_onlycap == NULL || smack_onlycap == skp) | ||
345 | return 1; | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * logging functions | 343 | * logging functions |
351 | */ | 344 | */ |
352 | #define SMACK_AUDIT_DENIED 0x1 | 345 | #define SMACK_AUDIT_DENIED 0x1 |
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 408e20be1ad7..00f6b38bffbd 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c | |||
@@ -617,3 +617,44 @@ struct smack_known *smack_from_secid(const u32 secid) | |||
617 | rcu_read_unlock(); | 617 | rcu_read_unlock(); |
618 | return &smack_known_invalid; | 618 | return &smack_known_invalid; |
619 | } | 619 | } |
620 | |||
621 | /* | ||
622 | * Unless a process is running with one of these labels | ||
623 | * even having CAP_MAC_OVERRIDE isn't enough to grant | ||
624 | * privilege to violate MAC policy. If no labels are | ||
625 | * designated (the empty list case) capabilities apply to | ||
626 | * everyone. | ||
627 | */ | ||
628 | LIST_HEAD(smack_onlycap_list); | ||
629 | DEFINE_MUTEX(smack_onlycap_lock); | ||
630 | |||
631 | /* | ||
632 | * Is the task privileged and allowed to be privileged | ||
633 | * by the onlycap rule. | ||
634 | * | ||
635 | * Returns 1 if the task is allowed to be privileged, 0 if it's not. | ||
636 | */ | ||
637 | int smack_privileged(int cap) | ||
638 | { | ||
639 | struct smack_known *skp = smk_of_current(); | ||
640 | struct smack_onlycap *sop; | ||
641 | |||
642 | if (!capable(cap)) | ||
643 | return 0; | ||
644 | |||
645 | rcu_read_lock(); | ||
646 | if (list_empty(&smack_onlycap_list)) { | ||
647 | rcu_read_unlock(); | ||
648 | return 1; | ||
649 | } | ||
650 | |||
651 | list_for_each_entry_rcu(sop, &smack_onlycap_list, list) { | ||
652 | if (sop->smk_label == skp) { | ||
653 | rcu_read_unlock(); | ||
654 | return 1; | ||
655 | } | ||
656 | } | ||
657 | rcu_read_unlock(); | ||
658 | |||
659 | return 0; | ||
660 | } | ||
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index e40dc48737ff..f1c22a891b1a 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
@@ -87,16 +87,6 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; | |||
87 | */ | 87 | */ |
88 | int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; | 88 | int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; |
89 | 89 | ||
90 | /* | ||
91 | * Unless a process is running with this label even | ||
92 | * having CAP_MAC_OVERRIDE isn't enough to grant | ||
93 | * privilege to violate MAC policy. If no label is | ||
94 | * designated (the NULL case) capabilities apply to | ||
95 | * everyone. It is expected that the hat (^) label | ||
96 | * will be used if any label is used. | ||
97 | */ | ||
98 | struct smack_known *smack_onlycap; | ||
99 | |||
100 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP | 90 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP |
101 | /* | 91 | /* |
102 | * Allow one label to be unconfined. This is for | 92 | * Allow one label to be unconfined. This is for |
@@ -1636,34 +1626,79 @@ static const struct file_operations smk_ambient_ops = { | |||
1636 | .llseek = default_llseek, | 1626 | .llseek = default_llseek, |
1637 | }; | 1627 | }; |
1638 | 1628 | ||
1639 | /** | 1629 | /* |
1640 | * smk_read_onlycap - read() for smackfs/onlycap | 1630 | * Seq_file operations for /smack/onlycap |
1641 | * @filp: file pointer, not actually used | ||
1642 | * @buf: where to put the result | ||
1643 | * @cn: maximum to send along | ||
1644 | * @ppos: where to start | ||
1645 | * | ||
1646 | * Returns number of bytes read or error code, as appropriate | ||
1647 | */ | 1631 | */ |
1648 | static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, | 1632 | static void *onlycap_seq_start(struct seq_file *s, loff_t *pos) |
1649 | size_t cn, loff_t *ppos) | ||
1650 | { | 1633 | { |
1651 | char *smack = ""; | 1634 | return smk_seq_start(s, pos, &smack_onlycap_list); |
1652 | ssize_t rc = -EINVAL; | 1635 | } |
1653 | int asize; | ||
1654 | 1636 | ||
1655 | if (*ppos != 0) | 1637 | static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos) |
1656 | return 0; | 1638 | { |
1639 | return smk_seq_next(s, v, pos, &smack_onlycap_list); | ||
1640 | } | ||
1657 | 1641 | ||
1658 | if (smack_onlycap != NULL) | 1642 | static int onlycap_seq_show(struct seq_file *s, void *v) |
1659 | smack = smack_onlycap->smk_known; | 1643 | { |
1644 | struct list_head *list = v; | ||
1645 | struct smack_onlycap *sop = | ||
1646 | list_entry_rcu(list, struct smack_onlycap, list); | ||
1660 | 1647 | ||
1661 | asize = strlen(smack) + 1; | 1648 | seq_puts(s, sop->smk_label->smk_known); |
1649 | seq_putc(s, ' '); | ||
1662 | 1650 | ||
1663 | if (cn >= asize) | 1651 | return 0; |
1664 | rc = simple_read_from_buffer(buf, cn, ppos, smack, asize); | 1652 | } |
1665 | 1653 | ||
1666 | return rc; | 1654 | static const struct seq_operations onlycap_seq_ops = { |
1655 | .start = onlycap_seq_start, | ||
1656 | .next = onlycap_seq_next, | ||
1657 | .show = onlycap_seq_show, | ||
1658 | .stop = smk_seq_stop, | ||
1659 | }; | ||
1660 | |||
1661 | static int smk_open_onlycap(struct inode *inode, struct file *file) | ||
1662 | { | ||
1663 | return seq_open(file, &onlycap_seq_ops); | ||
1664 | } | ||
1665 | |||
1666 | /** | ||
1667 | * smk_list_swap_rcu - swap public list with a private one in RCU-safe way | ||
1668 | * The caller must hold appropriate mutex to prevent concurrent modifications | ||
1669 | * to the public list. | ||
1670 | * Private list is assumed to be not accessible to other threads yet. | ||
1671 | * | ||
1672 | * @public: public list | ||
1673 | * @private: private list | ||
1674 | */ | ||
1675 | static void smk_list_swap_rcu(struct list_head *public, | ||
1676 | struct list_head *private) | ||
1677 | { | ||
1678 | struct list_head *first, *last; | ||
1679 | |||
1680 | if (list_empty(public)) { | ||
1681 | list_splice_init_rcu(private, public, synchronize_rcu); | ||
1682 | } else { | ||
1683 | /* Remember public list before replacing it */ | ||
1684 | first = public->next; | ||
1685 | last = public->prev; | ||
1686 | |||
1687 | /* Publish private list in place of public in RCU-safe way */ | ||
1688 | private->prev->next = public; | ||
1689 | private->next->prev = public; | ||
1690 | rcu_assign_pointer(public->next, private->next); | ||
1691 | public->prev = private->prev; | ||
1692 | |||
1693 | synchronize_rcu(); | ||
1694 | |||
1695 | /* When all readers are done with the old public list, | ||
1696 | * attach it in place of private */ | ||
1697 | private->next = first; | ||
1698 | private->prev = last; | ||
1699 | first->prev = private; | ||
1700 | last->next = private; | ||
1701 | } | ||
1667 | } | 1702 | } |
1668 | 1703 | ||
1669 | /** | 1704 | /** |
@@ -1679,28 +1714,47 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, | |||
1679 | size_t count, loff_t *ppos) | 1714 | size_t count, loff_t *ppos) |
1680 | { | 1715 | { |
1681 | char *data; | 1716 | char *data; |
1682 | struct smack_known *skp = smk_of_task(current->cred->security); | 1717 | char *data_parse; |
1718 | char *tok; | ||
1719 | struct smack_known *skp; | ||
1720 | struct smack_onlycap *sop; | ||
1721 | struct smack_onlycap *sop2; | ||
1722 | LIST_HEAD(list_tmp); | ||
1683 | int rc = count; | 1723 | int rc = count; |
1684 | 1724 | ||
1685 | if (!smack_privileged(CAP_MAC_ADMIN)) | 1725 | if (!smack_privileged(CAP_MAC_ADMIN)) |
1686 | return -EPERM; | 1726 | return -EPERM; |
1687 | 1727 | ||
1688 | /* | ||
1689 | * This can be done using smk_access() but is done | ||
1690 | * explicitly for clarity. The smk_access() implementation | ||
1691 | * would use smk_access(smack_onlycap, MAY_WRITE) | ||
1692 | */ | ||
1693 | if (smack_onlycap != NULL && smack_onlycap != skp) | ||
1694 | return -EPERM; | ||
1695 | |||
1696 | data = kzalloc(count + 1, GFP_KERNEL); | 1728 | data = kzalloc(count + 1, GFP_KERNEL); |
1697 | if (data == NULL) | 1729 | if (data == NULL) |
1698 | return -ENOMEM; | 1730 | return -ENOMEM; |
1699 | 1731 | ||
1700 | if (copy_from_user(data, buf, count) != 0) { | 1732 | if (copy_from_user(data, buf, count) != 0) { |
1701 | rc = -EFAULT; | 1733 | kfree(data); |
1702 | goto freeout; | 1734 | return -EFAULT; |
1735 | } | ||
1736 | |||
1737 | data_parse = data; | ||
1738 | while ((tok = strsep(&data_parse, " ")) != NULL) { | ||
1739 | if (!*tok) | ||
1740 | continue; | ||
1741 | |||
1742 | skp = smk_import_entry(tok, 0); | ||
1743 | if (IS_ERR(skp)) { | ||
1744 | rc = PTR_ERR(skp); | ||
1745 | break; | ||
1746 | } | ||
1747 | |||
1748 | sop = kzalloc(sizeof(*sop), GFP_KERNEL); | ||
1749 | if (sop == NULL) { | ||
1750 | rc = -ENOMEM; | ||
1751 | break; | ||
1752 | } | ||
1753 | |||
1754 | sop->smk_label = skp; | ||
1755 | list_add_rcu(&sop->list, &list_tmp); | ||
1703 | } | 1756 | } |
1757 | kfree(data); | ||
1704 | 1758 | ||
1705 | /* | 1759 | /* |
1706 | * Clear the smack_onlycap on invalid label errors. This means | 1760 | * Clear the smack_onlycap on invalid label errors. This means |
@@ -1710,26 +1764,29 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, | |||
1710 | * so "-usecapabilities" will also work. | 1764 | * so "-usecapabilities" will also work. |
1711 | * | 1765 | * |
1712 | * But do so only on invalid label, not on system errors. | 1766 | * But do so only on invalid label, not on system errors. |
1767 | * The invalid label must be first to count as clearing attempt. | ||
1713 | */ | 1768 | */ |
1714 | skp = smk_import_entry(data, count); | 1769 | if (rc == -EINVAL && list_empty(&list_tmp)) |
1715 | if (PTR_ERR(skp) == -EINVAL) | 1770 | rc = count; |
1716 | skp = NULL; | 1771 | |
1717 | else if (IS_ERR(skp)) { | 1772 | if (rc >= 0) { |
1718 | rc = PTR_ERR(skp); | 1773 | mutex_lock(&smack_onlycap_lock); |
1719 | goto freeout; | 1774 | smk_list_swap_rcu(&smack_onlycap_list, &list_tmp); |
1775 | mutex_unlock(&smack_onlycap_lock); | ||
1720 | } | 1776 | } |
1721 | 1777 | ||
1722 | smack_onlycap = skp; | 1778 | list_for_each_entry_safe(sop, sop2, &list_tmp, list) |
1779 | kfree(sop); | ||
1723 | 1780 | ||
1724 | freeout: | ||
1725 | kfree(data); | ||
1726 | return rc; | 1781 | return rc; |
1727 | } | 1782 | } |
1728 | 1783 | ||
1729 | static const struct file_operations smk_onlycap_ops = { | 1784 | static const struct file_operations smk_onlycap_ops = { |
1730 | .read = smk_read_onlycap, | 1785 | .open = smk_open_onlycap, |
1786 | .read = seq_read, | ||
1731 | .write = smk_write_onlycap, | 1787 | .write = smk_write_onlycap, |
1732 | .llseek = default_llseek, | 1788 | .llseek = seq_lseek, |
1789 | .release = seq_release, | ||
1733 | }; | 1790 | }; |
1734 | 1791 | ||
1735 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP | 1792 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP |