[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <49CD9387.8080001@schaufler-ca.com>
Date: Fri, 27 Mar 2009 20:03:35 -0700
From: Casey Schaufler <casey@...aufler-ca.com>
To: Paul Moore <paul.moore@...com>
CC: linux-security-module@...r.kernel.org, selinux@...ho.nsa.gov,
netdev@...r.kernel.org, etienne.basset@...ericable.fr,
Casey Schaufler <casey@...aufler-ca.com>
Subject: Re: [PATCH 2/6] netlabel: Label incoming TCP connections correctly
in SELinux
Paul Moore wrote:
> The current NetLabel/SELinux behavior for incoming TCP connections works but
> only through a series of happy coincidences that rely on the limited nature of
> standard CIPSO (only able to convey MLS attributes) and the write equality
> imposed by the SELinux MLS constraints. The problem is that network sockets
> created as the result of an incoming TCP connection were not on-the-wire
> labeled based on the security attributes of the parent socket but rather based
> on the wire label of the remote peer. The issue had to do with how IP options
> were managed as part of the network stack and where the LSM hooks were in
> relation to the code which set the IP options on these newly created child
> sockets. While NetLabel/SELinux did correctly set the socket's on-the-wire
> label it was promptly cleared by the network stack and reset based on the IP
> options of the remote peer.
>
> This patch, in conjunction with a prior patch that adjusted the LSM hook
> locations, works to set the correct on-the-wire label format for new incoming
> connections through the security_inet_conn_request() hook. Besides the
> correct behavior there are many advantages to this change, the most significant
> is that all of the NetLabel socket labeling code in SELinux now lives in hooks
> which can return error codes to the core stack which allows us to finally get
> ride of the selinux_netlbl_inode_permission() logic which greatly simplfies
> the NetLabel/SELinux glue code. In the process of developing this patch I
> also ran into a small handful of AF_INET6 cleanliness issues that have been
> fixed which should make the code safer and easier to extend in the future.
>
> Signed-off-by: Paul Moore <paul.moore@...com>
>
Acked-by: Casey Schaufler <casey@...aufler-ca.com>
> ---
>
> include/net/cipso_ipv4.h | 17 +++
> include/net/netlabel.h | 12 ++
> net/ipv4/cipso_ipv4.c | 130 ++++++++++++++++++++++--
> net/netlabel/netlabel_kapi.c | 152 +++++++++++++++++++++++++----
> security/selinux/hooks.c | 54 +++-------
> security/selinux/include/netlabel.h | 27 ++---
> security/selinux/netlabel.c | 186 ++++++++++-------------------------
> security/smack/smack_lsm.c | 2
> 8 files changed, 360 insertions(+), 220 deletions(-)
>
> diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
> index bedc7f6..abd4436 100644
> --- a/include/net/cipso_ipv4.h
> +++ b/include/net/cipso_ipv4.h
> @@ -40,6 +40,7 @@
> #include <linux/net.h>
> #include <linux/skbuff.h>
> #include <net/netlabel.h>
> +#include <net/request_sock.h>
> #include <asm/atomic.h>
>
> /* known doi values */
> @@ -215,6 +216,10 @@ int cipso_v4_sock_setattr(struct sock *sk,
> const struct netlbl_lsm_secattr *secattr);
> void cipso_v4_sock_delattr(struct sock *sk);
> int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr);
> +int cipso_v4_req_setattr(struct request_sock *req,
> + const struct cipso_v4_doi *doi_def,
> + const struct netlbl_lsm_secattr *secattr);
> +void cipso_v4_req_delattr(struct request_sock *req);
> int cipso_v4_skbuff_setattr(struct sk_buff *skb,
> const struct cipso_v4_doi *doi_def,
> const struct netlbl_lsm_secattr *secattr);
> @@ -247,6 +252,18 @@ static inline int cipso_v4_sock_getattr(struct sock *sk,
> return -ENOSYS;
> }
>
> +static inline int cipso_v4_req_setattr(struct request_sock *req,
> + const struct cipso_v4_doi *doi_def,
> + const struct netlbl_lsm_secattr *secattr)
> +{
> + return -ENOSYS;
> +}
> +
> +static inline void cipso_v4_req_delattr(struct request_sock *req)
> +{
> + return;
> +}
> +
> static inline int cipso_v4_skbuff_setattr(struct sk_buff *skb,
> const struct cipso_v4_doi *doi_def,
> const struct netlbl_lsm_secattr *secattr)
> diff --git a/include/net/netlabel.h b/include/net/netlabel.h
> index 749011e..bdb10e5 100644
> --- a/include/net/netlabel.h
> +++ b/include/net/netlabel.h
> @@ -36,6 +36,7 @@
> #include <linux/in.h>
> #include <linux/in6.h>
> #include <net/netlink.h>
> +#include <net/request_sock.h>
> #include <asm/atomic.h>
>
> struct cipso_v4_doi;
> @@ -406,6 +407,7 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap,
> */
> int netlbl_enabled(void);
> int netlbl_sock_setattr(struct sock *sk,
> + u16 family,
> const struct netlbl_lsm_secattr *secattr);
> void netlbl_sock_delattr(struct sock *sk);
> int netlbl_sock_getattr(struct sock *sk,
> @@ -413,6 +415,8 @@ int netlbl_sock_getattr(struct sock *sk,
> int netlbl_conn_setattr(struct sock *sk,
> struct sockaddr *addr,
> const struct netlbl_lsm_secattr *secattr);
> +int netlbl_req_setattr(struct request_sock *req,
> + const struct netlbl_lsm_secattr *secattr);
> int netlbl_skbuff_setattr(struct sk_buff *skb,
> u16 family,
> const struct netlbl_lsm_secattr *secattr);
> @@ -519,7 +523,8 @@ static inline int netlbl_enabled(void)
> return 0;
> }
> static inline int netlbl_sock_setattr(struct sock *sk,
> - const struct netlbl_lsm_secattr *secattr)
> + u16 family,
> + const struct netlbl_lsm_secattr *secattr)
> {
> return -ENOSYS;
> }
> @@ -537,6 +542,11 @@ static inline int netlbl_conn_setattr(struct sock *sk,
> {
> return -ENOSYS;
> }
> +static inline int netlbl_req_setattr(struct request_sock *req,
> + const struct netlbl_lsm_secattr *secattr)
> +{
> + return -ENOSYS;
> +}
> static inline int netlbl_skbuff_setattr(struct sk_buff *skb,
> u16 family,
> const struct netlbl_lsm_secattr *secattr)
> diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
> index 7bc9929..039cc1f 100644
> --- a/net/ipv4/cipso_ipv4.c
> +++ b/net/ipv4/cipso_ipv4.c
> @@ -1942,23 +1942,85 @@ socket_setattr_failure:
> }
>
> /**
> - * cipso_v4_sock_delattr - Delete the CIPSO option from a socket
> - * @sk: the socket
> + * cipso_v4_req_setattr - Add a CIPSO option to a connection request socket
> + * @req: the connection request socket
> + * @doi_def: the CIPSO DOI to use
> + * @secattr: the specific security attributes of the socket
> *
> * Description:
> - * Removes the CIPSO option from a socket, if present.
> + * Set the CIPSO option on the given socket using the DOI definition and
> + * security attributes passed to the function. Returns zero on success and
> + * negative values on failure.
> *
> */
> -void cipso_v4_sock_delattr(struct sock *sk)
> +int cipso_v4_req_setattr(struct request_sock *req,
> + const struct cipso_v4_doi *doi_def,
> + const struct netlbl_lsm_secattr *secattr)
> {
> - u8 hdr_delta;
> - struct ip_options *opt;
> - struct inet_sock *sk_inet;
> + int ret_val = -EPERM;
> + unsigned char *buf = NULL;
> + u32 buf_len;
> + u32 opt_len;
> + struct ip_options *opt = NULL;
> + struct inet_request_sock *req_inet;
>
> - sk_inet = inet_sk(sk);
> - opt = sk_inet->opt;
> - if (opt == NULL || opt->cipso == 0)
> - return;
> + /* We allocate the maximum CIPSO option size here so we are probably
> + * being a little wasteful, but it makes our life _much_ easier later
> + * on and after all we are only talking about 40 bytes. */
> + buf_len = CIPSO_V4_OPT_LEN_MAX;
> + buf = kmalloc(buf_len, GFP_ATOMIC);
> + if (buf == NULL) {
> + ret_val = -ENOMEM;
> + goto req_setattr_failure;
> + }
> +
> + ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
> + if (ret_val < 0)
> + goto req_setattr_failure;
> + buf_len = ret_val;
> +
> + /* We can't use ip_options_get() directly because it makes a call to
> + * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
> + * we won't always have CAP_NET_RAW even though we _always_ want to
> + * set the IPOPT_CIPSO option. */
> + opt_len = (buf_len + 3) & ~3;
> + opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
> + if (opt == NULL) {
> + ret_val = -ENOMEM;
> + goto req_setattr_failure;
> + }
> + memcpy(opt->__data, buf, buf_len);
> + opt->optlen = opt_len;
> + opt->cipso = sizeof(struct iphdr);
> + kfree(buf);
> + buf = NULL;
> +
> + req_inet = inet_rsk(req);
> + opt = xchg(&req_inet->opt, opt);
> + kfree(opt);
> +
> + return 0;
> +
> +req_setattr_failure:
> + kfree(buf);
> + kfree(opt);
> + return ret_val;
> +}
> +
> +/**
> + * cipso_v4_delopt - Delete the CIPSO option from a set of IP options
> + * @opt_ptr: IP option pointer
> + *
> + * Description:
> + * Deletes the CIPSO IP option from a set of IP options and makes the necessary
> + * adjustments to the IP option structure. Returns zero on success, negative
> + * values on failure.
> + *
> + */
> +int cipso_v4_delopt(struct ip_options **opt_ptr)
> +{
> + int hdr_delta = 0;
> + struct ip_options *opt = *opt_ptr;
>
> if (opt->srr || opt->rr || opt->ts || opt->router_alert) {
> u8 cipso_len;
> @@ -2003,11 +2065,34 @@ void cipso_v4_sock_delattr(struct sock *sk)
> } else {
> /* only the cipso option was present on the socket so we can
> * remove the entire option struct */
> - sk_inet->opt = NULL;
> + *opt_ptr = NULL;
> hdr_delta = opt->optlen;
> kfree(opt);
> }
>
> + return hdr_delta;
> +}
> +
> +/**
> + * cipso_v4_sock_delattr - Delete the CIPSO option from a socket
> + * @sk: the socket
> + *
> + * Description:
> + * Removes the CIPSO option from a socket, if present.
> + *
> + */
> +void cipso_v4_sock_delattr(struct sock *sk)
> +{
> + int hdr_delta;
> + struct ip_options *opt;
> + struct inet_sock *sk_inet;
> +
> + sk_inet = inet_sk(sk);
> + opt = sk_inet->opt;
> + if (opt == NULL || opt->cipso == 0)
> + return;
> +
> + hdr_delta = cipso_v4_delopt(&sk_inet->opt);
> if (sk_inet->is_icsk && hdr_delta > 0) {
> struct inet_connection_sock *sk_conn = inet_csk(sk);
> sk_conn->icsk_ext_hdr_len -= hdr_delta;
> @@ -2016,6 +2101,27 @@ void cipso_v4_sock_delattr(struct sock *sk)
> }
>
> /**
> + * cipso_v4_req_delattr - Delete the CIPSO option from a request socket
> + * @reg: the request socket
> + *
> + * Description:
> + * Removes the CIPSO option from a request socket, if present.
> + *
> + */
> +void cipso_v4_req_delattr(struct request_sock *req)
> +{
> + struct ip_options *opt;
> + struct inet_request_sock *req_inet;
> +
> + req_inet = inet_rsk(req);
> + opt = req_inet->opt;
> + if (opt == NULL || opt->cipso == 0)
> + return;
> +
> + cipso_v4_delopt(&req_inet->opt);
> +}
> +
> +/**
> * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
> * @cipso: the CIPSO v4 option
> * @secattr: the security attributes
> diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
> index fd9229d..cae2f5f 100644
> --- a/net/netlabel/netlabel_kapi.c
> +++ b/net/netlabel/netlabel_kapi.c
> @@ -619,8 +619,9 @@ int netlbl_enabled(void)
> }
>
> /**
> - * netlbl_socket_setattr - Label a socket using the correct protocol
> + * netlbl_sock_setattr - Label a socket using the correct protocol
> * @sk: the socket to label
> + * @family: protocol family
> * @secattr: the security attributes
> *
> * Description:
> @@ -633,29 +634,45 @@ int netlbl_enabled(void)
> *
> */
> int netlbl_sock_setattr(struct sock *sk,
> + u16 family,
> const struct netlbl_lsm_secattr *secattr)
> {
> - int ret_val = -ENOENT;
> + int ret_val;
> struct netlbl_dom_map *dom_entry;
>
> rcu_read_lock();
> dom_entry = netlbl_domhsh_getentry(secattr->domain);
> - if (dom_entry == NULL)
> + if (dom_entry == NULL) {
> + ret_val = -ENOENT;
> goto socket_setattr_return;
> - switch (dom_entry->type) {
> - case NETLBL_NLTYPE_ADDRSELECT:
> - ret_val = -EDESTADDRREQ;
> - break;
> - case NETLBL_NLTYPE_CIPSOV4:
> - ret_val = cipso_v4_sock_setattr(sk,
> - dom_entry->type_def.cipsov4,
> - secattr);
> + }
> + switch (family) {
> + case AF_INET:
> + switch (dom_entry->type) {
> + case NETLBL_NLTYPE_ADDRSELECT:
> + ret_val = -EDESTADDRREQ;
> + break;
> + case NETLBL_NLTYPE_CIPSOV4:
> + ret_val = cipso_v4_sock_setattr(sk,
> + dom_entry->type_def.cipsov4,
> + secattr);
> + break;
> + case NETLBL_NLTYPE_UNLABELED:
> + ret_val = 0;
> + break;
> + default:
> + ret_val = -ENOENT;
> + }
> break;
> - case NETLBL_NLTYPE_UNLABELED:
> +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> + case AF_INET6:
> + /* since we don't support any IPv6 labeling protocols right
> + * now we can optimize everything away until we do */
> ret_val = 0;
> break;
> +#endif /* IPv6 */
> default:
> - ret_val = -ENOENT;
> + ret_val = -EPROTONOSUPPORT;
> }
>
> socket_setattr_return:
> @@ -689,9 +706,25 @@ void netlbl_sock_delattr(struct sock *sk)
> * on failure.
> *
> */
> -int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
> +int netlbl_sock_getattr(struct sock *sk,
> + struct netlbl_lsm_secattr *secattr)
> {
> - return cipso_v4_sock_getattr(sk, secattr);
> + int ret_val;
> +
> + switch (sk->sk_family) {
> + case AF_INET:
> + ret_val = cipso_v4_sock_getattr(sk, secattr);
> + break;
> +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> + case AF_INET6:
> + ret_val = -ENOMSG;
> + break;
> +#endif /* IPv6 */
> + default:
> + ret_val = -EPROTONOSUPPORT;
> + }
> +
> + return ret_val;
> }
>
> /**
> @@ -748,7 +781,7 @@ int netlbl_conn_setattr(struct sock *sk,
> break;
> #endif /* IPv6 */
> default:
> - ret_val = 0;
> + ret_val = -EPROTONOSUPPORT;
> }
>
> conn_setattr_return:
> @@ -757,6 +790,77 @@ conn_setattr_return:
> }
>
> /**
> + * netlbl_req_setattr - Label a request socket using the correct protocol
> + * @req: the request socket to label
> + * @secattr: the security attributes
> + *
> + * Description:
> + * Attach the correct label to the given socket using the security attributes
> + * specified in @secattr. Returns zero on success, negative values on failure.
> + *
> + */
> +int netlbl_req_setattr(struct request_sock *req,
> + const struct netlbl_lsm_secattr *secattr)
> +{
> + int ret_val;
> + struct netlbl_dom_map *dom_entry;
> + struct netlbl_domaddr4_map *af4_entry;
> + u32 proto_type;
> + struct cipso_v4_doi *proto_cv4;
> +
> + rcu_read_lock();
> + dom_entry = netlbl_domhsh_getentry(secattr->domain);
> + if (dom_entry == NULL) {
> + ret_val = -ENOENT;
> + goto req_setattr_return;
> + }
> + switch (req->rsk_ops->family) {
> + case AF_INET:
> + if (dom_entry->type == NETLBL_NLTYPE_ADDRSELECT) {
> + struct inet_request_sock *req_inet = inet_rsk(req);
> + af4_entry = netlbl_domhsh_getentry_af4(secattr->domain,
> + req_inet->rmt_addr);
> + if (af4_entry == NULL) {
> + ret_val = -ENOENT;
> + goto req_setattr_return;
> + }
> + proto_type = af4_entry->type;
> + proto_cv4 = af4_entry->type_def.cipsov4;
> + } else {
> + proto_type = dom_entry->type;
> + proto_cv4 = dom_entry->type_def.cipsov4;
> + }
> + switch (proto_type) {
> + case NETLBL_NLTYPE_CIPSOV4:
> + ret_val = cipso_v4_req_setattr(req, proto_cv4, secattr);
> + break;
> + case NETLBL_NLTYPE_UNLABELED:
> + /* just delete the protocols we support for right now
> + * but we could remove other protocols if needed */
> + cipso_v4_req_delattr(req);
> + ret_val = 0;
> + break;
> + default:
> + ret_val = -ENOENT;
> + }
> + break;
> +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> + case AF_INET6:
> + /* since we don't support any IPv6 labeling protocols right
> + * now we can optimize everything away until we do */
> + ret_val = 0;
> + break;
> +#endif /* IPv6 */
> + default:
> + ret_val = -EPROTONOSUPPORT;
> + }
> +
> +req_setattr_return:
> + rcu_read_unlock();
> + return ret_val;
> +}
> +
> +/**
> * netlbl_skbuff_setattr - Label a packet using the correct protocol
> * @skb: the packet
> * @family: protocol family
> @@ -808,7 +912,7 @@ int netlbl_skbuff_setattr(struct sk_buff *skb,
> break;
> #endif /* IPv6 */
> default:
> - ret_val = 0;
> + ret_val = -EPROTONOSUPPORT;
> }
>
> skbuff_setattr_return:
> @@ -833,9 +937,17 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
> u16 family,
> struct netlbl_lsm_secattr *secattr)
> {
> - if (CIPSO_V4_OPTEXIST(skb) &&
> - cipso_v4_skbuff_getattr(skb, secattr) == 0)
> - return 0;
> + switch (family) {
> + case AF_INET:
> + if (CIPSO_V4_OPTEXIST(skb) &&
> + cipso_v4_skbuff_getattr(skb, secattr) == 0)
> + return 0;
> + break;
> +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> + case AF_INET6:
> + break;
> +#endif /* IPv6 */
> + }
>
> return netlbl_unlabel_getattr(skb, family, secattr);
> }
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 7c52ba2..ee2e781 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -311,7 +311,7 @@ static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
> ssec->sid = SECINITSID_UNLABELED;
> sk->sk_security = ssec;
>
> - selinux_netlbl_sk_security_reset(ssec, family);
> + selinux_netlbl_sk_security_reset(ssec);
>
> return 0;
> }
> @@ -2945,7 +2945,6 @@ static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
> static int selinux_revalidate_file_permission(struct file *file, int mask)
> {
> const struct cred *cred = current_cred();
> - int rc;
> struct inode *inode = file->f_path.dentry->d_inode;
>
> if (!mask) {
> @@ -2957,29 +2956,15 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
> if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
> mask |= MAY_APPEND;
>
> - rc = file_has_perm(cred, file,
> - file_mask_to_av(inode->i_mode, mask));
> - if (rc)
> - return rc;
> -
> - return selinux_netlbl_inode_permission(inode, mask);
> + return file_has_perm(cred, file,
> + file_mask_to_av(inode->i_mode, mask));
> }
>
> static int selinux_file_permission(struct file *file, int mask)
> {
> - struct inode *inode = file->f_path.dentry->d_inode;
> - struct file_security_struct *fsec = file->f_security;
> - struct inode_security_struct *isec = inode->i_security;
> - u32 sid = current_sid();
> -
> - if (!mask) {
> + if (!mask)
> /* No permission to check. Existence test. */
> return 0;
> - }
> -
> - if (sid == fsec->sid && fsec->isid == isec->sid
> - && fsec->pseqno == avc_policy_seqno())
> - return selinux_netlbl_inode_permission(inode, mask);
>
> return selinux_revalidate_file_permission(file, mask);
> }
> @@ -3723,7 +3708,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
> sksec = sock->sk->sk_security;
> sksec->sid = isec->sid;
> sksec->sclass = isec->sclass;
> - err = selinux_netlbl_socket_post_create(sock);
> + err = selinux_netlbl_socket_post_create(sock->sk, family);
> }
>
> return err;
> @@ -3914,13 +3899,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
> static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
> int size)
> {
> - int rc;
> -
> - rc = socket_has_perm(current, sock, SOCKET__WRITE);
> - if (rc)
> - return rc;
> -
> - return selinux_netlbl_inode_permission(SOCK_INODE(sock), MAY_WRITE);
> + return socket_has_perm(current, sock, SOCKET__WRITE);
> }
>
> static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
> @@ -4304,7 +4283,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
> newssec->peer_sid = ssec->peer_sid;
> newssec->sclass = ssec->sclass;
>
> - selinux_netlbl_sk_security_reset(newssec, newsk->sk_family);
> + selinux_netlbl_sk_security_reset(newssec);
> }
>
> static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
> @@ -4348,16 +4327,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
> if (peersid == SECSID_NULL) {
> req->secid = sksec->sid;
> req->peer_secid = SECSID_NULL;
> - return 0;
> + } else {
> + err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
> + if (err)
> + return err;
> + req->secid = newsid;
> + req->peer_secid = peersid;
> }
>
> - err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
> - if (err)
> - return err;
> -
> - req->secid = newsid;
> - req->peer_secid = peersid;
> - return 0;
> + return selinux_netlbl_inet_conn_request(req, family);
> }
>
> static void selinux_inet_csk_clone(struct sock *newsk,
> @@ -4374,7 +4352,7 @@ static void selinux_inet_csk_clone(struct sock *newsk,
>
> /* We don't need to take any sort of lock here as we are the only
> * thread with access to newsksec */
> - selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
> + selinux_netlbl_inet_csk_clone(newsk, req->rsk_ops->family);
> }
>
> static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
> @@ -4387,8 +4365,6 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
> family = PF_INET;
>
> selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
> -
> - selinux_netlbl_inet_conn_established(sk, family);
> }
>
> static void selinux_req_classify_flow(const struct request_sock *req,
> diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
> index b913c8d..b4b5b9b 100644
> --- a/security/selinux/include/netlabel.h
> +++ b/security/selinux/include/netlabel.h
> @@ -32,6 +32,7 @@
> #include <linux/net.h>
> #include <linux/skbuff.h>
> #include <net/sock.h>
> +#include <net/request_sock.h>
>
> #include "avc.h"
> #include "objsec.h"
> @@ -42,8 +43,7 @@ void selinux_netlbl_cache_invalidate(void);
> void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
>
> void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec);
> -void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
> - int family);
> +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec);
>
> int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
> u16 family,
> @@ -53,9 +53,9 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
> u16 family,
> u32 sid);
>
> -void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family);
> -int selinux_netlbl_socket_post_create(struct socket *sock);
> -int selinux_netlbl_inode_permission(struct inode *inode, int mask);
> +int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family);
> +void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family);
> +int selinux_netlbl_socket_post_create(struct sock *sk, u16 family);
> int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
> struct sk_buff *skb,
> u16 family,
> @@ -85,8 +85,7 @@ static inline void selinux_netlbl_sk_security_free(
> }
>
> static inline void selinux_netlbl_sk_security_reset(
> - struct sk_security_struct *ssec,
> - int family)
> + struct sk_security_struct *ssec)
> {
> return;
> }
> @@ -113,17 +112,17 @@ static inline int selinux_netlbl_conn_setsid(struct sock *sk,
> return 0;
> }
>
> -static inline void selinux_netlbl_inet_conn_established(struct sock *sk,
> - u16 family)
> +static inline int selinux_netlbl_inet_conn_request(struct request_sock *req,
> + u16 family)
> {
> - return;
> + return 0;
> }
> -static inline int selinux_netlbl_socket_post_create(struct socket *sock)
> +static inline void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
> {
> - return 0;
> + return;
> }
> -static inline int selinux_netlbl_inode_permission(struct inode *inode,
> - int mask)
> +static inline int selinux_netlbl_socket_post_create(struct sock *sk,
> + u16 family)
> {
> return 0;
> }
> diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
> index 350794a..2e98441 100644
> --- a/security/selinux/netlabel.c
> +++ b/security/selinux/netlabel.c
> @@ -100,41 +100,6 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
> }
>
> /**
> - * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
> - * @sk: the socket to label
> - *
> - * Description:
> - * Attempt to label a socket using the NetLabel mechanism. Returns zero values
> - * on success, negative values on failure.
> - *
> - */
> -static int selinux_netlbl_sock_setsid(struct sock *sk)
> -{
> - int rc;
> - struct sk_security_struct *sksec = sk->sk_security;
> - struct netlbl_lsm_secattr *secattr;
> -
> - if (sksec->nlbl_state != NLBL_REQUIRE)
> - return 0;
> -
> - secattr = selinux_netlbl_sock_genattr(sk);
> - if (secattr == NULL)
> - return -ENOMEM;
> - rc = netlbl_sock_setattr(sk, secattr);
> - switch (rc) {
> - case 0:
> - sksec->nlbl_state = NLBL_LABELED;
> - break;
> - case -EDESTADDRREQ:
> - sksec->nlbl_state = NLBL_REQSKB;
> - rc = 0;
> - break;
> - }
> -
> - return rc;
> -}
> -
> -/**
> * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
> *
> * Description:
> @@ -188,13 +153,9 @@ void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
> * The caller is responsibile for all the NetLabel sk_security_struct locking.
> *
> */
> -void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
> - int family)
> +void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec)
> {
> - if (family == PF_INET)
> - ssec->nlbl_state = NLBL_REQUIRE;
> - else
> - ssec->nlbl_state = NLBL_UNSET;
> + ssec->nlbl_state = NLBL_UNSET;
> }
>
> /**
> @@ -281,127 +242,86 @@ skbuff_setsid_return:
> }
>
> /**
> - * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection
> - * @sk: the new connection
> + * selinux_netlbl_inet_conn_request - Label an incoming stream connection
> + * @req: incoming connection request socket
> *
> * Description:
> - * A new connection has been established on @sk so make sure it is labeled
> - * correctly with the NetLabel susbsystem.
> + * A new incoming connection request is represented by @req, we need to label
> + * the new request_sock here and the stack will ensure the on-the-wire label
> + * will get preserved when a full sock is created once the connection handshake
> + * is complete. Returns zero on success, negative values on failure.
> *
> */
> -void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
> +int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
> {
> int rc;
> - struct sk_security_struct *sksec = sk->sk_security;
> - struct netlbl_lsm_secattr *secattr;
> - struct inet_sock *sk_inet = inet_sk(sk);
> - struct sockaddr_in addr;
> -
> - if (sksec->nlbl_state != NLBL_REQUIRE)
> - return;
> + struct netlbl_lsm_secattr secattr;
>
> - secattr = selinux_netlbl_sock_genattr(sk);
> - if (secattr == NULL)
> - return;
> + if (family != PF_INET)
> + return 0;
>
> - rc = netlbl_sock_setattr(sk, secattr);
> - switch (rc) {
> - case 0:
> - sksec->nlbl_state = NLBL_LABELED;
> - break;
> - case -EDESTADDRREQ:
> - /* no PF_INET6 support yet because we don't support any IPv6
> - * labeling protocols */
> - if (family != PF_INET) {
> - sksec->nlbl_state = NLBL_UNSET;
> - return;
> - }
> -
> - addr.sin_family = family;
> - addr.sin_addr.s_addr = sk_inet->daddr;
> - if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
> - secattr) != 0) {
> - /* we failed to label the connected socket (could be
> - * for a variety of reasons, the actual "why" isn't
> - * important here) so we have to go to our backup plan,
> - * labeling the packets individually in the netfilter
> - * local output hook. this is okay but we need to
> - * adjust the MSS of the connection to take into
> - * account any labeling overhead, since we don't know
> - * the exact overhead at this point we'll use the worst
> - * case value which is 40 bytes for IPv4 */
> - struct inet_connection_sock *sk_conn = inet_csk(sk);
> - sk_conn->icsk_ext_hdr_len += 40 -
> - (sk_inet->opt ? sk_inet->opt->optlen : 0);
> - sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
> -
> - sksec->nlbl_state = NLBL_REQSKB;
> - } else
> - sksec->nlbl_state = NLBL_CONNLABELED;
> - break;
> - default:
> - /* note that we are failing to label the socket which could be
> - * a bad thing since it means traffic could leave the system
> - * without the desired labeling, however, all is not lost as
> - * we have a check in selinux_netlbl_inode_permission() to
> - * pick up the pieces that we might drop here because we can't
> - * return an error code */
> - break;
> - }
> + netlbl_secattr_init(&secattr);
> + rc = security_netlbl_sid_to_secattr(req->secid, &secattr);
> + if (rc != 0)
> + goto inet_conn_request_return;
> + rc = netlbl_req_setattr(req, &secattr);
> +inet_conn_request_return:
> + netlbl_secattr_destroy(&secattr);
> + return rc;
> }
>
> /**
> - * selinux_netlbl_socket_post_create - Label a socket using NetLabel
> - * @sock: the socket to label
> + * selinux_netlbl_inet_csk_clone - Initialize the newly created sock
> + * @sk: the new sock
> *
> * Description:
> - * Attempt to label a socket using the NetLabel mechanism using the given
> - * SID. Returns zero values on success, negative values on failure.
> + * A new connection has been established using @sk, we've already labeled the
> + * socket via the request_sock struct in selinux_netlbl_inet_conn_request() but
> + * we need to set the NetLabel state here since we now have a sock structure.
> *
> */
> -int selinux_netlbl_socket_post_create(struct socket *sock)
> +void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
> {
> - return selinux_netlbl_sock_setsid(sock->sk);
> + struct sk_security_struct *sksec = sk->sk_security;
> +
> + if (family == PF_INET)
> + sksec->nlbl_state = NLBL_LABELED;
> + else
> + sksec->nlbl_state = NLBL_UNSET;
> }
>
> /**
> - * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
> - * @inode: the file descriptor's inode
> - * @mask: the permission mask
> + * selinux_netlbl_socket_post_create - Label a socket using NetLabel
> + * @sock: the socket to label
> + * @family: protocol family
> *
> * Description:
> - * Looks at a file's inode and if it is marked as a socket protected by
> - * NetLabel then verify that the socket has been labeled, if not try to label
> - * the socket now with the inode's SID. Returns zero on success, negative
> - * values on failure.
> + * Attempt to label a socket using the NetLabel mechanism using the given
> + * SID. Returns zero values on success, negative values on failure.
> *
> */
> -int selinux_netlbl_inode_permission(struct inode *inode, int mask)
> +int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
> {
> int rc;
> - struct sock *sk;
> - struct socket *sock;
> - struct sk_security_struct *sksec;
> + struct sk_security_struct *sksec = sk->sk_security;
> + struct netlbl_lsm_secattr *secattr;
>
> - if (!S_ISSOCK(inode->i_mode) ||
> - ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
> - return 0;
> - sock = SOCKET_I(inode);
> - sk = sock->sk;
> - if (sk == NULL)
> - return 0;
> - sksec = sk->sk_security;
> - if (sksec == NULL || sksec->nlbl_state != NLBL_REQUIRE)
> + if (family != PF_INET)
> return 0;
>
> - local_bh_disable();
> - bh_lock_sock_nested(sk);
> - if (likely(sksec->nlbl_state == NLBL_REQUIRE))
> - rc = selinux_netlbl_sock_setsid(sk);
> - else
> + secattr = selinux_netlbl_sock_genattr(sk);
> + if (secattr == NULL)
> + return -ENOMEM;
> + rc = netlbl_sock_setattr(sk, family, secattr);
> + switch (rc) {
> + case 0:
> + sksec->nlbl_state = NLBL_LABELED;
> + break;
> + case -EDESTADDRREQ:
> + sksec->nlbl_state = NLBL_REQSKB;
> rc = 0;
> - bh_unlock_sock(sk);
> - local_bh_enable();
> + break;
> + }
>
> return rc;
> }
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index fd20d15..23ad420 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1387,7 +1387,7 @@ static int smack_netlabel(struct sock *sk, int labeled)
> else {
> netlbl_secattr_init(&secattr);
> smack_to_secattr(ssp->smk_out, &secattr);
> - rc = netlbl_sock_setattr(sk, &secattr);
> + rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr);
> netlbl_secattr_destroy(&secattr);
> }
>
>
>
>
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists