diff options
Diffstat (limited to 'kernel/auditfilter.c')
-rw-r--r-- | kernel/auditfilter.c | 64 |
1 files changed, 57 insertions, 7 deletions
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index df66a21fb360..5d96f2cc7be8 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -87,7 +87,7 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | |||
87 | #endif | 87 | #endif |
88 | }; | 88 | }; |
89 | 89 | ||
90 | static DEFINE_MUTEX(audit_filter_mutex); | 90 | DEFINE_MUTEX(audit_filter_mutex); |
91 | 91 | ||
92 | /* Inotify handle */ | 92 | /* Inotify handle */ |
93 | extern struct inotify_handle *audit_ih; | 93 | extern struct inotify_handle *audit_ih; |
@@ -145,7 +145,7 @@ static inline void audit_free_rule(struct audit_entry *e) | |||
145 | kfree(e); | 145 | kfree(e); |
146 | } | 146 | } |
147 | 147 | ||
148 | static inline void audit_free_rule_rcu(struct rcu_head *head) | 148 | void audit_free_rule_rcu(struct rcu_head *head) |
149 | { | 149 | { |
150 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | 150 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); |
151 | audit_free_rule(e); | 151 | audit_free_rule(e); |
@@ -217,7 +217,7 @@ static inline struct audit_entry *audit_init_entry(u32 field_count) | |||
217 | 217 | ||
218 | /* Unpack a filter field's string representation from user-space | 218 | /* Unpack a filter field's string representation from user-space |
219 | * buffer. */ | 219 | * buffer. */ |
220 | static char *audit_unpack_string(void **bufp, size_t *remain, size_t len) | 220 | char *audit_unpack_string(void **bufp, size_t *remain, size_t len) |
221 | { | 221 | { |
222 | char *str; | 222 | char *str; |
223 | 223 | ||
@@ -247,7 +247,7 @@ static inline int audit_to_inode(struct audit_krule *krule, | |||
247 | struct audit_field *f) | 247 | struct audit_field *f) |
248 | { | 248 | { |
249 | if (krule->listnr != AUDIT_FILTER_EXIT || | 249 | if (krule->listnr != AUDIT_FILTER_EXIT || |
250 | krule->watch || krule->inode_f) | 250 | krule->watch || krule->inode_f || krule->tree) |
251 | return -EINVAL; | 251 | return -EINVAL; |
252 | 252 | ||
253 | krule->inode_f = f; | 253 | krule->inode_f = f; |
@@ -266,7 +266,7 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len, | |||
266 | if (path[0] != '/' || path[len-1] == '/' || | 266 | if (path[0] != '/' || path[len-1] == '/' || |
267 | krule->listnr != AUDIT_FILTER_EXIT || | 267 | krule->listnr != AUDIT_FILTER_EXIT || |
268 | op & ~AUDIT_EQUAL || | 268 | op & ~AUDIT_EQUAL || |
269 | krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */ | 269 | krule->inode_f || krule->watch || krule->tree) |
270 | return -EINVAL; | 270 | return -EINVAL; |
271 | 271 | ||
272 | watch = audit_init_watch(path); | 272 | watch = audit_init_watch(path); |
@@ -622,6 +622,17 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, | |||
622 | goto exit_free; | 622 | goto exit_free; |
623 | } | 623 | } |
624 | break; | 624 | break; |
625 | case AUDIT_DIR: | ||
626 | str = audit_unpack_string(&bufp, &remain, f->val); | ||
627 | if (IS_ERR(str)) | ||
628 | goto exit_free; | ||
629 | entry->rule.buflen += f->val; | ||
630 | |||
631 | err = audit_make_tree(&entry->rule, str, f->op); | ||
632 | kfree(str); | ||
633 | if (err) | ||
634 | goto exit_free; | ||
635 | break; | ||
625 | case AUDIT_INODE: | 636 | case AUDIT_INODE: |
626 | err = audit_to_inode(&entry->rule, f); | 637 | err = audit_to_inode(&entry->rule, f); |
627 | if (err) | 638 | if (err) |
@@ -668,7 +679,7 @@ exit_free: | |||
668 | } | 679 | } |
669 | 680 | ||
670 | /* Pack a filter field's string representation into data block. */ | 681 | /* Pack a filter field's string representation into data block. */ |
671 | static inline size_t audit_pack_string(void **bufp, char *str) | 682 | static inline size_t audit_pack_string(void **bufp, const char *str) |
672 | { | 683 | { |
673 | size_t len = strlen(str); | 684 | size_t len = strlen(str); |
674 | 685 | ||
@@ -747,6 +758,11 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | |||
747 | data->buflen += data->values[i] = | 758 | data->buflen += data->values[i] = |
748 | audit_pack_string(&bufp, krule->watch->path); | 759 | audit_pack_string(&bufp, krule->watch->path); |
749 | break; | 760 | break; |
761 | case AUDIT_DIR: | ||
762 | data->buflen += data->values[i] = | ||
763 | audit_pack_string(&bufp, | ||
764 | audit_tree_path(krule->tree)); | ||
765 | break; | ||
750 | case AUDIT_FILTERKEY: | 766 | case AUDIT_FILTERKEY: |
751 | data->buflen += data->values[i] = | 767 | data->buflen += data->values[i] = |
752 | audit_pack_string(&bufp, krule->filterkey); | 768 | audit_pack_string(&bufp, krule->filterkey); |
@@ -795,6 +811,11 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | |||
795 | if (strcmp(a->watch->path, b->watch->path)) | 811 | if (strcmp(a->watch->path, b->watch->path)) |
796 | return 1; | 812 | return 1; |
797 | break; | 813 | break; |
814 | case AUDIT_DIR: | ||
815 | if (strcmp(audit_tree_path(a->tree), | ||
816 | audit_tree_path(b->tree))) | ||
817 | return 1; | ||
818 | break; | ||
798 | case AUDIT_FILTERKEY: | 819 | case AUDIT_FILTERKEY: |
799 | /* both filterkeys exist based on above type compare */ | 820 | /* both filterkeys exist based on above type compare */ |
800 | if (strcmp(a->filterkey, b->filterkey)) | 821 | if (strcmp(a->filterkey, b->filterkey)) |
@@ -897,6 +918,14 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, | |||
897 | new->inode_f = old->inode_f; | 918 | new->inode_f = old->inode_f; |
898 | new->watch = NULL; | 919 | new->watch = NULL; |
899 | new->field_count = old->field_count; | 920 | new->field_count = old->field_count; |
921 | /* | ||
922 | * note that we are OK with not refcounting here; audit_match_tree() | ||
923 | * never dereferences tree and we can't get false positives there | ||
924 | * since we'd have to have rule gone from the list *and* removed | ||
925 | * before the chunks found by lookup had been allocated, i.e. before | ||
926 | * the beginning of list scan. | ||
927 | */ | ||
928 | new->tree = old->tree; | ||
900 | memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount); | 929 | memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount); |
901 | 930 | ||
902 | /* deep copy this information, updating the se_rule fields, because | 931 | /* deep copy this information, updating the se_rule fields, because |
@@ -1217,6 +1246,7 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
1217 | struct audit_entry *e; | 1246 | struct audit_entry *e; |
1218 | struct audit_field *inode_f = entry->rule.inode_f; | 1247 | struct audit_field *inode_f = entry->rule.inode_f; |
1219 | struct audit_watch *watch = entry->rule.watch; | 1248 | struct audit_watch *watch = entry->rule.watch; |
1249 | struct audit_tree *tree = entry->rule.tree; | ||
1220 | struct nameidata *ndp = NULL, *ndw = NULL; | 1250 | struct nameidata *ndp = NULL, *ndw = NULL; |
1221 | int h, err; | 1251 | int h, err; |
1222 | #ifdef CONFIG_AUDITSYSCALL | 1252 | #ifdef CONFIG_AUDITSYSCALL |
@@ -1238,6 +1268,9 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
1238 | mutex_unlock(&audit_filter_mutex); | 1268 | mutex_unlock(&audit_filter_mutex); |
1239 | if (e) { | 1269 | if (e) { |
1240 | err = -EEXIST; | 1270 | err = -EEXIST; |
1271 | /* normally audit_add_tree_rule() will free it on failure */ | ||
1272 | if (tree) | ||
1273 | audit_put_tree(tree); | ||
1241 | goto error; | 1274 | goto error; |
1242 | } | 1275 | } |
1243 | 1276 | ||
@@ -1259,6 +1292,13 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
1259 | h = audit_hash_ino((u32)watch->ino); | 1292 | h = audit_hash_ino((u32)watch->ino); |
1260 | list = &audit_inode_hash[h]; | 1293 | list = &audit_inode_hash[h]; |
1261 | } | 1294 | } |
1295 | if (tree) { | ||
1296 | err = audit_add_tree_rule(&entry->rule); | ||
1297 | if (err) { | ||
1298 | mutex_unlock(&audit_filter_mutex); | ||
1299 | goto error; | ||
1300 | } | ||
1301 | } | ||
1262 | 1302 | ||
1263 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { | 1303 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
1264 | list_add_rcu(&entry->list, list); | 1304 | list_add_rcu(&entry->list, list); |
@@ -1292,6 +1332,7 @@ static inline int audit_del_rule(struct audit_entry *entry, | |||
1292 | struct audit_entry *e; | 1332 | struct audit_entry *e; |
1293 | struct audit_field *inode_f = entry->rule.inode_f; | 1333 | struct audit_field *inode_f = entry->rule.inode_f; |
1294 | struct audit_watch *watch, *tmp_watch = entry->rule.watch; | 1334 | struct audit_watch *watch, *tmp_watch = entry->rule.watch; |
1335 | struct audit_tree *tree = entry->rule.tree; | ||
1295 | LIST_HEAD(inotify_list); | 1336 | LIST_HEAD(inotify_list); |
1296 | int h, ret = 0; | 1337 | int h, ret = 0; |
1297 | #ifdef CONFIG_AUDITSYSCALL | 1338 | #ifdef CONFIG_AUDITSYSCALL |
@@ -1336,6 +1377,9 @@ static inline int audit_del_rule(struct audit_entry *entry, | |||
1336 | } | 1377 | } |
1337 | } | 1378 | } |
1338 | 1379 | ||
1380 | if (e->rule.tree) | ||
1381 | audit_remove_tree_rule(&e->rule); | ||
1382 | |||
1339 | list_del_rcu(&e->list); | 1383 | list_del_rcu(&e->list); |
1340 | call_rcu(&e->rcu, audit_free_rule_rcu); | 1384 | call_rcu(&e->rcu, audit_free_rule_rcu); |
1341 | 1385 | ||
@@ -1354,6 +1398,8 @@ static inline int audit_del_rule(struct audit_entry *entry, | |||
1354 | out: | 1398 | out: |
1355 | if (tmp_watch) | 1399 | if (tmp_watch) |
1356 | audit_put_watch(tmp_watch); /* match initial get */ | 1400 | audit_put_watch(tmp_watch); /* match initial get */ |
1401 | if (tree) | ||
1402 | audit_put_tree(tree); /* that's the temporary one */ | ||
1357 | 1403 | ||
1358 | return ret; | 1404 | return ret; |
1359 | } | 1405 | } |
@@ -1737,6 +1783,7 @@ int selinux_audit_rule_update(void) | |||
1737 | { | 1783 | { |
1738 | struct audit_entry *entry, *n, *nentry; | 1784 | struct audit_entry *entry, *n, *nentry; |
1739 | struct audit_watch *watch; | 1785 | struct audit_watch *watch; |
1786 | struct audit_tree *tree; | ||
1740 | int i, err = 0; | 1787 | int i, err = 0; |
1741 | 1788 | ||
1742 | /* audit_filter_mutex synchronizes the writers */ | 1789 | /* audit_filter_mutex synchronizes the writers */ |
@@ -1748,6 +1795,7 @@ int selinux_audit_rule_update(void) | |||
1748 | continue; | 1795 | continue; |
1749 | 1796 | ||
1750 | watch = entry->rule.watch; | 1797 | watch = entry->rule.watch; |
1798 | tree = entry->rule.tree; | ||
1751 | nentry = audit_dupe_rule(&entry->rule, watch); | 1799 | nentry = audit_dupe_rule(&entry->rule, watch); |
1752 | if (unlikely(IS_ERR(nentry))) { | 1800 | if (unlikely(IS_ERR(nentry))) { |
1753 | /* save the first error encountered for the | 1801 | /* save the first error encountered for the |
@@ -1763,7 +1811,9 @@ int selinux_audit_rule_update(void) | |||
1763 | list_add(&nentry->rule.rlist, | 1811 | list_add(&nentry->rule.rlist, |
1764 | &watch->rules); | 1812 | &watch->rules); |
1765 | list_del(&entry->rule.rlist); | 1813 | list_del(&entry->rule.rlist); |
1766 | } | 1814 | } else if (tree) |
1815 | list_replace_init(&entry->rule.rlist, | ||
1816 | &nentry->rule.rlist); | ||
1767 | list_replace_rcu(&entry->list, &nentry->list); | 1817 | list_replace_rcu(&entry->list, &nentry->list); |
1768 | } | 1818 | } |
1769 | call_rcu(&entry->rcu, audit_free_rule_rcu); | 1819 | call_rcu(&entry->rcu, audit_free_rule_rcu); |