diff options
Diffstat (limited to 'security/selinux/ss/services.c')
-rw-r--r-- | security/selinux/ss/services.c | 107 |
1 files changed, 66 insertions, 41 deletions
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1f5bbb246d28..b66b454fe72b 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/string.h> | 34 | #include <linux/string.h> |
35 | #include <linux/spinlock.h> | 35 | #include <linux/spinlock.h> |
36 | #include <linux/rcupdate.h> | ||
36 | #include <linux/errno.h> | 37 | #include <linux/errno.h> |
37 | #include <linux/in.h> | 38 | #include <linux/in.h> |
38 | #include <linux/sched.h> | 39 | #include <linux/sched.h> |
@@ -2435,7 +2436,9 @@ static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, | |||
2435 | * | 2436 | * |
2436 | * Description: | 2437 | * Description: |
2437 | * Attempt to label a socket using the NetLabel mechanism using the given | 2438 | * Attempt to label a socket using the NetLabel mechanism using the given |
2438 | * SID. Returns zero values on success, negative values on failure. | 2439 | * SID. Returns zero values on success, negative values on failure. The |
2440 | * caller is responsibile for calling rcu_read_lock() before calling this | ||
2441 | * this function and rcu_read_unlock() after this function returns. | ||
2439 | * | 2442 | * |
2440 | */ | 2443 | */ |
2441 | static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) | 2444 | static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) |
@@ -2472,8 +2475,11 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid) | |||
2472 | secattr.flags |= NETLBL_SECATTR_MLS_CAT; | 2475 | secattr.flags |= NETLBL_SECATTR_MLS_CAT; |
2473 | 2476 | ||
2474 | rc = netlbl_socket_setattr(sock, &secattr); | 2477 | rc = netlbl_socket_setattr(sock, &secattr); |
2475 | if (rc == 0) | 2478 | if (rc == 0) { |
2479 | spin_lock(&sksec->nlbl_lock); | ||
2476 | sksec->nlbl_state = NLBL_LABELED; | 2480 | sksec->nlbl_state = NLBL_LABELED; |
2481 | spin_unlock(&sksec->nlbl_lock); | ||
2482 | } | ||
2477 | 2483 | ||
2478 | netlbl_socket_setsid_return: | 2484 | netlbl_socket_setsid_return: |
2479 | POLICY_RDUNLOCK; | 2485 | POLICY_RDUNLOCK; |
@@ -2482,6 +2488,25 @@ netlbl_socket_setsid_return: | |||
2482 | } | 2488 | } |
2483 | 2489 | ||
2484 | /** | 2490 | /** |
2491 | * selinux_netlbl_sk_security_reset - Reset the NetLabel fields | ||
2492 | * @ssec: the sk_security_struct | ||
2493 | * @family: the socket family | ||
2494 | * | ||
2495 | * Description: | ||
2496 | * Called when the NetLabel state of a sk_security_struct needs to be reset. | ||
2497 | * The caller is responsibile for all the NetLabel sk_security_struct locking. | ||
2498 | * | ||
2499 | */ | ||
2500 | void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, | ||
2501 | int family) | ||
2502 | { | ||
2503 | if (family == PF_INET) | ||
2504 | ssec->nlbl_state = NLBL_REQUIRE; | ||
2505 | else | ||
2506 | ssec->nlbl_state = NLBL_UNSET; | ||
2507 | } | ||
2508 | |||
2509 | /** | ||
2485 | * selinux_netlbl_sk_security_init - Setup the NetLabel fields | 2510 | * selinux_netlbl_sk_security_init - Setup the NetLabel fields |
2486 | * @ssec: the sk_security_struct | 2511 | * @ssec: the sk_security_struct |
2487 | * @family: the socket family | 2512 | * @family: the socket family |
@@ -2494,14 +2519,13 @@ netlbl_socket_setsid_return: | |||
2494 | void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, | 2519 | void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, |
2495 | int family) | 2520 | int family) |
2496 | { | 2521 | { |
2497 | if (family == PF_INET) | 2522 | /* No locking needed, we are the only one who has access to ssec */ |
2498 | ssec->nlbl_state = NLBL_REQUIRE; | 2523 | selinux_netlbl_sk_security_reset(ssec, family); |
2499 | else | 2524 | spin_lock_init(&ssec->nlbl_lock); |
2500 | ssec->nlbl_state = NLBL_UNSET; | ||
2501 | } | 2525 | } |
2502 | 2526 | ||
2503 | /** | 2527 | /** |
2504 | * selinux_netlbl_sk_clone_security - Copy the NetLabel fields | 2528 | * selinux_netlbl_sk_security_clone - Copy the NetLabel fields |
2505 | * @ssec: the original sk_security_struct | 2529 | * @ssec: the original sk_security_struct |
2506 | * @newssec: the cloned sk_security_struct | 2530 | * @newssec: the cloned sk_security_struct |
2507 | * | 2531 | * |
@@ -2510,41 +2534,41 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, | |||
2510 | * @newssec. | 2534 | * @newssec. |
2511 | * | 2535 | * |
2512 | */ | 2536 | */ |
2513 | void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec, | 2537 | void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, |
2514 | struct sk_security_struct *newssec) | 2538 | struct sk_security_struct *newssec) |
2515 | { | 2539 | { |
2540 | /* We don't need to take newssec->nlbl_lock because we are the only | ||
2541 | * thread with access to newssec, but we do need to take the RCU read | ||
2542 | * lock as other threads could have access to ssec */ | ||
2543 | rcu_read_lock(); | ||
2544 | selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); | ||
2516 | newssec->sclass = ssec->sclass; | 2545 | newssec->sclass = ssec->sclass; |
2517 | if (ssec->nlbl_state != NLBL_UNSET) | 2546 | rcu_read_unlock(); |
2518 | newssec->nlbl_state = NLBL_REQUIRE; | ||
2519 | else | ||
2520 | newssec->nlbl_state = NLBL_UNSET; | ||
2521 | } | 2547 | } |
2522 | 2548 | ||
2523 | /** | 2549 | /** |
2524 | * selinux_netlbl_socket_post_create - Label a socket using NetLabel | 2550 | * selinux_netlbl_socket_post_create - Label a socket using NetLabel |
2525 | * @sock: the socket to label | 2551 | * @sock: the socket to label |
2526 | * @sock_family: the socket family | ||
2527 | * @sid: the SID to use | ||
2528 | * | 2552 | * |
2529 | * Description: | 2553 | * Description: |
2530 | * Attempt to label a socket using the NetLabel mechanism using the given | 2554 | * Attempt to label a socket using the NetLabel mechanism using the given |
2531 | * SID. Returns zero values on success, negative values on failure. | 2555 | * SID. Returns zero values on success, negative values on failure. |
2532 | * | 2556 | * |
2533 | */ | 2557 | */ |
2534 | int selinux_netlbl_socket_post_create(struct socket *sock, | 2558 | int selinux_netlbl_socket_post_create(struct socket *sock) |
2535 | int sock_family, | ||
2536 | u32 sid) | ||
2537 | { | 2559 | { |
2560 | int rc = 0; | ||
2538 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | 2561 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; |
2539 | struct sk_security_struct *sksec = sock->sk->sk_security; | 2562 | struct sk_security_struct *sksec = sock->sk->sk_security; |
2540 | 2563 | ||
2541 | sksec->sclass = isec->sclass; | 2564 | sksec->sclass = isec->sclass; |
2542 | 2565 | ||
2543 | if (sock_family != PF_INET) | 2566 | rcu_read_lock(); |
2544 | return 0; | 2567 | if (sksec->nlbl_state == NLBL_REQUIRE) |
2568 | rc = selinux_netlbl_socket_setsid(sock, sksec->sid); | ||
2569 | rcu_read_unlock(); | ||
2545 | 2570 | ||
2546 | sksec->nlbl_state = NLBL_REQUIRE; | 2571 | return rc; |
2547 | return selinux_netlbl_socket_setsid(sock, sid); | ||
2548 | } | 2572 | } |
2549 | 2573 | ||
2550 | /** | 2574 | /** |
@@ -2566,8 +2590,12 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | |||
2566 | 2590 | ||
2567 | sksec->sclass = isec->sclass; | 2591 | sksec->sclass = isec->sclass; |
2568 | 2592 | ||
2569 | if (sk->sk_family != PF_INET) | 2593 | rcu_read_lock(); |
2594 | |||
2595 | if (sksec->nlbl_state != NLBL_REQUIRE) { | ||
2596 | rcu_read_unlock(); | ||
2570 | return; | 2597 | return; |
2598 | } | ||
2571 | 2599 | ||
2572 | netlbl_secattr_init(&secattr); | 2600 | netlbl_secattr_init(&secattr); |
2573 | if (netlbl_sock_getattr(sk, &secattr) == 0 && | 2601 | if (netlbl_sock_getattr(sk, &secattr) == 0 && |
@@ -2579,12 +2607,12 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | |||
2579 | sksec->peer_sid = nlbl_peer_sid; | 2607 | sksec->peer_sid = nlbl_peer_sid; |
2580 | netlbl_secattr_destroy(&secattr); | 2608 | netlbl_secattr_destroy(&secattr); |
2581 | 2609 | ||
2582 | sksec->nlbl_state = NLBL_REQUIRE; | ||
2583 | |||
2584 | /* Try to set the NetLabel on the socket to save time later, if we fail | 2610 | /* Try to set the NetLabel on the socket to save time later, if we fail |
2585 | * here we will pick up the pieces in later calls to | 2611 | * here we will pick up the pieces in later calls to |
2586 | * selinux_netlbl_inode_permission(). */ | 2612 | * selinux_netlbl_inode_permission(). */ |
2587 | selinux_netlbl_socket_setsid(sock, sksec->sid); | 2613 | selinux_netlbl_socket_setsid(sock, sksec->sid); |
2614 | |||
2615 | rcu_read_unlock(); | ||
2588 | } | 2616 | } |
2589 | 2617 | ||
2590 | /** | 2618 | /** |
@@ -2625,25 +2653,24 @@ u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid) | |||
2625 | int selinux_netlbl_inode_permission(struct inode *inode, int mask) | 2653 | int selinux_netlbl_inode_permission(struct inode *inode, int mask) |
2626 | { | 2654 | { |
2627 | int rc; | 2655 | int rc; |
2628 | struct inode_security_struct *isec; | ||
2629 | struct sk_security_struct *sksec; | 2656 | struct sk_security_struct *sksec; |
2630 | struct socket *sock; | 2657 | struct socket *sock; |
2631 | 2658 | ||
2632 | if (!S_ISSOCK(inode->i_mode)) | 2659 | if (!S_ISSOCK(inode->i_mode) || |
2660 | ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) | ||
2633 | return 0; | 2661 | return 0; |
2634 | |||
2635 | sock = SOCKET_I(inode); | 2662 | sock = SOCKET_I(inode); |
2636 | isec = inode->i_security; | ||
2637 | sksec = sock->sk->sk_security; | 2663 | sksec = sock->sk->sk_security; |
2638 | mutex_lock(&isec->lock); | 2664 | |
2639 | if (unlikely(sksec->nlbl_state == NLBL_REQUIRE && | 2665 | rcu_read_lock(); |
2640 | (mask & (MAY_WRITE | MAY_APPEND)))) { | 2666 | if (sksec->nlbl_state != NLBL_REQUIRE) { |
2641 | lock_sock(sock->sk); | 2667 | rcu_read_unlock(); |
2642 | rc = selinux_netlbl_socket_setsid(sock, sksec->sid); | 2668 | return 0; |
2643 | release_sock(sock->sk); | 2669 | } |
2644 | } else | 2670 | lock_sock(sock->sk); |
2645 | rc = 0; | 2671 | rc = selinux_netlbl_socket_setsid(sock, sksec->sid); |
2646 | mutex_unlock(&isec->lock); | 2672 | release_sock(sock->sk); |
2673 | rcu_read_unlock(); | ||
2647 | 2674 | ||
2648 | return rc; | 2675 | return rc; |
2649 | } | 2676 | } |
@@ -2754,12 +2781,10 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, | |||
2754 | int optname) | 2781 | int optname) |
2755 | { | 2782 | { |
2756 | int rc = 0; | 2783 | int rc = 0; |
2757 | struct inode *inode = SOCK_INODE(sock); | ||
2758 | struct sk_security_struct *sksec = sock->sk->sk_security; | 2784 | struct sk_security_struct *sksec = sock->sk->sk_security; |
2759 | struct inode_security_struct *isec = inode->i_security; | ||
2760 | struct netlbl_lsm_secattr secattr; | 2785 | struct netlbl_lsm_secattr secattr; |
2761 | 2786 | ||
2762 | mutex_lock(&isec->lock); | 2787 | rcu_read_lock(); |
2763 | if (level == IPPROTO_IP && optname == IP_OPTIONS && | 2788 | if (level == IPPROTO_IP && optname == IP_OPTIONS && |
2764 | sksec->nlbl_state == NLBL_LABELED) { | 2789 | sksec->nlbl_state == NLBL_LABELED) { |
2765 | netlbl_secattr_init(&secattr); | 2790 | netlbl_secattr_init(&secattr); |
@@ -2768,7 +2793,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, | |||
2768 | rc = -EACCES; | 2793 | rc = -EACCES; |
2769 | netlbl_secattr_destroy(&secattr); | 2794 | netlbl_secattr_destroy(&secattr); |
2770 | } | 2795 | } |
2771 | mutex_unlock(&isec->lock); | 2796 | rcu_read_unlock(); |
2772 | 2797 | ||
2773 | return rc; | 2798 | return rc; |
2774 | } | 2799 | } |