diff options
Diffstat (limited to 'security/smack/smackfs.c')
-rw-r--r-- | security/smack/smackfs.c | 180 |
1 files changed, 101 insertions, 79 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index a1b57e4dba3e..856c8a287523 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
@@ -80,10 +80,11 @@ char *smack_onlycap; | |||
80 | * Packets are sent there unlabeled, but only from tasks that | 80 | * Packets are sent there unlabeled, but only from tasks that |
81 | * can write to the specified label. | 81 | * can write to the specified label. |
82 | */ | 82 | */ |
83 | struct smk_netlbladdr *smack_netlbladdrs; | 83 | |
84 | LIST_HEAD(smk_netlbladdr_list); | ||
85 | LIST_HEAD(smack_rule_list); | ||
84 | 86 | ||
85 | static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; | 87 | static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; |
86 | struct smk_list_entry *smack_list; | ||
87 | 88 | ||
88 | #define SEQ_READ_FINISHED 1 | 89 | #define SEQ_READ_FINISHED 1 |
89 | 90 | ||
@@ -134,24 +135,27 @@ static void *load_seq_start(struct seq_file *s, loff_t *pos) | |||
134 | { | 135 | { |
135 | if (*pos == SEQ_READ_FINISHED) | 136 | if (*pos == SEQ_READ_FINISHED) |
136 | return NULL; | 137 | return NULL; |
137 | 138 | if (list_empty(&smack_rule_list)) | |
138 | return smack_list; | 139 | return NULL; |
140 | return smack_rule_list.next; | ||
139 | } | 141 | } |
140 | 142 | ||
141 | static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) | 143 | static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) |
142 | { | 144 | { |
143 | struct smk_list_entry *skp = ((struct smk_list_entry *) v)->smk_next; | 145 | struct list_head *list = v; |
144 | 146 | ||
145 | if (skp == NULL) | 147 | if (list_is_last(list, &smack_rule_list)) { |
146 | *pos = SEQ_READ_FINISHED; | 148 | *pos = SEQ_READ_FINISHED; |
147 | 149 | return NULL; | |
148 | return skp; | 150 | } |
151 | return list->next; | ||
149 | } | 152 | } |
150 | 153 | ||
151 | static int load_seq_show(struct seq_file *s, void *v) | 154 | static int load_seq_show(struct seq_file *s, void *v) |
152 | { | 155 | { |
153 | struct smk_list_entry *slp = (struct smk_list_entry *) v; | 156 | struct list_head *list = v; |
154 | struct smack_rule *srp = &slp->smk_rule; | 157 | struct smack_rule *srp = |
158 | list_entry(list, struct smack_rule, list); | ||
155 | 159 | ||
156 | seq_printf(s, "%s %s", (char *)srp->smk_subject, | 160 | seq_printf(s, "%s %s", (char *)srp->smk_subject, |
157 | (char *)srp->smk_object); | 161 | (char *)srp->smk_object); |
@@ -212,32 +216,23 @@ static int smk_open_load(struct inode *inode, struct file *file) | |||
212 | */ | 216 | */ |
213 | static int smk_set_access(struct smack_rule *srp) | 217 | static int smk_set_access(struct smack_rule *srp) |
214 | { | 218 | { |
215 | struct smk_list_entry *sp; | 219 | struct smack_rule *sp; |
216 | struct smk_list_entry *newp; | ||
217 | int ret = 0; | 220 | int ret = 0; |
218 | 221 | int found; | |
219 | mutex_lock(&smack_list_lock); | 222 | mutex_lock(&smack_list_lock); |
220 | 223 | ||
221 | for (sp = smack_list; sp != NULL; sp = sp->smk_next) | 224 | found = 0; |
222 | if (sp->smk_rule.smk_subject == srp->smk_subject && | 225 | list_for_each_entry_rcu(sp, &smack_rule_list, list) { |
223 | sp->smk_rule.smk_object == srp->smk_object) { | 226 | if (sp->smk_subject == srp->smk_subject && |
224 | sp->smk_rule.smk_access = srp->smk_access; | 227 | sp->smk_object == srp->smk_object) { |
228 | found = 1; | ||
229 | sp->smk_access = srp->smk_access; | ||
225 | break; | 230 | break; |
226 | } | 231 | } |
227 | |||
228 | if (sp == NULL) { | ||
229 | newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL); | ||
230 | if (newp == NULL) { | ||
231 | ret = -ENOMEM; | ||
232 | goto out; | ||
233 | } | ||
234 | |||
235 | newp->smk_rule = *srp; | ||
236 | newp->smk_next = smack_list; | ||
237 | smack_list = newp; | ||
238 | } | 232 | } |
233 | if (found == 0) | ||
234 | list_add_rcu(&srp->list, &smack_rule_list); | ||
239 | 235 | ||
240 | out: | ||
241 | mutex_unlock(&smack_list_lock); | 236 | mutex_unlock(&smack_list_lock); |
242 | 237 | ||
243 | return ret; | 238 | return ret; |
@@ -261,7 +256,7 @@ out: | |||
261 | static ssize_t smk_write_load(struct file *file, const char __user *buf, | 256 | static ssize_t smk_write_load(struct file *file, const char __user *buf, |
262 | size_t count, loff_t *ppos) | 257 | size_t count, loff_t *ppos) |
263 | { | 258 | { |
264 | struct smack_rule rule; | 259 | struct smack_rule *rule; |
265 | char *data; | 260 | char *data; |
266 | int rc = -EINVAL; | 261 | int rc = -EINVAL; |
267 | 262 | ||
@@ -272,9 +267,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
272 | */ | 267 | */ |
273 | if (!capable(CAP_MAC_ADMIN)) | 268 | if (!capable(CAP_MAC_ADMIN)) |
274 | return -EPERM; | 269 | return -EPERM; |
275 | if (*ppos != 0) | 270 | |
276 | return -EINVAL; | 271 | if (*ppos != 0 || count != SMK_LOADLEN) |
277 | if (count != SMK_LOADLEN) | ||
278 | return -EINVAL; | 272 | return -EINVAL; |
279 | 273 | ||
280 | data = kzalloc(count, GFP_KERNEL); | 274 | data = kzalloc(count, GFP_KERNEL); |
@@ -286,25 +280,31 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
286 | goto out; | 280 | goto out; |
287 | } | 281 | } |
288 | 282 | ||
289 | rule.smk_subject = smk_import(data, 0); | 283 | rule = kzalloc(sizeof(*rule), GFP_KERNEL); |
290 | if (rule.smk_subject == NULL) | 284 | if (rule == NULL) { |
285 | rc = -ENOMEM; | ||
291 | goto out; | 286 | goto out; |
287 | } | ||
292 | 288 | ||
293 | rule.smk_object = smk_import(data + SMK_LABELLEN, 0); | 289 | rule->smk_subject = smk_import(data, 0); |
294 | if (rule.smk_object == NULL) | 290 | if (rule->smk_subject == NULL) |
295 | goto out; | 291 | goto out_free_rule; |
296 | 292 | ||
297 | rule.smk_access = 0; | 293 | rule->smk_object = smk_import(data + SMK_LABELLEN, 0); |
294 | if (rule->smk_object == NULL) | ||
295 | goto out_free_rule; | ||
296 | |||
297 | rule->smk_access = 0; | ||
298 | 298 | ||
299 | switch (data[SMK_LABELLEN + SMK_LABELLEN]) { | 299 | switch (data[SMK_LABELLEN + SMK_LABELLEN]) { |
300 | case '-': | 300 | case '-': |
301 | break; | 301 | break; |
302 | case 'r': | 302 | case 'r': |
303 | case 'R': | 303 | case 'R': |
304 | rule.smk_access |= MAY_READ; | 304 | rule->smk_access |= MAY_READ; |
305 | break; | 305 | break; |
306 | default: | 306 | default: |
307 | goto out; | 307 | goto out_free_rule; |
308 | } | 308 | } |
309 | 309 | ||
310 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { | 310 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { |
@@ -312,10 +312,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
312 | break; | 312 | break; |
313 | case 'w': | 313 | case 'w': |
314 | case 'W': | 314 | case 'W': |
315 | rule.smk_access |= MAY_WRITE; | 315 | rule->smk_access |= MAY_WRITE; |
316 | break; | 316 | break; |
317 | default: | 317 | default: |
318 | goto out; | 318 | goto out_free_rule; |
319 | } | 319 | } |
320 | 320 | ||
321 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { | 321 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { |
@@ -323,10 +323,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
323 | break; | 323 | break; |
324 | case 'x': | 324 | case 'x': |
325 | case 'X': | 325 | case 'X': |
326 | rule.smk_access |= MAY_EXEC; | 326 | rule->smk_access |= MAY_EXEC; |
327 | break; | 327 | break; |
328 | default: | 328 | default: |
329 | goto out; | 329 | goto out_free_rule; |
330 | } | 330 | } |
331 | 331 | ||
332 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { | 332 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { |
@@ -334,17 +334,20 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
334 | break; | 334 | break; |
335 | case 'a': | 335 | case 'a': |
336 | case 'A': | 336 | case 'A': |
337 | rule.smk_access |= MAY_APPEND; | 337 | rule->smk_access |= MAY_APPEND; |
338 | break; | 338 | break; |
339 | default: | 339 | default: |
340 | goto out; | 340 | goto out_free_rule; |
341 | } | 341 | } |
342 | 342 | ||
343 | rc = smk_set_access(&rule); | 343 | rc = smk_set_access(rule); |
344 | 344 | ||
345 | if (!rc) | 345 | if (!rc) |
346 | rc = count; | 346 | rc = count; |
347 | goto out; | ||
347 | 348 | ||
349 | out_free_rule: | ||
350 | kfree(rule); | ||
348 | out: | 351 | out: |
349 | kfree(data); | 352 | kfree(data); |
350 | return rc; | 353 | return rc; |
@@ -433,24 +436,26 @@ static void *cipso_seq_start(struct seq_file *s, loff_t *pos) | |||
433 | { | 436 | { |
434 | if (*pos == SEQ_READ_FINISHED) | 437 | if (*pos == SEQ_READ_FINISHED) |
435 | return NULL; | 438 | return NULL; |
439 | if (list_empty(&smack_known_list)) | ||
440 | return NULL; | ||
436 | 441 | ||
437 | return smack_known; | 442 | return smack_known_list.next; |
438 | } | 443 | } |
439 | 444 | ||
440 | static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) | 445 | static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) |
441 | { | 446 | { |
442 | struct smack_known *skp = ((struct smack_known *) v)->smk_next; | 447 | struct list_head *list = v; |
443 | 448 | ||
444 | /* | 449 | /* |
445 | * Omit labels with no associated cipso value | 450 | * labels with no associated cipso value wont be printed |
451 | * in cipso_seq_show | ||
446 | */ | 452 | */ |
447 | while (skp != NULL && !skp->smk_cipso) | 453 | if (list_is_last(list, &smack_known_list)) { |
448 | skp = skp->smk_next; | ||
449 | |||
450 | if (skp == NULL) | ||
451 | *pos = SEQ_READ_FINISHED; | 454 | *pos = SEQ_READ_FINISHED; |
455 | return NULL; | ||
456 | } | ||
452 | 457 | ||
453 | return skp; | 458 | return list->next; |
454 | } | 459 | } |
455 | 460 | ||
456 | /* | 461 | /* |
@@ -459,7 +464,9 @@ static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) | |||
459 | */ | 464 | */ |
460 | static int cipso_seq_show(struct seq_file *s, void *v) | 465 | static int cipso_seq_show(struct seq_file *s, void *v) |
461 | { | 466 | { |
462 | struct smack_known *skp = (struct smack_known *) v; | 467 | struct list_head *list = v; |
468 | struct smack_known *skp = | ||
469 | list_entry(list, struct smack_known, list); | ||
463 | struct smack_cipso *scp = skp->smk_cipso; | 470 | struct smack_cipso *scp = skp->smk_cipso; |
464 | char *cbp; | 471 | char *cbp; |
465 | char sep = '/'; | 472 | char sep = '/'; |
@@ -638,18 +645,21 @@ static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) | |||
638 | { | 645 | { |
639 | if (*pos == SEQ_READ_FINISHED) | 646 | if (*pos == SEQ_READ_FINISHED) |
640 | return NULL; | 647 | return NULL; |
641 | 648 | if (list_empty(&smk_netlbladdr_list)) | |
642 | return smack_netlbladdrs; | 649 | return NULL; |
650 | return smk_netlbladdr_list.next; | ||
643 | } | 651 | } |
644 | 652 | ||
645 | static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) | 653 | static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) |
646 | { | 654 | { |
647 | struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next; | 655 | struct list_head *list = v; |
648 | 656 | ||
649 | if (skp == NULL) | 657 | if (list_is_last(list, &smk_netlbladdr_list)) { |
650 | *pos = SEQ_READ_FINISHED; | 658 | *pos = SEQ_READ_FINISHED; |
659 | return NULL; | ||
660 | } | ||
651 | 661 | ||
652 | return skp; | 662 | return list->next; |
653 | } | 663 | } |
654 | #define BEBITS (sizeof(__be32) * 8) | 664 | #define BEBITS (sizeof(__be32) * 8) |
655 | 665 | ||
@@ -658,7 +668,9 @@ static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) | |||
658 | */ | 668 | */ |
659 | static int netlbladdr_seq_show(struct seq_file *s, void *v) | 669 | static int netlbladdr_seq_show(struct seq_file *s, void *v) |
660 | { | 670 | { |
661 | struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v; | 671 | struct list_head *list = v; |
672 | struct smk_netlbladdr *skp = | ||
673 | list_entry(list, struct smk_netlbladdr, list); | ||
662 | unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; | 674 | unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; |
663 | int maskn; | 675 | int maskn; |
664 | u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr); | 676 | u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr); |
@@ -702,30 +714,36 @@ static int smk_open_netlbladdr(struct inode *inode, struct file *file) | |||
702 | * | 714 | * |
703 | * This helper insert netlabel in the smack_netlbladdrs list | 715 | * This helper insert netlabel in the smack_netlbladdrs list |
704 | * sorted by netmask length (longest to smallest) | 716 | * sorted by netmask length (longest to smallest) |
717 | * locked by &smk_netlbladdr_lock in smk_write_netlbladdr | ||
718 | * | ||
705 | */ | 719 | */ |
706 | static void smk_netlbladdr_insert(struct smk_netlbladdr *new) | 720 | static void smk_netlbladdr_insert(struct smk_netlbladdr *new) |
707 | { | 721 | { |
708 | struct smk_netlbladdr *m; | 722 | struct smk_netlbladdr *m, *m_next; |
709 | 723 | ||
710 | if (smack_netlbladdrs == NULL) { | 724 | if (list_empty(&smk_netlbladdr_list)) { |
711 | smack_netlbladdrs = new; | 725 | list_add_rcu(&new->list, &smk_netlbladdr_list); |
712 | return; | 726 | return; |
713 | } | 727 | } |
714 | 728 | ||
729 | m = list_entry(rcu_dereference(smk_netlbladdr_list.next), | ||
730 | struct smk_netlbladdr, list); | ||
731 | |||
715 | /* the comparison '>' is a bit hacky, but works */ | 732 | /* the comparison '>' is a bit hacky, but works */ |
716 | if (new->smk_mask.s_addr > smack_netlbladdrs->smk_mask.s_addr) { | 733 | if (new->smk_mask.s_addr > m->smk_mask.s_addr) { |
717 | new->smk_next = smack_netlbladdrs; | 734 | list_add_rcu(&new->list, &smk_netlbladdr_list); |
718 | smack_netlbladdrs = new; | ||
719 | return; | 735 | return; |
720 | } | 736 | } |
721 | for (m = smack_netlbladdrs; m != NULL; m = m->smk_next) { | 737 | |
722 | if (m->smk_next == NULL) { | 738 | list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) { |
723 | m->smk_next = new; | 739 | if (list_is_last(&m->list, &smk_netlbladdr_list)) { |
740 | list_add_rcu(&new->list, &m->list); | ||
724 | return; | 741 | return; |
725 | } | 742 | } |
726 | if (new->smk_mask.s_addr > m->smk_next->smk_mask.s_addr) { | 743 | m_next = list_entry(rcu_dereference(m->list.next), |
727 | new->smk_next = m->smk_next; | 744 | struct smk_netlbladdr, list); |
728 | m->smk_next = new; | 745 | if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) { |
746 | list_add_rcu(&new->list, &m->list); | ||
729 | return; | 747 | return; |
730 | } | 748 | } |
731 | } | 749 | } |
@@ -755,6 +773,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, | |||
755 | struct netlbl_audit audit_info; | 773 | struct netlbl_audit audit_info; |
756 | struct in_addr mask; | 774 | struct in_addr mask; |
757 | unsigned int m; | 775 | unsigned int m; |
776 | int found; | ||
758 | u32 mask_bits = (1<<31); | 777 | u32 mask_bits = (1<<31); |
759 | __be32 nsa; | 778 | __be32 nsa; |
760 | u32 temp_mask; | 779 | u32 temp_mask; |
@@ -808,14 +827,17 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, | |||
808 | 827 | ||
809 | nsa = newname.sin_addr.s_addr; | 828 | nsa = newname.sin_addr.s_addr; |
810 | /* try to find if the prefix is already in the list */ | 829 | /* try to find if the prefix is already in the list */ |
811 | for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next) | 830 | found = 0; |
831 | list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) { | ||
812 | if (skp->smk_host.sin_addr.s_addr == nsa && | 832 | if (skp->smk_host.sin_addr.s_addr == nsa && |
813 | skp->smk_mask.s_addr == mask.s_addr) | 833 | skp->smk_mask.s_addr == mask.s_addr) { |
834 | found = 1; | ||
814 | break; | 835 | break; |
815 | 836 | } | |
837 | } | ||
816 | smk_netlabel_audit_set(&audit_info); | 838 | smk_netlabel_audit_set(&audit_info); |
817 | 839 | ||
818 | if (skp == NULL) { | 840 | if (found == 0) { |
819 | skp = kzalloc(sizeof(*skp), GFP_KERNEL); | 841 | skp = kzalloc(sizeof(*skp), GFP_KERNEL); |
820 | if (skp == NULL) | 842 | if (skp == NULL) |
821 | rc = -ENOMEM; | 843 | rc = -ENOMEM; |