diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 193 |
1 files changed, 151 insertions, 42 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 28832e689800..2e8b4dfcbc74 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -18,7 +18,6 @@ | |||
18 | * as published by the Free Software Foundation. | 18 | * as published by the Free Software Foundation. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <linux/config.h> | ||
22 | #include <linux/module.h> | 21 | #include <linux/module.h> |
23 | #include <linux/init.h> | 22 | #include <linux/init.h> |
24 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
@@ -69,6 +68,7 @@ | |||
69 | #include <linux/sysctl.h> | 68 | #include <linux/sysctl.h> |
70 | #include <linux/audit.h> | 69 | #include <linux/audit.h> |
71 | #include <linux/string.h> | 70 | #include <linux/string.h> |
71 | #include <linux/selinux.h> | ||
72 | 72 | ||
73 | #include "avc.h" | 73 | #include "avc.h" |
74 | #include "objsec.h" | 74 | #include "objsec.h" |
@@ -246,6 +246,7 @@ static int superblock_alloc_security(struct super_block *sb) | |||
246 | sbsec->sb = sb; | 246 | sbsec->sb = sb; |
247 | sbsec->sid = SECINITSID_UNLABELED; | 247 | sbsec->sid = SECINITSID_UNLABELED; |
248 | sbsec->def_sid = SECINITSID_FILE; | 248 | sbsec->def_sid = SECINITSID_FILE; |
249 | sbsec->mntpoint_sid = SECINITSID_UNLABELED; | ||
249 | sb->s_security = sbsec; | 250 | sb->s_security = sbsec; |
250 | 251 | ||
251 | return 0; | 252 | return 0; |
@@ -319,19 +320,53 @@ enum { | |||
319 | Opt_context = 1, | 320 | Opt_context = 1, |
320 | Opt_fscontext = 2, | 321 | Opt_fscontext = 2, |
321 | Opt_defcontext = 4, | 322 | Opt_defcontext = 4, |
323 | Opt_rootcontext = 8, | ||
322 | }; | 324 | }; |
323 | 325 | ||
324 | static match_table_t tokens = { | 326 | static match_table_t tokens = { |
325 | {Opt_context, "context=%s"}, | 327 | {Opt_context, "context=%s"}, |
326 | {Opt_fscontext, "fscontext=%s"}, | 328 | {Opt_fscontext, "fscontext=%s"}, |
327 | {Opt_defcontext, "defcontext=%s"}, | 329 | {Opt_defcontext, "defcontext=%s"}, |
330 | {Opt_rootcontext, "rootcontext=%s"}, | ||
328 | }; | 331 | }; |
329 | 332 | ||
330 | #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" | 333 | #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" |
331 | 334 | ||
335 | static int may_context_mount_sb_relabel(u32 sid, | ||
336 | struct superblock_security_struct *sbsec, | ||
337 | struct task_security_struct *tsec) | ||
338 | { | ||
339 | int rc; | ||
340 | |||
341 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
342 | FILESYSTEM__RELABELFROM, NULL); | ||
343 | if (rc) | ||
344 | return rc; | ||
345 | |||
346 | rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, | ||
347 | FILESYSTEM__RELABELTO, NULL); | ||
348 | return rc; | ||
349 | } | ||
350 | |||
351 | static int may_context_mount_inode_relabel(u32 sid, | ||
352 | struct superblock_security_struct *sbsec, | ||
353 | struct task_security_struct *tsec) | ||
354 | { | ||
355 | int rc; | ||
356 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
357 | FILESYSTEM__RELABELFROM, NULL); | ||
358 | if (rc) | ||
359 | return rc; | ||
360 | |||
361 | rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
362 | FILESYSTEM__ASSOCIATE, NULL); | ||
363 | return rc; | ||
364 | } | ||
365 | |||
332 | static int try_context_mount(struct super_block *sb, void *data) | 366 | static int try_context_mount(struct super_block *sb, void *data) |
333 | { | 367 | { |
334 | char *context = NULL, *defcontext = NULL; | 368 | char *context = NULL, *defcontext = NULL; |
369 | char *fscontext = NULL, *rootcontext = NULL; | ||
335 | const char *name; | 370 | const char *name; |
336 | u32 sid; | 371 | u32 sid; |
337 | int alloc = 0, rc = 0, seen = 0; | 372 | int alloc = 0, rc = 0, seen = 0; |
@@ -374,7 +409,7 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
374 | 409 | ||
375 | switch (token) { | 410 | switch (token) { |
376 | case Opt_context: | 411 | case Opt_context: |
377 | if (seen) { | 412 | if (seen & (Opt_context|Opt_defcontext)) { |
378 | rc = -EINVAL; | 413 | rc = -EINVAL; |
379 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | 414 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); |
380 | goto out_free; | 415 | goto out_free; |
@@ -390,13 +425,13 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
390 | break; | 425 | break; |
391 | 426 | ||
392 | case Opt_fscontext: | 427 | case Opt_fscontext: |
393 | if (seen & (Opt_context|Opt_fscontext)) { | 428 | if (seen & Opt_fscontext) { |
394 | rc = -EINVAL; | 429 | rc = -EINVAL; |
395 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | 430 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); |
396 | goto out_free; | 431 | goto out_free; |
397 | } | 432 | } |
398 | context = match_strdup(&args[0]); | 433 | fscontext = match_strdup(&args[0]); |
399 | if (!context) { | 434 | if (!fscontext) { |
400 | rc = -ENOMEM; | 435 | rc = -ENOMEM; |
401 | goto out_free; | 436 | goto out_free; |
402 | } | 437 | } |
@@ -405,6 +440,22 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
405 | seen |= Opt_fscontext; | 440 | seen |= Opt_fscontext; |
406 | break; | 441 | break; |
407 | 442 | ||
443 | case Opt_rootcontext: | ||
444 | if (seen & Opt_rootcontext) { | ||
445 | rc = -EINVAL; | ||
446 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | ||
447 | goto out_free; | ||
448 | } | ||
449 | rootcontext = match_strdup(&args[0]); | ||
450 | if (!rootcontext) { | ||
451 | rc = -ENOMEM; | ||
452 | goto out_free; | ||
453 | } | ||
454 | if (!alloc) | ||
455 | alloc = 1; | ||
456 | seen |= Opt_rootcontext; | ||
457 | break; | ||
458 | |||
408 | case Opt_defcontext: | 459 | case Opt_defcontext: |
409 | if (sbsec->behavior != SECURITY_FS_USE_XATTR) { | 460 | if (sbsec->behavior != SECURITY_FS_USE_XATTR) { |
410 | rc = -EINVAL; | 461 | rc = -EINVAL; |
@@ -441,6 +492,28 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
441 | if (!seen) | 492 | if (!seen) |
442 | goto out; | 493 | goto out; |
443 | 494 | ||
495 | /* sets the context of the superblock for the fs being mounted. */ | ||
496 | if (fscontext) { | ||
497 | rc = security_context_to_sid(fscontext, strlen(fscontext), &sid); | ||
498 | if (rc) { | ||
499 | printk(KERN_WARNING "SELinux: security_context_to_sid" | ||
500 | "(%s) failed for (dev %s, type %s) errno=%d\n", | ||
501 | fscontext, sb->s_id, name, rc); | ||
502 | goto out_free; | ||
503 | } | ||
504 | |||
505 | rc = may_context_mount_sb_relabel(sid, sbsec, tsec); | ||
506 | if (rc) | ||
507 | goto out_free; | ||
508 | |||
509 | sbsec->sid = sid; | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * Switch to using mount point labeling behavior. | ||
514 | * sets the label used on all file below the mountpoint, and will set | ||
515 | * the superblock context if not already set. | ||
516 | */ | ||
444 | if (context) { | 517 | if (context) { |
445 | rc = security_context_to_sid(context, strlen(context), &sid); | 518 | rc = security_context_to_sid(context, strlen(context), &sid); |
446 | if (rc) { | 519 | if (rc) { |
@@ -450,20 +523,34 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
450 | goto out_free; | 523 | goto out_free; |
451 | } | 524 | } |
452 | 525 | ||
453 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | 526 | rc = may_context_mount_sb_relabel(sid, sbsec, tsec); |
454 | FILESYSTEM__RELABELFROM, NULL); | ||
455 | if (rc) | 527 | if (rc) |
456 | goto out_free; | 528 | goto out_free; |
457 | 529 | ||
458 | rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, | 530 | if (!fscontext) |
459 | FILESYSTEM__RELABELTO, NULL); | 531 | sbsec->sid = sid; |
460 | if (rc) | 532 | sbsec->mntpoint_sid = sid; |
533 | |||
534 | sbsec->behavior = SECURITY_FS_USE_MNTPOINT; | ||
535 | } | ||
536 | |||
537 | if (rootcontext) { | ||
538 | struct inode *inode = sb->s_root->d_inode; | ||
539 | struct inode_security_struct *isec = inode->i_security; | ||
540 | rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid); | ||
541 | if (rc) { | ||
542 | printk(KERN_WARNING "SELinux: security_context_to_sid" | ||
543 | "(%s) failed for (dev %s, type %s) errno=%d\n", | ||
544 | rootcontext, sb->s_id, name, rc); | ||
461 | goto out_free; | 545 | goto out_free; |
546 | } | ||
462 | 547 | ||
463 | sbsec->sid = sid; | 548 | rc = may_context_mount_inode_relabel(sid, sbsec, tsec); |
549 | if (rc) | ||
550 | goto out_free; | ||
464 | 551 | ||
465 | if (seen & Opt_context) | 552 | isec->sid = sid; |
466 | sbsec->behavior = SECURITY_FS_USE_MNTPOINT; | 553 | isec->initialized = 1; |
467 | } | 554 | } |
468 | 555 | ||
469 | if (defcontext) { | 556 | if (defcontext) { |
@@ -478,13 +565,7 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
478 | if (sid == sbsec->def_sid) | 565 | if (sid == sbsec->def_sid) |
479 | goto out_free; | 566 | goto out_free; |
480 | 567 | ||
481 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | 568 | rc = may_context_mount_inode_relabel(sid, sbsec, tsec); |
482 | FILESYSTEM__RELABELFROM, NULL); | ||
483 | if (rc) | ||
484 | goto out_free; | ||
485 | |||
486 | rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
487 | FILESYSTEM__ASSOCIATE, NULL); | ||
488 | if (rc) | 569 | if (rc) |
489 | goto out_free; | 570 | goto out_free; |
490 | 571 | ||
@@ -495,6 +576,8 @@ out_free: | |||
495 | if (alloc) { | 576 | if (alloc) { |
496 | kfree(context); | 577 | kfree(context); |
497 | kfree(defcontext); | 578 | kfree(defcontext); |
579 | kfree(fscontext); | ||
580 | kfree(rootcontext); | ||
498 | } | 581 | } |
499 | out: | 582 | out: |
500 | return rc; | 583 | return rc; |
@@ -876,8 +959,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
876 | goto out; | 959 | goto out; |
877 | isec->sid = sid; | 960 | isec->sid = sid; |
878 | break; | 961 | break; |
962 | case SECURITY_FS_USE_MNTPOINT: | ||
963 | isec->sid = sbsec->mntpoint_sid; | ||
964 | break; | ||
879 | default: | 965 | default: |
880 | /* Default to the fs SID. */ | 966 | /* Default to the fs superblock SID. */ |
881 | isec->sid = sbsec->sid; | 967 | isec->sid = sbsec->sid; |
882 | 968 | ||
883 | if (sbsec->proc) { | 969 | if (sbsec->proc) { |
@@ -1843,7 +1929,8 @@ static inline int selinux_option(char *option, int len) | |||
1843 | { | 1929 | { |
1844 | return (match_prefix("context=", sizeof("context=")-1, option, len) || | 1930 | return (match_prefix("context=", sizeof("context=")-1, option, len) || |
1845 | match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || | 1931 | match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || |
1846 | match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); | 1932 | match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) || |
1933 | match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len)); | ||
1847 | } | 1934 | } |
1848 | 1935 | ||
1849 | static inline void take_option(char **to, char *from, int *first, int len) | 1936 | static inline void take_option(char **to, char *from, int *first, int len) |
@@ -2643,6 +2730,11 @@ static int selinux_task_getsid(struct task_struct *p) | |||
2643 | return task_has_perm(current, p, PROCESS__GETSESSION); | 2730 | return task_has_perm(current, p, PROCESS__GETSESSION); |
2644 | } | 2731 | } |
2645 | 2732 | ||
2733 | static void selinux_task_getsecid(struct task_struct *p, u32 *secid) | ||
2734 | { | ||
2735 | selinux_get_task_sid(p, secid); | ||
2736 | } | ||
2737 | |||
2646 | static int selinux_task_setgroups(struct group_info *group_info) | 2738 | static int selinux_task_setgroups(struct group_info *group_info) |
2647 | { | 2739 | { |
2648 | /* See the comment for setuid above. */ | 2740 | /* See the comment for setuid above. */ |
@@ -2665,6 +2757,11 @@ static int selinux_task_setioprio(struct task_struct *p, int ioprio) | |||
2665 | return task_has_perm(current, p, PROCESS__SETSCHED); | 2757 | return task_has_perm(current, p, PROCESS__SETSCHED); |
2666 | } | 2758 | } |
2667 | 2759 | ||
2760 | static int selinux_task_getioprio(struct task_struct *p) | ||
2761 | { | ||
2762 | return task_has_perm(current, p, PROCESS__GETSCHED); | ||
2763 | } | ||
2764 | |||
2668 | static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) | 2765 | static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) |
2669 | { | 2766 | { |
2670 | struct rlimit *old_rlim = current->signal->rlim + resource; | 2767 | struct rlimit *old_rlim = current->signal->rlim + resource; |
@@ -2699,12 +2796,14 @@ static int selinux_task_movememory(struct task_struct *p) | |||
2699 | return task_has_perm(current, p, PROCESS__SETSCHED); | 2796 | return task_has_perm(current, p, PROCESS__SETSCHED); |
2700 | } | 2797 | } |
2701 | 2798 | ||
2702 | static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig) | 2799 | static int selinux_task_kill(struct task_struct *p, struct siginfo *info, |
2800 | int sig, u32 secid) | ||
2703 | { | 2801 | { |
2704 | u32 perm; | 2802 | u32 perm; |
2705 | int rc; | 2803 | int rc; |
2804 | struct task_security_struct *tsec; | ||
2706 | 2805 | ||
2707 | rc = secondary_ops->task_kill(p, info, sig); | 2806 | rc = secondary_ops->task_kill(p, info, sig, secid); |
2708 | if (rc) | 2807 | if (rc) |
2709 | return rc; | 2808 | return rc; |
2710 | 2809 | ||
@@ -2715,8 +2814,12 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int si | |||
2715 | perm = PROCESS__SIGNULL; /* null signal; existence test */ | 2814 | perm = PROCESS__SIGNULL; /* null signal; existence test */ |
2716 | else | 2815 | else |
2717 | perm = signal_to_av(sig); | 2816 | perm = signal_to_av(sig); |
2718 | 2817 | tsec = p->security; | |
2719 | return task_has_perm(current, p, perm); | 2818 | if (secid) |
2819 | rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL); | ||
2820 | else | ||
2821 | rc = task_has_perm(current, p, perm); | ||
2822 | return rc; | ||
2720 | } | 2823 | } |
2721 | 2824 | ||
2722 | static int selinux_task_prctl(int option, | 2825 | static int selinux_task_prctl(int option, |
@@ -3420,7 +3523,13 @@ out: | |||
3420 | static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen) | 3523 | static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen) |
3421 | { | 3524 | { |
3422 | int err = 0; | 3525 | int err = 0; |
3423 | u32 peer_sid = selinux_socket_getpeer_dgram(skb); | 3526 | u32 peer_sid; |
3527 | |||
3528 | if (skb->sk->sk_family == PF_UNIX) | ||
3529 | selinux_get_inode_sid(SOCK_INODE(skb->sk->sk_socket), | ||
3530 | &peer_sid); | ||
3531 | else | ||
3532 | peer_sid = selinux_socket_getpeer_dgram(skb); | ||
3424 | 3533 | ||
3425 | if (peer_sid == SECSID_NULL) | 3534 | if (peer_sid == SECSID_NULL) |
3426 | return -EINVAL; | 3535 | return -EINVAL; |
@@ -3432,8 +3541,6 @@ static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, | |||
3432 | return 0; | 3541 | return 0; |
3433 | } | 3542 | } |
3434 | 3543 | ||
3435 | |||
3436 | |||
3437 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) | 3544 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) |
3438 | { | 3545 | { |
3439 | return sk_alloc_security(sk, family, priority); | 3546 | return sk_alloc_security(sk, family, priority); |
@@ -3641,32 +3748,32 @@ static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum, | |||
3641 | 3748 | ||
3642 | static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) | 3749 | static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) |
3643 | { | 3750 | { |
3644 | struct task_security_struct *tsec; | ||
3645 | struct av_decision avd; | ||
3646 | int err; | 3751 | int err; |
3647 | 3752 | ||
3648 | err = secondary_ops->netlink_send(sk, skb); | 3753 | err = secondary_ops->netlink_send(sk, skb); |
3649 | if (err) | 3754 | if (err) |
3650 | return err; | 3755 | return err; |
3651 | 3756 | ||
3652 | tsec = current->security; | ||
3653 | |||
3654 | avd.allowed = 0; | ||
3655 | avc_has_perm_noaudit(tsec->sid, tsec->sid, | ||
3656 | SECCLASS_CAPABILITY, ~0, &avd); | ||
3657 | cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed); | ||
3658 | |||
3659 | if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) | 3757 | if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) |
3660 | err = selinux_nlmsg_perm(sk, skb); | 3758 | err = selinux_nlmsg_perm(sk, skb); |
3661 | 3759 | ||
3662 | return err; | 3760 | return err; |
3663 | } | 3761 | } |
3664 | 3762 | ||
3665 | static int selinux_netlink_recv(struct sk_buff *skb) | 3763 | static int selinux_netlink_recv(struct sk_buff *skb, int capability) |
3666 | { | 3764 | { |
3667 | if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) | 3765 | int err; |
3668 | return -EPERM; | 3766 | struct avc_audit_data ad; |
3669 | return 0; | 3767 | |
3768 | err = secondary_ops->netlink_recv(skb, capability); | ||
3769 | if (err) | ||
3770 | return err; | ||
3771 | |||
3772 | AVC_AUDIT_DATA_INIT(&ad, CAP); | ||
3773 | ad.u.cap = capability; | ||
3774 | |||
3775 | return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid, | ||
3776 | SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad); | ||
3670 | } | 3777 | } |
3671 | 3778 | ||
3672 | static int ipc_alloc_security(struct task_struct *task, | 3779 | static int ipc_alloc_security(struct task_struct *task, |
@@ -4429,9 +4536,11 @@ static struct security_operations selinux_ops = { | |||
4429 | .task_setpgid = selinux_task_setpgid, | 4536 | .task_setpgid = selinux_task_setpgid, |
4430 | .task_getpgid = selinux_task_getpgid, | 4537 | .task_getpgid = selinux_task_getpgid, |
4431 | .task_getsid = selinux_task_getsid, | 4538 | .task_getsid = selinux_task_getsid, |
4539 | .task_getsecid = selinux_task_getsecid, | ||
4432 | .task_setgroups = selinux_task_setgroups, | 4540 | .task_setgroups = selinux_task_setgroups, |
4433 | .task_setnice = selinux_task_setnice, | 4541 | .task_setnice = selinux_task_setnice, |
4434 | .task_setioprio = selinux_task_setioprio, | 4542 | .task_setioprio = selinux_task_setioprio, |
4543 | .task_getioprio = selinux_task_getioprio, | ||
4435 | .task_setrlimit = selinux_task_setrlimit, | 4544 | .task_setrlimit = selinux_task_setrlimit, |
4436 | .task_setscheduler = selinux_task_setscheduler, | 4545 | .task_setscheduler = selinux_task_setscheduler, |
4437 | .task_getscheduler = selinux_task_getscheduler, | 4546 | .task_getscheduler = selinux_task_getscheduler, |