summaryrefslogtreecommitdiff
path: root/security/selinux/netlabel.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/netlabel.c')
-rw-r--r--security/selinux/netlabel.c115
1 files changed, 79 insertions, 36 deletions
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index b22b7dafa0e3..f58701a7b728 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -68,6 +68,38 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
}
/**
+ * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
+ * @sk: the socket
+ *
+ * Description:
+ * Generate the NetLabel security attributes for a socket, making full use of
+ * the socket's attribute cache. Returns a pointer to the security attributes
+ * on success, NULL on failure.
+ *
+ */
+static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_secattr != NULL)
+ return sksec->nlbl_secattr;
+
+ secattr = netlbl_secattr_alloc(GFP_ATOMIC);
+ if (secattr == NULL)
+ return NULL;
+ rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
+ if (rc != 0) {
+ netlbl_secattr_free(secattr);
+ return NULL;
+ }
+ sksec->nlbl_secattr = secattr;
+
+ return secattr;
+}
+
+/**
* selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
* @sk: the socket to label
*
@@ -80,17 +112,15 @@ static int selinux_netlbl_sock_setsid(struct sock *sk)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
+ struct netlbl_lsm_secattr *secattr;
if (sksec->nlbl_state != NLBL_REQUIRE)
return 0;
- netlbl_secattr_init(&secattr);
-
- rc = security_netlbl_sid_to_secattr(sksec->sid, &secattr);
- if (rc != 0)
- goto sock_setsid_return;
- rc = netlbl_sock_setattr(sk, &secattr);
+ 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;
@@ -101,8 +131,6 @@ static int selinux_netlbl_sock_setsid(struct sock *sk)
break;
}
-sock_setsid_return:
- netlbl_secattr_destroy(&secattr);
return rc;
}
@@ -137,6 +165,20 @@ void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
}
/**
+ * selinux_netlbl_sk_security_free - Free the NetLabel fields
+ * @sssec: the sk_security_struct
+ *
+ * Description:
+ * Free all of the memory in the NetLabel fields of a sk_security_struct.
+ *
+ */
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
+{
+ if (ssec->nlbl_secattr != NULL)
+ netlbl_secattr_free(ssec->nlbl_secattr);
+}
+
+/**
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
* @ssec: the sk_security_struct
* @family: the socket family
@@ -209,7 +251,8 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
u32 sid)
{
int rc;
- struct netlbl_lsm_secattr secattr;
+ struct netlbl_lsm_secattr secattr_storage;
+ struct netlbl_lsm_secattr *secattr = NULL;
struct sock *sk;
/* if this is a locally generated packet check to see if it is already
@@ -219,16 +262,21 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
struct sk_security_struct *sksec = sk->sk_security;
if (sksec->nlbl_state != NLBL_REQSKB)
return 0;
+ secattr = sksec->nlbl_secattr;
+ }
+ if (secattr == NULL) {
+ secattr = &secattr_storage;
+ netlbl_secattr_init(secattr);
+ rc = security_netlbl_sid_to_secattr(sid, secattr);
+ if (rc != 0)
+ goto skbuff_setsid_return;
}
- netlbl_secattr_init(&secattr);
- rc = security_netlbl_sid_to_secattr(sid, &secattr);
- if (rc != 0)
- goto skbuff_setsid_return;
- rc = netlbl_skbuff_setattr(skb, family, &secattr);
+ rc = netlbl_skbuff_setattr(skb, family, secattr);
skbuff_setsid_return:
- netlbl_secattr_destroy(&secattr);
+ if (secattr == &secattr_storage)
+ netlbl_secattr_destroy(secattr);
return rc;
}
@@ -245,18 +293,18 @@ void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
+ struct netlbl_lsm_secattr *secattr;
struct inet_sock *sk_inet = inet_sk(sk);
struct sockaddr_in addr;
if (sksec->nlbl_state != NLBL_REQUIRE)
return;
- netlbl_secattr_init(&secattr);
- if (security_netlbl_sid_to_secattr(sksec->sid, &secattr) != 0)
- goto inet_conn_established_return;
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL)
+ return;
- rc = netlbl_sock_setattr(sk, &secattr);
+ rc = netlbl_sock_setattr(sk, secattr);
switch (rc) {
case 0:
sksec->nlbl_state = NLBL_LABELED;
@@ -266,13 +314,13 @@ void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
* labeling protocols */
if (family != PF_INET) {
sksec->nlbl_state = NLBL_UNSET;
- goto inet_conn_established_return;
+ return;
}
addr.sin_family = family;
addr.sin_addr.s_addr = sk_inet->daddr;
if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
- &secattr) != 0) {
+ 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,
@@ -300,10 +348,6 @@ void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
* return an error code */
break;
}
-
-inet_conn_established_return:
- netlbl_secattr_destroy(&secattr);
- return;
}
/**
@@ -468,13 +512,12 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
+ struct netlbl_lsm_secattr *secattr;
if (sksec->nlbl_state != NLBL_REQSKB &&
sksec->nlbl_state != NLBL_CONNLABELED)
return 0;
- netlbl_secattr_init(&secattr);
local_bh_disable();
bh_lock_sock_nested(sk);
@@ -487,17 +530,17 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
rc = 0;
goto socket_connect_return;
}
- rc = security_netlbl_sid_to_secattr(sksec->sid, &secattr);
- if (rc != 0)
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL) {
+ rc = -ENOMEM;
goto socket_connect_return;
- rc = netlbl_conn_setattr(sk, addr, &secattr);
- if (rc != 0)
- goto socket_connect_return;
- sksec->nlbl_state = NLBL_CONNLABELED;
+ }
+ rc = netlbl_conn_setattr(sk, addr, secattr);
+ if (rc == 0)
+ sksec->nlbl_state = NLBL_CONNLABELED;
socket_connect_return:
bh_unlock_sock(sk);
local_bh_enable();
- netlbl_secattr_destroy(&secattr);
return rc;
}