diff options
Diffstat (limited to 'security/smack/smackfs.c')
-rw-r--r-- | security/smack/smackfs.c | 218 |
1 files changed, 131 insertions, 87 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index a1b57e4dba3e..e03a7e19c73b 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
@@ -80,10 +80,14 @@ 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; | 88 | |
89 | const char *smack_cipso_option = SMACK_CIPSO_OPTION; | ||
90 | |||
87 | 91 | ||
88 | #define SEQ_READ_FINISHED 1 | 92 | #define SEQ_READ_FINISHED 1 |
89 | 93 | ||
@@ -134,24 +138,27 @@ static void *load_seq_start(struct seq_file *s, loff_t *pos) | |||
134 | { | 138 | { |
135 | if (*pos == SEQ_READ_FINISHED) | 139 | if (*pos == SEQ_READ_FINISHED) |
136 | return NULL; | 140 | return NULL; |
137 | 141 | if (list_empty(&smack_rule_list)) | |
138 | return smack_list; | 142 | return NULL; |
143 | return smack_rule_list.next; | ||
139 | } | 144 | } |
140 | 145 | ||
141 | static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) | 146 | static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) |
142 | { | 147 | { |
143 | struct smk_list_entry *skp = ((struct smk_list_entry *) v)->smk_next; | 148 | struct list_head *list = v; |
144 | 149 | ||
145 | if (skp == NULL) | 150 | if (list_is_last(list, &smack_rule_list)) { |
146 | *pos = SEQ_READ_FINISHED; | 151 | *pos = SEQ_READ_FINISHED; |
147 | 152 | return NULL; | |
148 | return skp; | 153 | } |
154 | return list->next; | ||
149 | } | 155 | } |
150 | 156 | ||
151 | static int load_seq_show(struct seq_file *s, void *v) | 157 | static int load_seq_show(struct seq_file *s, void *v) |
152 | { | 158 | { |
153 | struct smk_list_entry *slp = (struct smk_list_entry *) v; | 159 | struct list_head *list = v; |
154 | struct smack_rule *srp = &slp->smk_rule; | 160 | struct smack_rule *srp = |
161 | list_entry(list, struct smack_rule, list); | ||
155 | 162 | ||
156 | seq_printf(s, "%s %s", (char *)srp->smk_subject, | 163 | seq_printf(s, "%s %s", (char *)srp->smk_subject, |
157 | (char *)srp->smk_object); | 164 | (char *)srp->smk_object); |
@@ -212,32 +219,23 @@ static int smk_open_load(struct inode *inode, struct file *file) | |||
212 | */ | 219 | */ |
213 | static int smk_set_access(struct smack_rule *srp) | 220 | static int smk_set_access(struct smack_rule *srp) |
214 | { | 221 | { |
215 | struct smk_list_entry *sp; | 222 | struct smack_rule *sp; |
216 | struct smk_list_entry *newp; | ||
217 | int ret = 0; | 223 | int ret = 0; |
218 | 224 | int found; | |
219 | mutex_lock(&smack_list_lock); | 225 | mutex_lock(&smack_list_lock); |
220 | 226 | ||
221 | for (sp = smack_list; sp != NULL; sp = sp->smk_next) | 227 | found = 0; |
222 | if (sp->smk_rule.smk_subject == srp->smk_subject && | 228 | list_for_each_entry_rcu(sp, &smack_rule_list, list) { |
223 | sp->smk_rule.smk_object == srp->smk_object) { | 229 | if (sp->smk_subject == srp->smk_subject && |
224 | sp->smk_rule.smk_access = srp->smk_access; | 230 | sp->smk_object == srp->smk_object) { |
231 | found = 1; | ||
232 | sp->smk_access = srp->smk_access; | ||
225 | break; | 233 | break; |
226 | } | 234 | } |
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 | } | 235 | } |
236 | if (found == 0) | ||
237 | list_add_rcu(&srp->list, &smack_rule_list); | ||
239 | 238 | ||
240 | out: | ||
241 | mutex_unlock(&smack_list_lock); | 239 | mutex_unlock(&smack_list_lock); |
242 | 240 | ||
243 | return ret; | 241 | return ret; |
@@ -261,7 +259,7 @@ out: | |||
261 | static ssize_t smk_write_load(struct file *file, const char __user *buf, | 259 | static ssize_t smk_write_load(struct file *file, const char __user *buf, |
262 | size_t count, loff_t *ppos) | 260 | size_t count, loff_t *ppos) |
263 | { | 261 | { |
264 | struct smack_rule rule; | 262 | struct smack_rule *rule; |
265 | char *data; | 263 | char *data; |
266 | int rc = -EINVAL; | 264 | int rc = -EINVAL; |
267 | 265 | ||
@@ -272,9 +270,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
272 | */ | 270 | */ |
273 | if (!capable(CAP_MAC_ADMIN)) | 271 | if (!capable(CAP_MAC_ADMIN)) |
274 | return -EPERM; | 272 | return -EPERM; |
275 | if (*ppos != 0) | 273 | |
276 | return -EINVAL; | 274 | if (*ppos != 0 || count != SMK_LOADLEN) |
277 | if (count != SMK_LOADLEN) | ||
278 | return -EINVAL; | 275 | return -EINVAL; |
279 | 276 | ||
280 | data = kzalloc(count, GFP_KERNEL); | 277 | data = kzalloc(count, GFP_KERNEL); |
@@ -286,25 +283,31 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
286 | goto out; | 283 | goto out; |
287 | } | 284 | } |
288 | 285 | ||
289 | rule.smk_subject = smk_import(data, 0); | 286 | rule = kzalloc(sizeof(*rule), GFP_KERNEL); |
290 | if (rule.smk_subject == NULL) | 287 | if (rule == NULL) { |
288 | rc = -ENOMEM; | ||
291 | goto out; | 289 | goto out; |
290 | } | ||
292 | 291 | ||
293 | rule.smk_object = smk_import(data + SMK_LABELLEN, 0); | 292 | rule->smk_subject = smk_import(data, 0); |
294 | if (rule.smk_object == NULL) | 293 | if (rule->smk_subject == NULL) |
295 | goto out; | 294 | goto out_free_rule; |
296 | 295 | ||
297 | rule.smk_access = 0; | 296 | rule->smk_object = smk_import(data + SMK_LABELLEN, 0); |
297 | if (rule->smk_object == NULL) | ||
298 | goto out_free_rule; | ||
299 | |||
300 | rule->smk_access = 0; | ||
298 | 301 | ||
299 | switch (data[SMK_LABELLEN + SMK_LABELLEN]) { | 302 | switch (data[SMK_LABELLEN + SMK_LABELLEN]) { |
300 | case '-': | 303 | case '-': |
301 | break; | 304 | break; |
302 | case 'r': | 305 | case 'r': |
303 | case 'R': | 306 | case 'R': |
304 | rule.smk_access |= MAY_READ; | 307 | rule->smk_access |= MAY_READ; |
305 | break; | 308 | break; |
306 | default: | 309 | default: |
307 | goto out; | 310 | goto out_free_rule; |
308 | } | 311 | } |
309 | 312 | ||
310 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { | 313 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { |
@@ -312,10 +315,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
312 | break; | 315 | break; |
313 | case 'w': | 316 | case 'w': |
314 | case 'W': | 317 | case 'W': |
315 | rule.smk_access |= MAY_WRITE; | 318 | rule->smk_access |= MAY_WRITE; |
316 | break; | 319 | break; |
317 | default: | 320 | default: |
318 | goto out; | 321 | goto out_free_rule; |
319 | } | 322 | } |
320 | 323 | ||
321 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { | 324 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { |
@@ -323,10 +326,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
323 | break; | 326 | break; |
324 | case 'x': | 327 | case 'x': |
325 | case 'X': | 328 | case 'X': |
326 | rule.smk_access |= MAY_EXEC; | 329 | rule->smk_access |= MAY_EXEC; |
327 | break; | 330 | break; |
328 | default: | 331 | default: |
329 | goto out; | 332 | goto out_free_rule; |
330 | } | 333 | } |
331 | 334 | ||
332 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { | 335 | switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { |
@@ -334,17 +337,20 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
334 | break; | 337 | break; |
335 | case 'a': | 338 | case 'a': |
336 | case 'A': | 339 | case 'A': |
337 | rule.smk_access |= MAY_APPEND; | 340 | rule->smk_access |= MAY_APPEND; |
338 | break; | 341 | break; |
339 | default: | 342 | default: |
340 | goto out; | 343 | goto out_free_rule; |
341 | } | 344 | } |
342 | 345 | ||
343 | rc = smk_set_access(&rule); | 346 | rc = smk_set_access(rule); |
344 | 347 | ||
345 | if (!rc) | 348 | if (!rc) |
346 | rc = count; | 349 | rc = count; |
350 | goto out; | ||
347 | 351 | ||
352 | out_free_rule: | ||
353 | kfree(rule); | ||
348 | out: | 354 | out: |
349 | kfree(data); | 355 | kfree(data); |
350 | return rc; | 356 | return rc; |
@@ -433,24 +439,26 @@ static void *cipso_seq_start(struct seq_file *s, loff_t *pos) | |||
433 | { | 439 | { |
434 | if (*pos == SEQ_READ_FINISHED) | 440 | if (*pos == SEQ_READ_FINISHED) |
435 | return NULL; | 441 | return NULL; |
442 | if (list_empty(&smack_known_list)) | ||
443 | return NULL; | ||
436 | 444 | ||
437 | return smack_known; | 445 | return smack_known_list.next; |
438 | } | 446 | } |
439 | 447 | ||
440 | static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) | 448 | static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) |
441 | { | 449 | { |
442 | struct smack_known *skp = ((struct smack_known *) v)->smk_next; | 450 | struct list_head *list = v; |
443 | 451 | ||
444 | /* | 452 | /* |
445 | * Omit labels with no associated cipso value | 453 | * labels with no associated cipso value wont be printed |
454 | * in cipso_seq_show | ||
446 | */ | 455 | */ |
447 | while (skp != NULL && !skp->smk_cipso) | 456 | if (list_is_last(list, &smack_known_list)) { |
448 | skp = skp->smk_next; | ||
449 | |||
450 | if (skp == NULL) | ||
451 | *pos = SEQ_READ_FINISHED; | 457 | *pos = SEQ_READ_FINISHED; |
458 | return NULL; | ||
459 | } | ||
452 | 460 | ||
453 | return skp; | 461 | return list->next; |
454 | } | 462 | } |
455 | 463 | ||
456 | /* | 464 | /* |
@@ -459,7 +467,9 @@ static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) | |||
459 | */ | 467 | */ |
460 | static int cipso_seq_show(struct seq_file *s, void *v) | 468 | static int cipso_seq_show(struct seq_file *s, void *v) |
461 | { | 469 | { |
462 | struct smack_known *skp = (struct smack_known *) v; | 470 | struct list_head *list = v; |
471 | struct smack_known *skp = | ||
472 | list_entry(list, struct smack_known, list); | ||
463 | struct smack_cipso *scp = skp->smk_cipso; | 473 | struct smack_cipso *scp = skp->smk_cipso; |
464 | char *cbp; | 474 | char *cbp; |
465 | char sep = '/'; | 475 | char sep = '/'; |
@@ -558,6 +568,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, | |||
558 | goto unlockedout; | 568 | goto unlockedout; |
559 | } | 569 | } |
560 | 570 | ||
571 | /* labels cannot begin with a '-' */ | ||
572 | if (data[0] == '-') { | ||
573 | rc = -EINVAL; | ||
574 | goto unlockedout; | ||
575 | } | ||
561 | data[count] = '\0'; | 576 | data[count] = '\0'; |
562 | rule = data; | 577 | rule = data; |
563 | /* | 578 | /* |
@@ -638,18 +653,21 @@ static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) | |||
638 | { | 653 | { |
639 | if (*pos == SEQ_READ_FINISHED) | 654 | if (*pos == SEQ_READ_FINISHED) |
640 | return NULL; | 655 | return NULL; |
641 | 656 | if (list_empty(&smk_netlbladdr_list)) | |
642 | return smack_netlbladdrs; | 657 | return NULL; |
658 | return smk_netlbladdr_list.next; | ||
643 | } | 659 | } |
644 | 660 | ||
645 | static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) | 661 | static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) |
646 | { | 662 | { |
647 | struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next; | 663 | struct list_head *list = v; |
648 | 664 | ||
649 | if (skp == NULL) | 665 | if (list_is_last(list, &smk_netlbladdr_list)) { |
650 | *pos = SEQ_READ_FINISHED; | 666 | *pos = SEQ_READ_FINISHED; |
667 | return NULL; | ||
668 | } | ||
651 | 669 | ||
652 | return skp; | 670 | return list->next; |
653 | } | 671 | } |
654 | #define BEBITS (sizeof(__be32) * 8) | 672 | #define BEBITS (sizeof(__be32) * 8) |
655 | 673 | ||
@@ -658,7 +676,9 @@ static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) | |||
658 | */ | 676 | */ |
659 | static int netlbladdr_seq_show(struct seq_file *s, void *v) | 677 | static int netlbladdr_seq_show(struct seq_file *s, void *v) |
660 | { | 678 | { |
661 | struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v; | 679 | struct list_head *list = v; |
680 | struct smk_netlbladdr *skp = | ||
681 | list_entry(list, struct smk_netlbladdr, list); | ||
662 | unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; | 682 | unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; |
663 | int maskn; | 683 | int maskn; |
664 | u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr); | 684 | u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr); |
@@ -702,30 +722,36 @@ static int smk_open_netlbladdr(struct inode *inode, struct file *file) | |||
702 | * | 722 | * |
703 | * This helper insert netlabel in the smack_netlbladdrs list | 723 | * This helper insert netlabel in the smack_netlbladdrs list |
704 | * sorted by netmask length (longest to smallest) | 724 | * sorted by netmask length (longest to smallest) |
725 | * locked by &smk_netlbladdr_lock in smk_write_netlbladdr | ||
726 | * | ||
705 | */ | 727 | */ |
706 | static void smk_netlbladdr_insert(struct smk_netlbladdr *new) | 728 | static void smk_netlbladdr_insert(struct smk_netlbladdr *new) |
707 | { | 729 | { |
708 | struct smk_netlbladdr *m; | 730 | struct smk_netlbladdr *m, *m_next; |
709 | 731 | ||
710 | if (smack_netlbladdrs == NULL) { | 732 | if (list_empty(&smk_netlbladdr_list)) { |
711 | smack_netlbladdrs = new; | 733 | list_add_rcu(&new->list, &smk_netlbladdr_list); |
712 | return; | 734 | return; |
713 | } | 735 | } |
714 | 736 | ||
737 | m = list_entry(rcu_dereference(smk_netlbladdr_list.next), | ||
738 | struct smk_netlbladdr, list); | ||
739 | |||
715 | /* the comparison '>' is a bit hacky, but works */ | 740 | /* the comparison '>' is a bit hacky, but works */ |
716 | if (new->smk_mask.s_addr > smack_netlbladdrs->smk_mask.s_addr) { | 741 | if (new->smk_mask.s_addr > m->smk_mask.s_addr) { |
717 | new->smk_next = smack_netlbladdrs; | 742 | list_add_rcu(&new->list, &smk_netlbladdr_list); |
718 | smack_netlbladdrs = new; | ||
719 | return; | 743 | return; |
720 | } | 744 | } |
721 | for (m = smack_netlbladdrs; m != NULL; m = m->smk_next) { | 745 | |
722 | if (m->smk_next == NULL) { | 746 | list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) { |
723 | m->smk_next = new; | 747 | if (list_is_last(&m->list, &smk_netlbladdr_list)) { |
748 | list_add_rcu(&new->list, &m->list); | ||
724 | return; | 749 | return; |
725 | } | 750 | } |
726 | if (new->smk_mask.s_addr > m->smk_next->smk_mask.s_addr) { | 751 | m_next = list_entry(rcu_dereference(m->list.next), |
727 | new->smk_next = m->smk_next; | 752 | struct smk_netlbladdr, list); |
728 | m->smk_next = new; | 753 | if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) { |
754 | list_add_rcu(&new->list, &m->list); | ||
729 | return; | 755 | return; |
730 | } | 756 | } |
731 | } | 757 | } |
@@ -755,6 +781,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, | |||
755 | struct netlbl_audit audit_info; | 781 | struct netlbl_audit audit_info; |
756 | struct in_addr mask; | 782 | struct in_addr mask; |
757 | unsigned int m; | 783 | unsigned int m; |
784 | int found; | ||
758 | u32 mask_bits = (1<<31); | 785 | u32 mask_bits = (1<<31); |
759 | __be32 nsa; | 786 | __be32 nsa; |
760 | u32 temp_mask; | 787 | u32 temp_mask; |
@@ -789,9 +816,18 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, | |||
789 | if (m > BEBITS) | 816 | if (m > BEBITS) |
790 | return -EINVAL; | 817 | return -EINVAL; |
791 | 818 | ||
792 | sp = smk_import(smack, 0); | 819 | /* if smack begins with '-', its an option, don't import it */ |
793 | if (sp == NULL) | 820 | if (smack[0] != '-') { |
794 | return -EINVAL; | 821 | sp = smk_import(smack, 0); |
822 | if (sp == NULL) | ||
823 | return -EINVAL; | ||
824 | } else { | ||
825 | /* check known options */ | ||
826 | if (strcmp(smack, smack_cipso_option) == 0) | ||
827 | sp = (char *)smack_cipso_option; | ||
828 | else | ||
829 | return -EINVAL; | ||
830 | } | ||
795 | 831 | ||
796 | for (temp_mask = 0; m > 0; m--) { | 832 | for (temp_mask = 0; m > 0; m--) { |
797 | temp_mask |= mask_bits; | 833 | temp_mask |= mask_bits; |
@@ -808,14 +844,17 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, | |||
808 | 844 | ||
809 | nsa = newname.sin_addr.s_addr; | 845 | nsa = newname.sin_addr.s_addr; |
810 | /* try to find if the prefix is already in the list */ | 846 | /* try to find if the prefix is already in the list */ |
811 | for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next) | 847 | found = 0; |
848 | list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) { | ||
812 | if (skp->smk_host.sin_addr.s_addr == nsa && | 849 | if (skp->smk_host.sin_addr.s_addr == nsa && |
813 | skp->smk_mask.s_addr == mask.s_addr) | 850 | skp->smk_mask.s_addr == mask.s_addr) { |
851 | found = 1; | ||
814 | break; | 852 | break; |
815 | 853 | } | |
854 | } | ||
816 | smk_netlabel_audit_set(&audit_info); | 855 | smk_netlabel_audit_set(&audit_info); |
817 | 856 | ||
818 | if (skp == NULL) { | 857 | if (found == 0) { |
819 | skp = kzalloc(sizeof(*skp), GFP_KERNEL); | 858 | skp = kzalloc(sizeof(*skp), GFP_KERNEL); |
820 | if (skp == NULL) | 859 | if (skp == NULL) |
821 | rc = -ENOMEM; | 860 | rc = -ENOMEM; |
@@ -827,18 +866,23 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, | |||
827 | smk_netlbladdr_insert(skp); | 866 | smk_netlbladdr_insert(skp); |
828 | } | 867 | } |
829 | } else { | 868 | } else { |
830 | rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, | 869 | /* we delete the unlabeled entry, only if the previous label |
831 | &skp->smk_host.sin_addr, &skp->smk_mask, | 870 | * wasnt the special CIPSO option */ |
832 | PF_INET, &audit_info); | 871 | if (skp->smk_label != smack_cipso_option) |
872 | rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, | ||
873 | &skp->smk_host.sin_addr, &skp->smk_mask, | ||
874 | PF_INET, &audit_info); | ||
875 | else | ||
876 | rc = 0; | ||
833 | skp->smk_label = sp; | 877 | skp->smk_label = sp; |
834 | } | 878 | } |
835 | 879 | ||
836 | /* | 880 | /* |
837 | * Now tell netlabel about the single label nature of | 881 | * Now tell netlabel about the single label nature of |
838 | * this host so that incoming packets get labeled. | 882 | * this host so that incoming packets get labeled. |
883 | * but only if we didn't get the special CIPSO option | ||
839 | */ | 884 | */ |
840 | 885 | if (rc == 0 && sp != smack_cipso_option) | |
841 | if (rc == 0) | ||
842 | rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, | 886 | rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, |
843 | &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, | 887 | &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, |
844 | smack_to_secid(skp->smk_label), &audit_info); | 888 | smack_to_secid(skp->smk_label), &audit_info); |