aboutsummaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/dummy.c64
-rw-r--r--security/seclvl.c18
-rw-r--r--security/selinux/hooks.c245
-rw-r--r--security/selinux/include/av_perm_to_string.h1
-rw-r--r--security/selinux/include/av_permissions.h1
-rw-r--r--security/selinux/include/objsec.h9
-rw-r--r--security/selinux/include/security.h2
-rw-r--r--security/selinux/include/selinux_netlabel.h119
-rw-r--r--security/selinux/include/xfrm.h43
-rw-r--r--security/selinux/ss/ebitmap.c140
-rw-r--r--security/selinux/ss/ebitmap.h6
-rw-r--r--security/selinux/ss/mls.c176
-rw-r--r--security/selinux/ss/mls.h41
-rw-r--r--security/selinux/ss/services.c613
-rw-r--r--security/selinux/xfrm.c216
15 files changed, 1534 insertions, 160 deletions
diff --git a/security/dummy.c b/security/dummy.c
index 58c6d399c84..aeee7056550 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -709,10 +709,10 @@ static int dummy_socket_create (int family, int type,
return 0;
}
-static void dummy_socket_post_create (struct socket *sock, int family, int type,
- int protocol, int kern)
+static int dummy_socket_post_create (struct socket *sock, int family, int type,
+ int protocol, int kern)
{
- return;
+ return 0;
}
static int dummy_socket_bind (struct socket *sock, struct sockaddr *address,
@@ -805,14 +805,38 @@ static inline void dummy_sk_free_security (struct sock *sk)
{
}
-static unsigned int dummy_sk_getsid(struct sock *sk, struct flowi *fl, u8 dir)
+static inline void dummy_sk_clone_security (const struct sock *sk, struct sock *newsk)
+{
+}
+
+static inline void dummy_sk_getsecid(struct sock *sk, u32 *secid)
+{
+}
+
+static inline void dummy_sock_graft(struct sock* sk, struct socket *parent)
+{
+}
+
+static inline int dummy_inet_conn_request(struct sock *sk,
+ struct sk_buff *skb, struct request_sock *req)
{
return 0;
}
+
+static inline void dummy_inet_csk_clone(struct sock *newsk,
+ const struct request_sock *req)
+{
+}
+
+static inline void dummy_req_classify_flow(const struct request_sock *req,
+ struct flowi *fl)
+{
+}
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
-static int dummy_xfrm_policy_alloc_security(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
+static int dummy_xfrm_policy_alloc_security(struct xfrm_policy *xp,
+ struct xfrm_user_sec_ctx *sec_ctx, struct sock *sk)
{
return 0;
}
@@ -831,7 +855,8 @@ static int dummy_xfrm_policy_delete_security(struct xfrm_policy *xp)
return 0;
}
-static int dummy_xfrm_state_alloc_security(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+static int dummy_xfrm_state_alloc_security(struct xfrm_state *x,
+ struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid)
{
return 0;
}
@@ -849,6 +874,23 @@ static int dummy_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
{
return 0;
}
+
+static int dummy_xfrm_state_pol_flow_match(struct xfrm_state *x,
+ struct xfrm_policy *xp, struct flowi *fl)
+{
+ return 1;
+}
+
+static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
+{
+ return 1;
+}
+
+static int dummy_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall)
+{
+ return 0;
+}
+
#endif /* CONFIG_SECURITY_NETWORK_XFRM */
static int dummy_register_security (const char *name, struct security_operations *ops)
{
@@ -1060,7 +1102,12 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, socket_getpeersec_dgram);
set_to_dummy_if_null(ops, sk_alloc_security);
set_to_dummy_if_null(ops, sk_free_security);
- set_to_dummy_if_null(ops, sk_getsid);
+ set_to_dummy_if_null(ops, sk_clone_security);
+ set_to_dummy_if_null(ops, sk_getsecid);
+ set_to_dummy_if_null(ops, sock_graft);
+ set_to_dummy_if_null(ops, inet_conn_request);
+ set_to_dummy_if_null(ops, inet_csk_clone);
+ set_to_dummy_if_null(ops, req_classify_flow);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
set_to_dummy_if_null(ops, xfrm_policy_alloc_security);
@@ -1071,6 +1118,9 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, xfrm_state_free_security);
set_to_dummy_if_null(ops, xfrm_state_delete_security);
set_to_dummy_if_null(ops, xfrm_policy_lookup);
+ set_to_dummy_if_null(ops, xfrm_state_pol_flow_match);
+ set_to_dummy_if_null(ops, xfrm_flow_state_match);
+ set_to_dummy_if_null(ops, xfrm_decode_session);
#endif /* CONFIG_SECURITY_NETWORK_XFRM */
#ifdef CONFIG_KEYS
set_to_dummy_if_null(ops, key_alloc);
diff --git a/security/seclvl.c b/security/seclvl.c
index c26dd7de047..8f6291991fb 100644
--- a/security/seclvl.c
+++ b/security/seclvl.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -197,26 +198,27 @@ static unsigned char hashedPassword[SHA1_DIGEST_SIZE];
static int
plaintext_to_sha1(unsigned char *hash, const char *plaintext, unsigned int len)
{
- struct crypto_tfm *tfm;
+ struct hash_desc desc;
struct scatterlist sg;
+ int err;
+
if (len > PAGE_SIZE) {
seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d "
"characters). Largest possible is %lu "
"bytes.\n", len, PAGE_SIZE);
return -EINVAL;
}
- tfm = crypto_alloc_tfm("sha1", CRYPTO_TFM_REQ_MAY_SLEEP);
- if (tfm == NULL) {
+ desc.tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc.tfm)) {
seclvl_printk(0, KERN_ERR,
"Failed to load transform for SHA1\n");
return -EINVAL;
}
sg_init_one(&sg, (u8 *)plaintext, len);
- crypto_digest_init(tfm);
- crypto_digest_update(tfm, &sg, 1);
- crypto_digest_final(tfm, hash);
- crypto_free_tfm(tfm);
- return 0;
+ desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ err = crypto_hash_digest(&desc, &sg, len, hash);
+ crypto_free_hash(desc.tfm);
+ return err;
}
/**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 5d1b8c73319..5a66c4c09f7 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -12,6 +12,8 @@
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* <dgoeddel@trustedcs.com>
+ * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
+ * Paul Moore, <paul.moore@hp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -74,6 +76,7 @@
#include "objsec.h"
#include "netif.h"
#include "xfrm.h"
+#include "selinux_netlabel.h"
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
@@ -269,17 +272,17 @@ static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
{
struct sk_security_struct *ssec;
- if (family != PF_UNIX)
- return 0;
-
ssec = kzalloc(sizeof(*ssec), priority);
if (!ssec)
return -ENOMEM;
ssec->sk = sk;
ssec->peer_sid = SECINITSID_UNLABELED;
+ ssec->sid = SECINITSID_UNLABELED;
sk->sk_security = ssec;
+ selinux_netlbl_sk_security_init(ssec, family);
+
return 0;
}
@@ -287,9 +290,6 @@ static void sk_free_security(struct sock *sk)
{
struct sk_security_struct *ssec = sk->sk_security;
- if (sk->sk_family != PF_UNIX)
- return;
-
sk->sk_security = NULL;
kfree(ssec);
}
@@ -2400,6 +2400,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
static int selinux_file_permission(struct file *file, int mask)
{
+ int rc;
struct inode *inode = file->f_dentry->d_inode;
if (!mask) {
@@ -2411,8 +2412,12 @@ static int selinux_file_permission(struct file *file, int mask)
if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
mask |= MAY_APPEND;
- return file_has_perm(current, file,
- file_mask_to_av(inode->i_mode, mask));
+ rc = file_has_perm(current, file,
+ file_mask_to_av(inode->i_mode, mask));
+ if (rc)
+ return rc;
+
+ return selinux_netlbl_inode_permission(inode, mask);
}
static int selinux_file_alloc_security(struct file *file)
@@ -3063,11 +3068,13 @@ out:
return err;
}
-static void selinux_socket_post_create(struct socket *sock, int family,
- int type, int protocol, int kern)
+static int selinux_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
{
+ int err = 0;
struct inode_security_struct *isec;
struct task_security_struct *tsec;
+ struct sk_security_struct *sksec;
u32 newsid;
isec = SOCK_INODE(sock)->i_security;
@@ -3078,7 +3085,15 @@ static void selinux_socket_post_create(struct socket *sock, int family,
isec->sid = kern ? SECINITSID_KERNEL : newsid;
isec->initialized = 1;
- return;
+ if (sock->sk) {
+ sksec = sock->sk->sk_security;
+ sksec->sid = isec->sid;
+ err = selinux_netlbl_socket_post_create(sock,
+ family,
+ isec->sid);
+ }
+
+ return err;
}
/* Range of port numbers used to automatically bind.
@@ -3259,7 +3274,13 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
- return socket_has_perm(current, sock, SOCKET__WRITE);
+ int rc;
+
+ rc = socket_has_perm(current, sock, SOCKET__WRITE);
+ if (rc)
+ return rc;
+
+ return selinux_netlbl_inode_permission(SOCK_INODE(sock), MAY_WRITE);
}
static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
@@ -3327,8 +3348,9 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
/* server child socket */
ssec = newsk->sk_security;
ssec->peer_sid = isec->sid;
-
- return 0;
+ err = security_sid_mls_copy(other_isec->sid, ssec->peer_sid, &ssec->sid);
+
+ return err;
}
static int selinux_socket_unix_may_send(struct socket *sock,
@@ -3354,11 +3376,29 @@ static int selinux_socket_unix_may_send(struct socket *sock,
}
static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
- struct avc_audit_data *ad, u32 sock_sid, u16 sock_class,
- u16 family, char *addrp, int len)
+ struct avc_audit_data *ad, u16 family, char *addrp, int len)
{
int err = 0;
u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
+ struct socket *sock;
+ u16 sock_class = 0;
+ u32 sock_sid = 0;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ sock = sk->sk_socket;
+ if (sock) {
+ struct inode *inode;
+ inode = SOCK_INODE(sock);
+ if (inode) {
+ struct inode_security_struct *isec;
+ isec = inode->i_security;
+ sock_sid = isec->sid;
+ sock_class = isec->sclass;
+ }
+ }
+ read_unlock_bh(&sk->sk_callback_lock);
+ if (!sock_sid)
+ goto out;
if (!skb->dev)
goto out;
@@ -3418,12 +3458,10 @@ out:
static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
u16 family;
- u16 sock_class = 0;
char *addrp;
int len, err = 0;
- u32 sock_sid = 0;
- struct socket *sock;
struct avc_audit_data ad;
+ struct sk_security_struct *sksec = sk->sk_security;
family = sk->sk_family;
if (family != PF_INET && family != PF_INET6)
@@ -3433,22 +3471,6 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
family = PF_INET;
- read_lock_bh(&sk->sk_callback_lock);
- sock = sk->sk_socket;
- if (sock) {
- struct inode *inode;
- inode = SOCK_INODE(sock);
- if (inode) {
- struct inode_security_struct *isec;
- isec = inode->i_security;
- sock_sid = isec->sid;
- sock_class = isec->sclass;
- }
- }
- read_unlock_bh(&sk->sk_callback_lock);
- if (!sock_sid)
- goto out;
-
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]";
ad.u.net.family = family;
@@ -3458,16 +3480,19 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
goto out;
if (selinux_compat_net)
- err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid,
- sock_class, family,
+ err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family,
addrp, len);
else
- err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET,
+ err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
goto out;
- err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
+ err = selinux_netlbl_sock_rcv_skb(sksec, skb, &ad);
+ if (err)
+ goto out;
+
+ err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
out:
return err;
}
@@ -3490,8 +3515,9 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
peer_sid = ssec->peer_sid;
}
else if (isec->sclass == SECCLASS_TCP_SOCKET) {
- peer_sid = selinux_socket_getpeer_stream(sock->sk);
-
+ peer_sid = selinux_netlbl_socket_getpeersec_stream(sock);
+ if (peer_sid == SECSID_NULL)
+ peer_sid = selinux_socket_getpeer_stream(sock->sk);
if (peer_sid == SECSID_NULL) {
err = -ENOPROTOOPT;
goto out;
@@ -3531,8 +3557,11 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
if (sock && (sock->sk->sk_family == PF_UNIX))
selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
- else if (skb)
- peer_secid = selinux_socket_getpeer_dgram(skb);
+ else if (skb) {
+ peer_secid = selinux_netlbl_socket_getpeersec_dgram(skb);
+ if (peer_secid == SECSID_NULL)
+ peer_secid = selinux_socket_getpeer_dgram(skb);
+ }
if (peer_secid == SECSID_NULL)
err = -EINVAL;
@@ -3551,22 +3580,86 @@ static void selinux_sk_free_security(struct sock *sk)
sk_free_security(sk);
}
-static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
+static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
{
- struct inode_security_struct *isec;
- u32 sock_sid = SECINITSID_ANY_SOCKET;
+ struct sk_security_struct *ssec = sk->sk_security;
+ struct sk_security_struct *newssec = newsk->sk_security;
+
+ newssec->sid = ssec->sid;
+ newssec->peer_sid = ssec->peer_sid;
+ selinux_netlbl_sk_clone_security(ssec, newssec);
+}
+
+static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
+{
if (!sk)
- return selinux_no_sk_sid(fl);
+ *secid = SECINITSID_ANY_SOCKET;
+ else {
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ *secid = sksec->sid;
+ }
+}
+
+static void selinux_sock_graft(struct sock* sk, struct socket *parent)
+{
+ struct inode_security_struct *isec = SOCK_INODE(parent)->i_security;
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ isec->sid = sksec->sid;
+
+ selinux_netlbl_sock_graft(sk, parent);
+}
+
+static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+ int err;
+ u32 newsid;
+ u32 peersid;
+
+ newsid = selinux_netlbl_inet_conn_request(skb, sksec->sid);
+ if (newsid != SECSID_NULL) {
+ req->secid = newsid;
+ return 0;
+ }
+
+ err = selinux_xfrm_decode_session(skb, &peersid, 0);
+ BUG_ON(err);
+
+ if (peersid == SECSID_NULL) {
+ req->secid = sksec->sid;
+ return 0;
+ }
+
+ err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
+ if (err)
+ return err;
+
+ req->secid = newsid;
+ return 0;
+}
+
+static void selinux_inet_csk_clone(struct sock *newsk,
+ const struct request_sock *req)
+{
+ struct sk_security_struct *newsksec = newsk->sk_security;
- read_lock_bh(&sk->sk_callback_lock);
- isec = get_sock_isec(sk);
+ newsksec->sid = req->secid;
+ /* NOTE: Ideally, we should also get the isec->sid for the
+ new socket in sync, but we don't have the isec available yet.
+ So we will wait until sock_graft to do it, by which
+ time it will have been created and available. */
- if (isec)
- sock_sid = isec->sid;
+ selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family);
+}
- read_unlock_bh(&sk->sk_callback_lock);
- return sock_sid;
+static void selinux_req_classify_flow(const struct request_sock *req,
+ struct flowi *fl)
+{
+ fl->secid = req->secid;
}
static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
@@ -3608,12 +3701,24 @@ out:
#ifdef CONFIG_NETFILTER
static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev,
- struct inode_security_struct *isec,
struct avc_audit_data *ad,
u16 family, char *addrp, int len)
{
- int err;
+ int err = 0;
u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
+ struct socket *sock;
+ struct inode *inode;
+ struct inode_security_struct *isec;
+
+ sock = sk->sk_socket;
+ if (!sock)
+ goto out;
+
+ inode = SOCK_INODE(sock);
+ if (!inode)
+ goto out;
+
+ isec = inode->i_security;
err = sel_netif_sids(dev, &if_sid, NULL);
if (err)
@@ -3678,26 +3783,16 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
char *addrp;
int len, err = 0;
struct sock *sk;
- struct socket *sock;
- struct inode *inode;
struct sk_buff *skb = *pskb;
- struct inode_security_struct *isec;
struct avc_audit_data ad;
struct net_device *dev = (struct net_device *)out;
+ struct sk_security_struct *sksec;
sk = skb->sk;
if (!sk)
goto out;
- sock = sk->sk_socket;
- if (!sock)
- goto out;
-
- inode = SOCK_INODE(sock);
- if (!inode)
- goto out;
-
- isec = inode->i_security;
+ sksec = sk->sk_security;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = dev->name;
@@ -3708,16 +3803,16 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
goto out;
if (selinux_compat_net)
- err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad,
+ err = selinux_ip_postroute_last_compat(sk, dev, &ad,
family, addrp, len);
else
- err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET,
+ err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
PACKET__SEND, &ad);
if (err)
goto out;
- err = selinux_xfrm_postroute_last(isec->sid, skb);
+ err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad);
out:
return err ? NF_DROP : NF_ACCEPT;
}
@@ -4618,7 +4713,12 @@ static struct security_operations selinux_ops = {
.socket_getpeersec_dgram = selinux_socket_getpeersec_dgram,
.sk_alloc_security = selinux_sk_alloc_security,
.sk_free_security = selinux_sk_free_security,
- .sk_getsid = selinux_sk_getsid_security,
+ .sk_clone_security = selinux_sk_clone_security,
+ .sk_getsecid = selinux_sk_getsecid,
+ .sock_graft = selinux_sock_graft,
+ .inet_conn_request = selinux_inet_conn_request,
+ .inet_csk_clone = selinux_inet_csk_clone,
+ .req_classify_flow = selinux_req_classify_flow,
#ifdef CONFIG_SECURITY_NETWORK_XFRM
.xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
@@ -4629,6 +4729,9 @@ static struct security_operations selinux_ops = {
.xfrm_state_free_security = selinux_xfrm_state_free,
.xfrm_state_delete_security = selinux_xfrm_state_delete,
.xfrm_policy_lookup = selinux_xfrm_policy_lookup,
+ .xfrm_state_pol_flow_match = selinux_xfrm_state_pol_flow_match,
+ .xfrm_flow_state_match = selinux_xfrm_flow_state_match,
+ .xfrm_decode_session = selinux_xfrm_decode_session,
#endif
#ifdef CONFIG_KEYS
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index 7c9b5838083..09fc8a2345e 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -241,6 +241,7 @@
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, "polmatch")
S_(SECCLASS_PACKET, PACKET__SEND, "send")
S_(SECCLASS_PACKET, PACKET__RECV, "recv")
S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index 69fd4b48202..81f4f526c8b 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -911,6 +911,7 @@
#define ASSOCIATION__SENDTO 0x00000001UL
#define ASSOCIATION__RECVFROM 0x00000002UL
#define ASSOCIATION__SETCONTEXT 0x00000004UL
+#define ASSOCIATION__POLMATCH 0x00000008UL
#define NETLINK_KOBJECT_UEVENT_SOCKET__IOCTL 0x00000001UL
#define NETLINK_KOBJECT_UEVENT_SOCKET__READ 0x00000002UL
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 940178865fc..0a39bfd1319 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -99,7 +99,16 @@ struct netif_security_struct {
struct sk_security_struct {
struct sock *sk; /* back pointer to sk object */
+ u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
+#ifdef CONFIG_NETLABEL
+ u16 sclass; /* sock security class */
+ enum { /* NetLabel state */
+ NLBL_UNSET = 0,
+ NLBL_REQUIRE,
+ NLBL_LABELED,
+ } nlbl_state;
+#endif
};
struct key_security_struct {
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 063af47bb23..911954a692f 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -78,6 +78,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen,
int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
+int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
+
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
#define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */
diff --git a/security/selinux/include/selinux_netlabel.h b/security/selinux/include/selinux_netlabel.h
new file mode 100644
index 00000000000..ecab4bddaaf
--- /dev/null
+++ b/security/selinux/include/selinux_netlabel.h
@@ -0,0 +1,119 @@
+/*
+ * SELinux interface to the NetLabel subsystem
+ *
+ * Author : Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _SELINUX_NETLABEL_H_
+#define _SELINUX_NETLABEL_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+
+#include "avc.h"
+#include "objsec.h"
+
+#ifdef CONFIG_NETLABEL
+void selinux_netlbl_cache_invalidate(void);
+int selinux_netlbl_socket_post_create(struct socket *sock,
+ int sock_family,
+ u32 sid);
+void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
+u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid);
+int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+ struct sk_buff *skb,
+ struct avc_audit_data *ad);
+u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock);
+u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb);
+void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
+ int family);
+void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec,
+ struct sk_security_struct *newssec);
+int selinux_netlbl_inode_permission(struct inode *inode, int mask);
+#else
+static inline void selinux_netlbl_cache_invalidate(void)
+{
+ return;
+}
+
+static inline int selinux_netlbl_socket_post_create(struct socket *sock,
+ int sock_family,
+ u32 sid)
+{
+ return 0;
+}
+
+static inline void selinux_netlbl_sock_graft(struct sock *sk,
+ struct socket *sock)
+{
+ return;
+}
+
+static inline u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb,
+ u32 sock_sid)
+{
+ return SECSID_NULL;
+}
+
+static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+ struct sk_buff *skb,
+ struct avc_audit_data *ad)
+{
+ return 0;
+}
+
+static inline u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock)
+{
+ return SECSID_NULL;
+}
+
+static inline u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb)
+{
+ return SECSID_NULL;
+}
+
+static inline void selinux_netlbl_sk_security_init(
+ struct sk_security_struct *ssec,
+ int family)
+{
+ return;
+}
+
+static inline void selinux_netlbl_sk_clone_security(
+ struct sk_security_struct *ssec,
+ struct sk_security_struct *newssec)
+{
+ return;
+}
+
+static inline int selinux_netlbl_inode_permission(struct inode *inode,
+ int mask)
+{
+ return 0;
+}
+#endif /* CONFIG_NETLABEL */
+
+#endif
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index c96498a10eb..81eb5989016 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -2,18 +2,25 @@
* SELinux support for the XFRM LSM hooks
*
* Author : Trent Jaeger, <jaegert@us.ibm.com>
+ * Updated : Venkat Yekkirala, <vyekkirala@TrustedCS.com>
*/
#ifndef _SELINUX_XFRM_H_
#define _SELINUX_XFRM_H_
-int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx);
+int selinux_xfrm_policy_alloc(struct xfrm_policy *xp,
+ struct xfrm_user_sec_ctx *sec_ctx, struct sock *sk);
int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new);
void selinux_xfrm_policy_free(struct xfrm_policy *xp);
int selinux_xfrm_policy_delete(struct xfrm_policy *xp);
-int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx);
+int selinux_xfrm_state_alloc(struct xfrm_state *x,
+ struct xfrm_user_sec_ctx *sec_ctx, struct xfrm_sec_ctx *pol, u32 secid);
void selinux_xfrm_state_free(struct xfrm_state *x);
int selinux_xfrm_state_delete(struct xfrm_state *x);
-int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir);
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
+ struct xfrm_policy *xp, struct flowi *fl);
+int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm);
+
/*
* Extract the security blob from the sock (it's actually on the socket)
@@ -26,30 +33,23 @@ static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
return SOCK_INODE(sk->sk_socket)->i_security;
}
-
-static inline u32 selinux_no_sk_sid(struct flowi *fl)
-{
- /* NOTE: no sock occurs on ICMP reply, forwards, ... */
- /* icmp_reply: authorize as kernel packet */
- if (fl && fl->proto == IPPROTO_ICMP) {
- return SECINITSID_KERNEL;
- }
-
- return SECINITSID_ANY_SOCKET;
-}
-
#ifdef CONFIG_SECURITY_NETWORK_XFRM
-int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb);
-int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb);
+int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
+ struct avc_audit_data *ad);
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad);
u32 selinux_socket_getpeer_stream(struct sock *sk);
u32 selinux_socket_getpeer_dgram(struct sk_buff *skb);
+int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
#else
-static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad)
{
return 0;
}
-static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad)
{
return 0;
}
@@ -63,6 +63,11 @@ static inline int selinux_socket_getpeer_dgram(struct sk_buff *skb)
{
return SECSID_NULL;
}
+static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
+{
+ *sid = SECSID_NULL;
+ return 0;
+}
#endif
#endif /* _SELINUX_XFRM_H_ */
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index 47024a6e184..cfed1d30fa6 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -3,6 +3,14 @@
*
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
+/*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added ebitmap_export() and ebitmap_import()
+ *
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ */
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
@@ -59,6 +67,138 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
return 0;
}
+/**
+ * ebitmap_export - Export an ebitmap to a unsigned char bitmap string
+ * @src: the ebitmap to export
+ * @dst: the resulting bitmap string
+ * @dst_len: length of dst in bytes
+ *
+ * Description:
+ * Allocate a buffer at least src->highbit bits long and export the extensible
+ * bitmap into the buffer. The bitmap string will be in little endian format,
+ * i.e. LSB first. The value returned in dst_len may not the true size of the
+ * buffer as the length of the buffer is rounded up to a multiple of MAPTYPE.
+ * The caller must free the buffer when finished. Returns zero on success,
+ * negative values on failure.
+ *
+ */
+int ebitmap_export(const struct ebitmap *src,
+ unsigned char **dst,
+ size_t *dst_len)
+{
+ size_t bitmap_len;
+ unsigned char *bitmap;
+ struct ebitmap_node *iter_node;
+ MAPTYPE node_val;
+ size_t bitmap_byte;
+ unsigned char bitmask;
+
+ bitmap_len = src->highbit / 8;
+ if (src->highbit % 7)
+ bitmap_len += 1;
+ if (bitmap_len == 0)
+ return -EINVAL;
+
+ bitmap = kzalloc((bitmap_len & ~(sizeof(MAPTYPE) - 1)) +
+ sizeof(MAPTYPE),
+ GFP_ATOMIC);
+ if (bitmap == NULL)
+ return -ENOMEM;
+
+ iter_node = src->node;
+ do {
+ bitmap_byte = iter_node->startbit / 8;
+ bitmask = 0x80;
+ node_val = iter_node->map;
+ do {
+ if (bitmask == 0) {
+ bitmap_byte++;
+ bitmask = 0x80;
+ }
+ if (node_val & (MAPTYPE)0x01)
+ bitmap[bitmap_byte] |= bitmask;
+ node_val >>= 1;
+ bitmask >>= 1;
+ } while (node_val > 0);
+ iter_node = iter_node->next;
+ } while (iter_node);
+
+ *dst = bitmap;
+ *dst_len = bitmap_len;
+ return 0;
+}
+
+/**
+ * ebitmap_import - Import an unsigned char bitmap string into an ebitmap
+ * @src: the bitmap string
+ * @src_len: the bitmap length in bytes
+ * @dst: the empty ebitmap
+ *
+ * Description:
+ * This function takes a little endian bitmap string in src and imports it into
+ * the ebitmap pointed to by dst. Returns zero on success, negative values on
+ * failure.
+ *
+ */
+int ebitmap_import(const unsigned char *src,
+ size_t src_len,
+ struct ebitmap *dst)
+{
+ size_t src_off = 0;
+ size_t node_limit;
+ struct ebitmap_node *node_new;
+ struct ebitmap_node *node_last = NULL;
+ u32 i_byte;
+ u32 i_bit;
+ unsigned char src_byte;
+
+ while (src_off < src_len) {
+ if (src_len - src_off >= sizeof(MAPTYPE)) {
+ if (*(MAPTYPE *)&src[src_off] == 0) {
+ src_off += sizeof(MAPTYPE);
+ continue;
+ }
+ node_limit = sizeof(MAPTYPE);
+ } else {
+ for (src_byte = 0, i_byte = src_off;
+ i_byte < src_len && src_byte == 0;
+ i_byte++)
+ src_byte |= src[i_byte];
+ if (src_byte == 0)
+ break;
+ node_limit = src_len - src_off;
+ }
+
+ node_new = kzalloc(sizeof(*node_new), GFP_ATOMIC);
+ if (unlikely(node_new == NULL)) {
+ ebitmap_destroy(dst);
+ return -ENOMEM;
+ }
+ node_new->startbit = src_off * 8;
+ for (i_byte = 0; i_byte < node_limit; i_byte++) {
+ src_byte = src[src_off++];
+ for (i_bit = i_byte * 8; src_byte != 0; i_bit++) {
+ if (src_byte & 0x80)
+ node_new->map |= MAPBIT << i_bit;
+ src_byte <<= 1;
+ }
+ }
+
+ if (node_last != NULL)
+ node_last->next = node_new;
+ else
+ dst->node = node_new;
+ node_last = node_new;
+ }
+
+ if (likely(node_last != NULL))
+ dst->highbit = node_last->startbit + MAPSIZE;
+ else
+ ebitmap_init(dst);
+
+ return 0;
+}
+
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
{
struct ebitmap_node *n1, *n2;
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
index 8bf41055a6c..da2d4651b10 100644
--- a/security/selinux/ss/ebitmap.h
+++ b/security/selinux/ss/ebitmap.h
@@ -69,6 +69,12 @@ static inline int ebitmap_node_get_bit(struct ebitmap_node * n,
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
+int ebitmap_export(const struct ebitmap *src,
+ unsigned char **dst,
+ size_t *dst_len);
+int ebitmap_import(const unsigned char *src,
+ size_t src_len,
+ struct ebitmap *dst);
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);
int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 7bc5b6440f7..119bd6078ba 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -10,6 +10,13 @@
*
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
+/*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support to import/export the MLS label
+ *
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ */
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -212,26 +219,6 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
}
/*
- * Copies the MLS range from `src' into `dst'.
- */
-static inline int mls_copy_context(struct context *dst,
- struct context *src)
-{
- int l, rc = 0;
-
- /* Copy the MLS range from the source context */
- for (l = 0; l < 2; l++) {
- dst->range.level[l].sens = src->range.level[l].sens;
- rc = ebitmap_cpy(&dst->range.level[l].cat,
- &src->range.level[l].cat);
- if (rc)
- break;
- }
-
- return rc;
-}
-
-/*
* Set the MLS fields in the security context structure
* `context' based on the string representation in
* the string `*scontext'. Update `*scontext' to
@@ -585,3 +572,152 @@ int mls_compute_sid(struct context *scontext,
return -EINVAL;
}
+/**
+ * mls_export_lvl - Export the MLS sensitivity levels
+ * @context: the security context
+ * @low: the low sensitivity level
+ * @high: the high sensitivity level
+ *
+ * Description:
+ * Given the security context copy the low MLS sensitivity level into lvl_low
+ * and the high sensitivity level in lvl_high. The MLS levels are only
+ * exported if the pointers are not NULL, if they are NULL then that level is
+ * not exported.
+ *
+ */
+void mls_export_lvl(const struct context *context, u32 *low, u32 *high)
+{
+ if (!selinux_mls_enabled)
+ return;
+
+ if (low != NULL)
+ *low = context->range.level[0].sens - 1;
+ if (high != NULL)
+ *high = context->range.level[1].sens - 1;
+}
+
+/**
+ * mls_import_lvl - Import the MLS sensitivity levels
+ * @context: the security context
+ * @low: the low sensitivity level
+ * @high: the high sensitivity level
+ *
+ * Description:
+ * Given the security context and the two sensitivty levels, set the MLS levels
+ * in the context according the two given as parameters. Returns zero on
+ * success, negative values on failure.
+ *
+ */
+void mls_import_lvl(struct context *context, u32 low, u32 high)
+{
+ if (!selinux_mls_enabled)
+ return;
+
+ context->range.level[0].sens = low + 1;
+ context->range.level[1].sens = high + 1;
+}
+
+/**
+ * mls_export_cat - Export the MLS categories
+ * @context: the security context
+ * @low: the low category
+ * @low_len: length of the cat_low bitmap in bytes
+ * @high: the high category
+ * @high_len: length of the cat_high bitmap in bytes
+ *
+ * Description:
+ * Given the security context export the low MLS category bitmap into cat_low
+ * and the high category bitmap into cat_high. The MLS categories are only
+ * exported if the pointers are not NULL, if they are NULL then that level is
+ * not exported. The caller is responsibile for freeing the memory when
+ * finished. Returns zero on success, negative values on failure.
+ *
+ */
+int mls_export_cat(const struct context *context,
+ unsigned char **low,
+ size_t *low_len,
+ unsigned char **high,
+ size_t *high_len)
+{
+ int rc = -EPERM;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ if (low != NULL) {
+ rc = ebitmap_export(&context->range.level[0].cat,
+ low,
+ low_len);
+ if (rc != 0)
+ goto export_cat_failure;
+ }
+ if (high != NULL) {
+ rc = ebitmap_export(&context->range.level[1].cat,
+ high,
+ high_len);
+ if (rc != 0)
+ goto export_cat_failure;
+ }
+
+ return 0;
+
+export_cat_failure:
+ if (low != NULL)
+ kfree(*low);
+ if (high != NULL)
+ kfree(*high);
+ return rc;
+}
+
+/**
+ * mls_import_cat - Import the MLS categories
+ * @context: the security context
+ * @low: the low category
+ * @low_len: length of the cat_low bitmap in bytes
+ * @high: the high category
+ * @high_len: length of the cat_high bitmap in bytes
+ *
+ * Description:
+ * Given the security context and the two category bitmap strings import the
+ * categories into the security context. The MLS categories are only imported
+ * if the pointers are not NULL, if they are NULL they are skipped. Returns
+ * zero on success, negative values on failure.
+ *
+ */
+int mls_import_cat(struct context *context,
+ const unsigned char *low,
+ size_t low_len,
+ const unsigned char *high,
+ size_t high_len)
+{
+ int rc = -EPERM;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ if (low != NULL) {
+ rc = ebitmap_import(low,
+ low_len,
+ &context->range.level[0].cat);
+ if (rc != 0)
+ goto import_cat_failure;
+ }
+ if (high != NULL) {
+ if (high == low)
+ rc = ebitmap_cpy(&context->range.level[1].cat,
+ &context->range.level[0].cat);
+ else
+ rc = ebitmap_import(high,
+ high_len,
+ &context->range.level[1].cat);
+ if (rc != 0)
+ goto import_cat_failure;
+ }
+
+ return 0;
+
+import_cat_failure:
+ ebitmap_destroy(&context->range.level[0].cat);
+ ebitmap_destroy(&context->range.level[1].cat);
+ return rc;
+}
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index fbb42f07dd7..df6032c6d49 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -10,6 +10,13 @@
*
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*/
+/*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support to import/export the MLS label
+ *
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ */
#ifndef _SS_MLS_H_
#define _SS_MLS_H_
@@ -17,6 +24,26 @@
#include "context.h"
#include "policydb.h"
+/*
+ * Copies the MLS range from `src' into `dst'.
+ */
+static inline int mls_copy_context(struct context *dst,
+ struct context *src)
+{
+ int l, rc = 0;
+
+ /* Copy the MLS range from the source context */
+ for (l = 0; l < 2; l++) {
+ dst->range.level[l].sens = src->range.level[l].sens;
+ rc = ebitmap_cpy(&dst->range.level[l].cat,
+ &src->range.level[l].cat);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
int mls_compute_context_len(struct context *context);
void mls_sid_to_context(struct context *context, char **scontext);
int mls_context_isvalid(struct policydb *p, struct context *c);
@@ -42,5 +69,19 @@ int mls_compute_sid(struct context *scontext,
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
struct context *usercon);
+void mls_export_lvl(const struct context *context, u32 *low, u32 *high);
+void mls_import_lvl(struct context *context, u32 low, u32 high);
+
+int mls_export_cat(const struct context *context,
+ unsigned char **low,
+ size_t *low_len,
+ unsigned char **high,
+ size_t *high_len);
+int mls_import_cat(struct context *context,
+ const unsigned char *low,
+ size_t low_len,
+ const unsigned char *high,
+ size_t high_len);
+
#endif /* _SS_MLS_H */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 85e42988439..7eb69a602d8 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -13,6 +13,11 @@
*
* Added conditional policy language extensions
*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support for NetLabel
+ *
+ * Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
@@ -29,6 +34,8 @@
#include <linux/sched.h>
#include <linux/audit.h>
#include <linux/mutex.h>
+#include <net/sock.h>
+#include <net/netlabel.h>
#include "flask.h"
#include "avc.h"
@@ -40,6 +47,8 @@
#include "services.h"
#include "conditional.h"
#include "mls.h"
+#include "objsec.h"
+#include "selinux_netlabel.h"
extern void selnl_notify_policyload(u32 seqno);
unsigned int policydb_loaded_version;
@@ -1241,6 +1250,7 @@ int security_load_policy(void *data, size_t len)
selinux_complete_init();
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
+ selinux_netlbl_cache_invalidate();
return 0;
}
@@ -1295,6 +1305,7 @@ int security_load_policy(void *data, size_t len)
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
+ selinux_netlbl_cache_invalidate();
return 0;
@@ -1817,6 +1828,75 @@ out:
return rc;
}
+/*
+ * security_sid_mls_copy() - computes a new sid based on the given
+ * sid and the mls portion of mls_sid.
+ */
+int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
+{
+ struct context *context1;
+ struct context *context2;
+ struct context newcon;
+ char *s;
+ u32 len;
+ int rc = 0;
+
+ if (!ss_initialized || !selinux_mls_enabled) {
+ *new_sid = sid;
+ goto out;
+ }
+
+ context_init(&newcon);
+
+ POLICY_RDLOCK;
+ context1 = sidtab_search(&sidtab, sid);
+ if (!context1) {
+ printk(KERN_ERR "security_sid_mls_copy: unrecognized SID "
+ "%d\n", sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ context2 = sidtab_search(&sidtab, mls_sid);
+ if (!context2) {
+ printk(KERN_ERR "security_sid_mls_copy: unrecognized SID "
+ "%d\n", mls_sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ newcon.user = context1->user;
+ newcon.role = context1->role;
+ newcon.type = context1->type;
+ rc = mls_copy_context(&newcon, context2);
+ if (rc)
+ goto out_unlock;
+
+
+ /* Check the validity of the new context. */
+ if (!policydb_context_isvalid(&policydb, &newcon)) {
+ rc = convert_context_handle_invalid_context(&newcon);
+ if (rc)
+ goto bad;
+ }
+
+ rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid);
+ goto out_unlock;
+
+bad:
+ if (!context_struct_to_string(&newcon, &s, &len)) {
+ audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "security_sid_mls_copy: invalid context %s", s);
+ kfree(s);
+ }
+
+out_unlock:
+ POLICY_RDUNLOCK;
+ context_destroy(&newcon);
+out:
+ return rc;
+}
+
struct selinux_audit_rule {
u32 au_seqno;
struct context au_ctxt;
@@ -2064,3 +2144,536 @@ void selinux_audit_set_callback(int (*callback)(void))
{
aurule_callback = callback;
}
+
+#ifdef CONFIG_NETLABEL
+/*
+ * This is the structure we store inside the NetLabel cache block.
+ */
+#define NETLBL_CACHE(x) ((struct netlbl_cache *)(x))
+#define NETLBL_CACHE_T_NONE 0
+#define NETLBL_CACHE_T_SID 1
+#define NETLBL_CACHE_T_MLS 2
+struct netlbl_cache {
+ u32 type;
+ union {
+ u32 sid;
+ struct mls_range mls_label;
+ } data;
+};
+
+/**
+ * selinux_netlbl_cache_free - Free the NetLabel cached data
+ * @data: the data to free
+ *
+ * Description:
+ * This function is intended to be used as the free() callback inside the
+ * netlbl_lsm_cache structure.
+ *
+ */
+static void selinux_netlbl_cache_free(const void *data)
+{
+ struct netlbl_cache *cache = NETLBL_CACHE(data);
+ switch (cache->type) {
+ case NETLBL_CACHE_T_MLS:
+ ebitmap_destroy(&cache->data.mls_label.level[0].cat);
+ break;
+ }
+ kfree(data);
+}
+
+/**
+ * selinux_netlbl_cache_add - Add an entry to the NetLabel cache
+ * @skb: the packet
+ * @ctx: the SELinux context
+ *
+ * Description:
+ * Attempt to cache the context in @ctx, which was derived from the packet in
+ * @skb, in the NetLabel subsystem cache.
+ *
+ */
+static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx)
+{
+ struct netlbl_cache *cache = NULL;
+ struct netlbl_lsm_secattr secattr;
+
+ netlbl_secattr_init(&secattr);
+
+ cache = kzalloc(sizeof(*cache), GFP_ATOMIC);
+ if (cache == NULL)
+ goto netlbl_cache_add_failure;
+ secattr.cache.free = selinux_netlbl_cache_free;
+ secattr.cache.data = (void *)cache;
+
+ cache->type = NETLBL_CACHE_T_MLS;
+ if (ebitmap_cpy(&cache->data.mls_label.level[0].cat,
+ &ctx->range.level[0].cat) != 0)
+ goto netlbl_cache_add_failure;
+ cache->data.mls_label.level[1].cat.highbit =
+ cache->data.mls_label.level[0].cat.highbit;
+ cache->data.mls_label.level[1].cat.node =
+ cache->data.mls_label.level[0].cat.node;
+ cache->data.mls_label.level[0].sens = ctx->range.level[0].sens;
+ cache->data.mls_label.level[1].sens = ctx->range.level[0].sens;
+
+ if (netlbl_cache_add(skb, &secattr) != 0)
+ goto netlbl_cache_add_failure;
+
+ return;
+
+netlbl_cache_add_failure:
+ netlbl_secattr_destroy(&secattr, 1);
+}
+
+/**
+ * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
+ *
+ * Description:
+ * Invalidate the NetLabel security attribute mapping cache.
+ *
+ */
+void selinux_netlbl_cache_invalidate(void)
+{
+ netlbl_cache_invalidate();
+}
+
+/**
+ * selinux_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
+ * @skb: the network packet
+ * @secattr: the NetLabel packet security attributes
+ * @base_sid: the SELinux SID to use as a context for MLS only attributes
+ * @sid: the SELinux SID
+ *
+ * Description:
+ * Convert the given NetLabel packet security attributes in @secattr into a
+ * SELinux SID. If the @secattr field does not contain a full SELinux
+ * SID/context then use the context in @base_sid as the foundation. If @skb
+ * is not NULL attempt to cache as much data as possibile. Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb,
+ struct netlbl_lsm_secattr *secattr,
+ u32 base_sid,
+ u32 *sid)
+{
+ int rc = -EIDRM;
+ struct context *ctx;
+ struct context ctx_new;
+ struct netlbl_cache *cache;
+
+ POLICY_RDLOCK;
+
+ if (secattr->cache.data) {
+ cache = NETLBL_CACHE(secattr->cache.data);
+ switch (cache->type) {
+ case NETLBL_CACHE_T_SID:
+ *sid = cache->data.sid;
+ rc = 0;
+ break;
+ case NETLBL_CACHE_T_MLS:
+ ctx = sidtab_search(&sidtab, base_sid);
+ if (ctx == NULL)
+ goto netlbl_secattr_to_sid_return;
+
+ ctx_new.user = ctx->user;
+ ctx_new.role = ctx->role;
+ ctx_new.type = ctx->type;
+ ctx_new.range.level[0].sens =
+ cache->data.mls_label.level[0].sens;
+ ctx_new.range.level[0].cat.highbit =
+ cache->data.mls_label.level[0].cat.highbit;
+ ctx_new.range.level[0].cat.node =
+ cache->data.mls_label.level[0].cat.node;
+ ctx_new.range.level[1].sens =
+ cache->data.mls_label.level[1].sens;
+ ctx_new.range.level[1].cat.highbit =
+ cache->data.mls_label.level[1].cat.highbit;
+ ctx_new.range.level[1].cat.node =
+ cache->data.mls_label.level[1].cat.node;
+
+ rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+ break;
+ default:
+ goto netlbl_secattr_to_sid_return;
+ }
+ } else if (secattr->mls_lvl_vld) {
+ ctx = sidtab_search(&sidtab, base_sid);
+ if (ctx == NULL)
+ goto netlbl_secattr_to_sid_return;
+
+ ctx_new.user = ctx->user;
+ ctx_new.role = ctx->role;
+ ctx_new.type = ctx->type;
+ mls_import_lvl(&ctx_new, secattr->mls_lvl, secattr->mls_lvl);
+ if (secattr->mls_cat) {
+ if (mls_import_cat(&ctx_new,
+ secattr->mls_cat,
+ secattr->mls_cat_len,
+ NULL,
+ 0) != 0)
+ goto netlbl_secattr_to_sid_return;
+ ctx_new.range.level[1].cat.highbit =
+ ctx_new.range.level[0].cat.highbit;
+ ctx_new.range.level[1].cat.node =
+ ctx_new.range.level[0].cat.node;
+ } else {
+ ebitmap_init(&ctx_new.range.level[0].cat);
+ ebitmap_init(&ctx_new.range.level[1].cat);
+ }
+ if (mls_context_isvalid(&policydb, &ctx_new) != 1)
+ goto netlbl_secattr_to_sid_return_cleanup;
+
+ rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+ if (rc != 0)
+ goto netlbl_secattr_to_sid_return_cleanup;
+
+ if (skb != NULL)
+ selinux_netlbl_cache_add(skb, &ctx_new);
+ ebitmap_destroy(&ctx_new.range.level[0].cat);
+ } else {
+ *sid = SECINITSID_UNLABELED;
+ rc = 0;
+ }
+
+netlbl_secattr_to_sid_return:
+ POLICY_RDUNLOCK;
+ return rc;
+netlbl_secattr_to_sid_return_cleanup:
+ ebitmap_destroy(&ctx_new.range.level[0].cat);
+ goto netlbl_secattr_to_sid_return;
+}
+
+/**
+ * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
+ * @skb: the packet
+ * @base_sid: the SELinux SID to use as a context for MLS only attributes
+ * @sid: the SID
+ *
+ * Description:
+ * Call the NetLabel mechanism to get the security attributes of the given
+ * packet and use those attributes to determine the correct context/SID to
+ * assign to the packet. Returns zero on success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
+ u32 base_sid,
+ u32 *sid)
+{
+ int rc;
+ struct netlbl_lsm_secattr secattr;
+
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_skbuff_getattr(skb, &secattr);
+ if (rc == 0)
+ rc = selinux_netlbl_secattr_to_sid(skb,
+ &secattr,
+ base_sid,
+ sid);
+ netlbl_secattr_destroy(&secattr, 0);
+
+ return rc;
+}
+
+/**
+ * selinux_netlbl_socket_setsid - Label a socket using the NetLabel mechanism
+ * @sock: the socket to label
+ * @sid: the SID to use
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism using the given
+ * SID. Returns zero values on success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid)
+{
+ int rc = -ENOENT;
+ struct sk_security_struct *sksec = sock->sk->sk_security;
+ struct netlbl_lsm_secattr secattr;
+ struct context *ctx;
+
+ if (!ss_initialized)
+ return 0;
+
+ POLICY_RDLOCK;
+
+ ctx = sidtab_search(&sidtab, sid);
+ if (ctx == NULL)
+ goto netlbl_socket_setsid_return;
+
+ netlbl_secattr_init(&secattr);
+ secattr.domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
+ GFP_ATOMIC);
+ mls_export_lvl(ctx, &secattr.mls_lvl, NULL);
+ secattr.mls_lvl_vld = 1;
+ mls_export_cat(ctx,
+ &secattr.mls_cat,
+ &secattr.mls_cat_len,
+ NULL,
+ NULL);
+
+ rc = netlbl_socket_setattr(sock, &secattr);
+ if (rc == 0)
+ sksec->nlbl_state = NLBL_LABELED;
+
+ netlbl_secattr_destroy(&secattr, 0);
+
+netlbl_socket_setsid_return:
+ POLICY_RDUNLOCK;
+ return rc;
+}
+
+/**
+ * selinux_netlbl_sk_security_init - Setup the NetLabel fields
+ * @ssec: the sk_security_struct
+ * @family: the socket family
+ *
+ * Description:
+ * Called when a new sk_security_struct is allocated to initialize the NetLabel
+ * fields.
+ *
+ */
+void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
+ int family)
+{
+ if (family == PF_INET)
+ ssec->nlbl_state = NLBL_REQUIRE;
+ else
+ ssec->nlbl_state = NLBL_UNSET;
+}
+
+/**
+ * selinux_netlbl_sk_clone_security - Copy the NetLabel fields
+ * @ssec: the original sk_security_struct
+ * @newssec: the cloned sk_security_struct
+ *
+ * Description:
+ * Clone the NetLabel specific sk_security_struct fields from @ssec to
+ * @newssec.
+ *
+ */
+void selinux_netlbl_sk_clone_security(struct sk_security_struct *ssec,
+ struct sk_security_struct *newssec)
+{
+ newssec->sclass = ssec->sclass;
+ if (ssec->nlbl_state != NLBL_UNSET)
+ newssec->nlbl_state = NLBL_REQUIRE;
+ else
+ newssec->nlbl_state = NLBL_UNSET;
+}
+
+/**
+ * selinux_netlbl_socket_post_create - Label a socket using NetLabel
+ * @sock: the socket to label
+ * @sock_family: the socket family
+ * @sid: the SID to use
+ *
+ * Description:
+ * 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_socket_post_create(struct socket *sock,
+ int sock_family,
+ u32 sid)
+{
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+ struct sk_security_struct *sksec = sock->sk->sk_security;
+
+ sksec->sclass = isec->sclass;
+
+ if (sock_family != PF_INET)
+ return 0;
+
+ sksec->nlbl_state = NLBL_REQUIRE;
+ return selinux_netlbl_socket_setsid(sock, sid);
+}
+
+/**
+ * selinux_netlbl_sock_graft - Netlabel the new socket
+ * @sk: the new connection
+ * @sock: the new socket
+ *
+ * Description:
+ * The connection represented by @sk is being grafted onto @sock so set the
+ * socket's NetLabel to match the SID of @sk.
+ *
+ */
+void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
+{
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ sksec->sclass = isec->sclass;
+
+ if (sk->sk_family != PF_INET)
+ return;
+
+ sksec->nlbl_state = NLBL_REQUIRE;
+ sksec->peer_sid = sksec->sid;
+
+ /* Try to set the NetLabel on the socket to save time later, if we fail
+ * here we will pick up the pieces in later calls to
+ * selinux_netlbl_inode_permission(). */
+ selinux_netlbl_socket_setsid(sock, sksec->sid);
+}
+
+/**
+ * selinux_netlbl_inet_conn_request - Handle a new connection request
+ * @skb: the packet
+ * @sock_sid: the SID of the parent socket
+ *
+ * Description:
+ * If present, use the security attributes of the packet in @skb and the
+ * parent sock's SID to arrive at a SID for the new child sock. Returns the
+ * SID of the connection or SECSID_NULL on failure.
+ *
+ */
+u32 selinux_netlbl_inet_conn_request(struct sk_buff *skb, u32 sock_sid)
+{
+ int rc;
+ u32 peer_sid;
+
+ rc = selinux_netlbl_skbuff_getsid(skb, sock_sid, &peer_sid);
+ if (rc != 0)
+ return SECSID_NULL;
+
+ if (peer_sid == SECINITSID_UNLABELED)
+ return SECSID_NULL;
+
+ return peer_sid;
+}
+
+/**
+ * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
+ * @inode: the file descriptor's inode
+ * @mask: the permission mask
+ *
+ * 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.
+ *
+ */
+int selinux_netlbl_inode_permission(struct inode *inode, int mask)
+{
+ int rc;
+ struct inode_security_struct *isec;
+ struct sk_security_struct *sksec;
+ struct socket *sock;
+
+ if (!S_ISSOCK(inode->i_mode))
+ return 0;
+
+ sock = SOCKET_I(inode);
+ isec = inode->i_security;
+ sksec = sock->sk->sk_security;
+ down(&isec->sem);
+ if (unlikely(sksec->nlbl_state == NLBL_REQUIRE &&
+ (mask & (MAY_WRITE | MAY_APPEND)))) {
+ lock_sock(sock->sk);
+ rc = selinux_netlbl_socket_setsid(sock, sksec->sid);
+ release_sock(sock->sk);
+ } else
+ rc = 0;
+ up(&isec->sem);
+
+ return rc;
+}
+
+/**
+ * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
+ * @sksec: the sock's sk_security_struct
+ * @skb: the packet
+ * @ad: the audit data
+ *
+ * Description:
+ * Fetch the NetLabel security attributes from @skb and perform an access check
+ * against the receiving socket. Returns zero on success, negative values on
+ * error.
+ *
+ */
+int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+ struct sk_buff *skb,
+ struct avc_audit_data *ad)
+{
+ int rc;
+ u32 netlbl_sid;
+ u32 recv_perm;
+
+ rc = selinux_netlbl_skbuff_getsid(skb, sksec->sid, &netlbl_sid);
+ if (rc != 0)
+ return rc;
+
+ if (netlbl_sid == SECINITSID_UNLABELED)
+ return 0;
+
+ switch (sksec->sclass) {
+ case SECCLASS_UDP_SOCKET:
+ recv_perm = UDP_SOCKET__RECV_MSG;
+ break;
+ case SECCLASS_TCP_SOCKET:
+ recv_perm = TCP_SOCKET__RECV_MSG;
+ break;
+ default:
+ recv_perm = RAWIP_SOCKET__RECV_MSG;
+ }
+
+ rc = avc_has_perm(sksec->sid,
+ netlbl_sid,
+ sksec->sclass,
+ recv_perm,
+ ad);
+ if (rc == 0)
+ return 0;
+
+ netlbl_skbuff_err(skb, rc);
+ return rc;
+}
+
+/**
+ * selinux_netlbl_socket_getpeersec_stream - Return the connected peer's SID
+ * @sock: the socket
+ *
+ * Description:
+ * Examine @sock to find the connected peer's SID. Returns the SID on success
+ * or SECSID_NULL on error.
+ *
+ */
+u32 selinux_netlbl_socket_getpeersec_stream(struct socket *sock)
+{
+ struct sk_security_struct *sksec = sock->sk->sk_security;
+
+ if (sksec->peer_sid == SECINITSID_UNLABELED)
+ return SECSID_NULL;
+
+ return sksec->peer_sid;
+}
+
+/**
+ * selinux_netlbl_socket_getpeersec_dgram - Return the SID of a NetLabel packet
+ * @skb: the packet
+ *
+ * Description:
+ * Examine @skb to find the SID assigned to it by NetLabel. Returns the SID on
+ * success, SECSID_NULL on error.
+ *
+ */
+u32 selinux_netlbl_socket_getpeersec_dgram(struct sk_buff *skb)
+{
+ int peer_sid;
+ struct sock *sk = skb->sk;
+ struct inode_security_struct *isec;
+
+ if (sk == NULL || sk->sk_socket == NULL)
+ return SECSID_NULL;
+
+ isec = SOCK_INODE(sk->sk_socket)->i_security;
+ if (selinux_netlbl_skbuff_getsid(skb, isec->sid, &peer_sid) != 0)
+ return SECSID_NULL;
+ if (peer_sid == SECINITSID_UNLABELED)
+ return SECSID_NULL;
+
+ return peer_sid;
+}
+#endif /* CONFIG_NETLABEL */
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 6c985ced810..3e742b850af 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -6,7 +6,12 @@
* Authors: Serge Hallyn <sergeh@us.ibm.com>
* Trent Jaeger <jaegert@us.ibm.com>
*
+ * Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com>
+ *
+ * Granular IPSec Associations for use in MLS environments.
+ *
* Copyright (C) 2005 International Business Machines Corporation
+ * Copyright (C) 2006 Trusted Computer Solutions, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -67,10 +72,10 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
}
/*
- * LSM hook implementation that authorizes that a socket can be used
- * with the corresponding xfrm_sec_ctx and direction.
+ * LSM hook implementation that authorizes that a flow can use
+ * a xfrm policy rule.
*/
-int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
{
int rc = 0;
u32 sel_sid = SECINITSID_UNLABELED;
@@ -84,27 +89,130 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
sel_sid = ctx->ctx_sid;
}
- rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION,
- ((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM :
- ((dir == FLOW_DIR_OUT) ? ASSOCIATION__SENDTO :
- (ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))),
+ rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
+ ASSOCIATION__POLMATCH,
NULL);
return rc;
}
/*
+ * LSM hook implementation that authorizes that a state matches
+ * the given policy, flow combo.
+ */
+
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp,
+ struct flowi *fl)
+{
+ u32 state_sid;
+ u32 pol_sid;
+ int err;
+
+ if (x->security)
+ state_sid = x->security->ctx_sid;
+ else
+ state_sid = SECINITSID_UNLABELED;
+
+ if (xp->security)
+ pol_sid = xp->security->ctx_sid;
+ else
+ pol_sid = SECINITSID_UNLABELED;
+
+ err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION,
+ ASSOCIATION__POLMATCH,
+ NULL);
+
+ if (err)
+ return 0;
+
+ return selinux_xfrm_flow_state_match(fl, x);
+}
+
+/*
+ * LSM hook implementation that authorizes that a particular outgoing flow
+ * can use a given security association.
+ */
+
+int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
+{
+ int rc = 0;
+ u32 sel_sid = SECINITSID_UNLABELED;
+ struct xfrm_sec_ctx *ctx;
+
+ /* Context sid is either set to label or ANY_ASSOC */
+ if ((ctx = xfrm->security)) {
+ if (!selinux_authorizable_ctx(ctx))
+ return 0;
+
+ sel_sid = ctx->ctx_sid;
+ }
+
+ rc = avc_has_perm(fl->secid, sel_sid, SECCLASS_ASSOCIATION,
+ ASSOCIATION__SENDTO,
+ NULL)? 0:1;
+
+ return rc;
+}
+
+/*
+ * LSM hook implementation that determines the sid for the session.
+ */
+
+int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
+{
+ struct sec_path *sp;
+
+ *sid = SECSID_NULL;
+
+ if (skb == NULL)
+ return 0;
+
+ sp = skb->sp;
+ if (sp) {
+ int i, sid_set = 0;
+
+ for (i = sp->len-1; i >= 0; i--) {
+ struct xfrm_state *x = sp->xvec[i];
+ if (selinux_authorizable_xfrm(x)) {
+ struct xfrm_sec_ctx *ctx = x->security;
+
+ if (!sid_set) {
+ *sid = ctx->ctx_sid;
+ sid_set = 1;
+
+ if (!ckall)
+ break;
+ }
+ else if (*sid != ctx->ctx_sid)
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
* Security blob allocation for xfrm_policy and xfrm_state
* CTX does not have a meaningful value on input
*/
-static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *uctx)
+static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
+ struct xfrm_user_sec_ctx *uctx, struct xfrm_sec_ctx *pol, u32 sid)
{
int rc = 0;
struct task_security_struct *tsec = current->security;
- struct xfrm_sec_ctx *ctx;
+ struct xfrm_sec_ctx *ctx = NULL;
+ char *ctx_str = NULL;
+ u32 str_len;
+ u32 ctx_sid;
+
+ BUG_ON(uctx && pol);
+
+ if (!uctx)
+ goto not_from_user;
- BUG_ON(!uctx);
- BUG_ON(uctx->ctx_doi != XFRM_SC_ALG_SELINUX);
+ if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
+ return -EINVAL;
if (uctx->ctx_len >= PAGE_SIZE)
return -ENOMEM;
@@ -141,9 +249,43 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_us
return rc;
+not_from_user:
+ if (pol) {
+ rc = security_sid_mls_copy(pol->ctx_sid, sid, &ctx_sid);
+ if (rc)
+ goto out;
+ }
+ else
+ ctx_sid = sid;
+
+ rc = security_sid_to_context(ctx_sid, &ctx_str, &str_len);
+ if (rc)
+ goto out;
+
+ *ctxp = ctx = kmalloc(sizeof(*ctx) +
+ str_len,
+ GFP_ATOMIC);
+
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ctx->ctx_doi = XFRM_SC_DOI_LSM;
+ ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
+ ctx->ctx_sid = ctx_sid;
+ ctx->ctx_len = str_len;
+ memcpy(ctx->ctx_str,
+ ctx_str,
+ str_len);
+
+ goto out2;
+
out:
*ctxp = NULL;
kfree(ctx);
+out2:
+ kfree(ctx_str);
return rc;
}
@@ -151,13 +293,23 @@ out:
* LSM hook implementation that allocs and transfers uctx spec to
* xfrm_policy.
*/
-int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *uctx)
+int selinux_xfrm_policy_alloc(struct xfrm_policy *xp,
+ struct xfrm_user_sec_ctx *uctx, struct sock *sk)
{
int err;
+ u32 sid;
BUG_ON(!xp);
+ BUG_ON(uctx && sk);
- err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx);
+ if (sk) {
+ struct sk_security_struct *ssec = sk->sk_security;
+ sid = ssec->sid;
+ }
+ else
+ sid = SECSID_NULL;
+
+ err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, NULL, sid);
return err;
}
@@ -217,13 +369,14 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
* LSM hook implementation that allocs and transfers sec_ctx spec to
* xfrm_state.
*/
-int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx)
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx,
+ struct xfrm_sec_ctx *pol, u32 secid)
{
int err;
BUG_ON(!x);
- err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx);
+ err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, pol, secid);
return err;
}
@@ -329,38 +482,30 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
* we need to check for unlabelled access since this may not have
* gone thru the IPSec process.
*/
-int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad)
{
int i, rc = 0;
struct sec_path *sp;
+ u32 sel_sid = SECINITSID_UNLABELED;
sp = skb->sp;
if (sp) {
- /*
- * __xfrm_policy_check does not approve unless xfrm_policy_ok
- * says that spi's match for policy and the socket.
- *
- * Only need to verify the existence of an authorizable sp.
- */
for (i = 0; i < sp->len; i++) {
struct xfrm_state *x = sp->xvec[i];
- if (x && selinux_authorizable_xfrm(x))
- goto accept;
+ if (x && selinux_authorizable_xfrm(x)) {
+ struct xfrm_sec_ctx *ctx = x->security;
+ sel_sid = ctx->ctx_sid;
+ break;
+ }
}
}
- /* check SELinux sock for unlabelled access */
- rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
- ASSOCIATION__RECVFROM, NULL);
- if (rc)
- goto drop;
-
-accept:
- return 0;
+ rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
+ ASSOCIATION__RECVFROM, ad);
-drop:
return rc;
}
@@ -371,7 +516,8 @@ drop:
* If we do have a authorizable security association, then it has already been
* checked in xfrm_policy_lookup hook.
*/
-int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad)
{
struct dst_entry *dst;
int rc = 0;
@@ -391,7 +537,7 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
}
rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
- ASSOCIATION__SENDTO, NULL);
+ ASSOCIATION__SENDTO, ad);
out:
return rc;
}