Commit 6d3dc07c authored by Casey Schaufler's avatar Casey Schaufler Committed by Paul Moore

smack: Add support for unlabeled network hosts and networks

Add support for unlabeled network hosts and networks.
Relies heavily on Paul Moore's netlabel support.

Creates a new entry in /smack called netlabel. Writes to /smack/netlabel
take the form:

    A.B.C.D LABEL
or
    A.B.C.D/N LABEL

where A.B.C.D is a network address, N is an integer between 0-32,
and LABEL is the Smack label to be used. If /N is omitted /32 is
assumed. N designates the netmask for the address. Entries are
matched by the most specific address/mask pair. 0.0.0.0/0 will
match everything, while 192.168.1.117/32 will match exactly one
host.

A new system label "@", pronounced "web", is defined. Processes
can not be assigned the web label. An address assigned the web
label can be written to by any process, and packets coming from
a web address can be written to any socket. Use of the web label
is a violation of any strict MAC policy, but the web label has
been requested many times.

The nltype entry has been removed from /smack. It did not work right
and the netlabel interface can be used to specify that all hosts
be treated as unlabeled.

CIPSO labels on incoming packets will be honored, even from designated
single label hosts. Single label hosts can only be written to by
processes with labels that can write to the label of the host.
Packets sent to single label hosts will always be unlabeled.

Once added a single label designation cannot be removed, however
the label may be changed.

The behavior of the ambient label remains unchanged.
Signed-off-by: default avatarCasey Schaufler <casey@schaufler-ca.com>
Signed-off-by: default avatarPaul Moore <paul.moore@hp.com>
parent 277d342f
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/in.h>
#include <net/netlabel.h> #include <net/netlabel.h>
/* /*
...@@ -39,6 +40,7 @@ struct superblock_smack { ...@@ -39,6 +40,7 @@ struct superblock_smack {
struct socket_smack { struct socket_smack {
char *smk_out; /* outbound label */ char *smk_out; /* outbound label */
char *smk_in; /* inbound label */ char *smk_in; /* inbound label */
int smk_labeled; /* label scheme */
char smk_packet[SMK_LABELLEN]; /* TCP peer label */ char smk_packet[SMK_LABELLEN]; /* TCP peer label */
}; };
...@@ -79,6 +81,16 @@ struct smack_cipso { ...@@ -79,6 +81,16 @@ struct smack_cipso {
char smk_catset[SMK_LABELLEN]; char smk_catset[SMK_LABELLEN];
}; };
/*
* An entry in the table identifying hosts.
*/
struct smk_netlbladdr {
struct smk_netlbladdr *smk_next;
struct sockaddr_in smk_host; /* network address */
struct in_addr smk_mask; /* network mask */
char *smk_label; /* label */
};
/* /*
* This is the repository for labels seen so that it is * This is the repository for labels seen so that it is
* not necessary to keep allocating tiny chuncks of memory * not necessary to keep allocating tiny chuncks of memory
...@@ -127,6 +139,20 @@ struct smack_known { ...@@ -127,6 +139,20 @@ struct smack_known {
#define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT
/* /*
* How communications on this socket are treated.
* Usually it's determined by the underlying netlabel code
* but there are certain cases, including single label hosts
* and potentially single label interfaces for which the
* treatment can not be known in advance.
*
* The possibility of additional labeling schemes being
* introduced in the future exists as well.
*/
#define SMACK_UNLABELED_SOCKET 0
#define SMACK_CIPSO_SOCKET 1
/*
* smackfs magic number
* smackfs macic number * smackfs macic number
*/ */
#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ #define SMACK_MAGIC 0x43415d53 /* "SMAC" */
...@@ -141,6 +167,7 @@ struct smack_known { ...@@ -141,6 +167,7 @@ struct smack_known {
* CIPSO defaults. * CIPSO defaults.
*/ */
#define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */ #define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */
#define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */
#define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ #define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */
#define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */ #define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */
#define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */
...@@ -176,7 +203,6 @@ u32 smack_to_secid(const char *); ...@@ -176,7 +203,6 @@ u32 smack_to_secid(const char *);
* Shared data. * Shared data.
*/ */
extern int smack_cipso_direct; extern int smack_cipso_direct;
extern int smack_net_nltype;
extern char *smack_net_ambient; extern char *smack_net_ambient;
extern char *smack_onlycap; extern char *smack_onlycap;
...@@ -186,9 +212,10 @@ extern struct smack_known smack_known_hat; ...@@ -186,9 +212,10 @@ extern struct smack_known smack_known_hat;
extern struct smack_known smack_known_huh; extern struct smack_known smack_known_huh;
extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_invalid;
extern struct smack_known smack_known_star; extern struct smack_known smack_known_star;
extern struct smack_known smack_known_unset; extern struct smack_known smack_known_web;
extern struct smk_list_entry *smack_list; extern struct smk_list_entry *smack_list;
extern struct smk_netlbladdr *smack_netlbladdrs;
extern struct security_operations smack_ops; extern struct security_operations smack_ops;
/* /*
......
...@@ -15,15 +15,8 @@ ...@@ -15,15 +15,8 @@
#include <linux/sched.h> #include <linux/sched.h>
#include "smack.h" #include "smack.h"
struct smack_known smack_known_unset = {
.smk_next = NULL,
.smk_known = "UNSET",
.smk_secid = 1,
.smk_cipso = NULL,
};
struct smack_known smack_known_huh = { struct smack_known smack_known_huh = {
.smk_next = &smack_known_unset, .smk_next = NULL,
.smk_known = "?", .smk_known = "?",
.smk_secid = 2, .smk_secid = 2,
.smk_cipso = NULL, .smk_cipso = NULL,
...@@ -57,7 +50,14 @@ struct smack_known smack_known_invalid = { ...@@ -57,7 +50,14 @@ struct smack_known smack_known_invalid = {
.smk_cipso = NULL, .smk_cipso = NULL,
}; };
struct smack_known *smack_known = &smack_known_invalid; struct smack_known smack_known_web = {
.smk_next = &smack_known_invalid,
.smk_known = "@",
.smk_secid = 7,
.smk_cipso = NULL,
};
struct smack_known *smack_known = &smack_known_web;
/* /*
* The initial value needs to be bigger than any of the * The initial value needs to be bigger than any of the
...@@ -98,6 +98,16 @@ int smk_access(char *subject_label, char *object_label, int request) ...@@ -98,6 +98,16 @@ int smk_access(char *subject_label, char *object_label, int request)
if (subject_label == smack_known_star.smk_known || if (subject_label == smack_known_star.smk_known ||
strcmp(subject_label, smack_known_star.smk_known) == 0) strcmp(subject_label, smack_known_star.smk_known) == 0)
return -EACCES; return -EACCES;
/*
* An internet object can be accessed by any subject.
* Tasks cannot be assigned the internet label.
* An internet subject can access any object.
*/
if (object_label == smack_known_web.smk_known ||
subject_label == smack_known_web.smk_known ||
strcmp(object_label, smack_known_web.smk_known) == 0 ||
strcmp(subject_label, smack_known_web.smk_known) == 0)
return 0;
/* /*
* A star object can be accessed by any subject. * A star object can be accessed by any subject.
*/ */
......
...@@ -1277,6 +1277,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) ...@@ -1277,6 +1277,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
ssp->smk_in = csp; ssp->smk_in = csp;
ssp->smk_out = csp; ssp->smk_out = csp;
ssp->smk_labeled = SMACK_CIPSO_SOCKET;
ssp->smk_packet[0] = '\0'; ssp->smk_packet[0] = '\0';
sk->sk_security = ssp; sk->sk_security = ssp;
...@@ -1341,45 +1342,69 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) ...@@ -1341,45 +1342,69 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
struct smack_cipso cipso; struct smack_cipso cipso;
int rc; int rc;
switch (smack_net_nltype) { nlsp->domain = smack;
case NETLBL_NLTYPE_CIPSOV4: nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
nlsp->domain = smack;
nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
rc = smack_to_cipso(smack, &cipso); rc = smack_to_cipso(smack, &cipso);
if (rc == 0) { if (rc == 0) {
nlsp->attr.mls.lvl = cipso.smk_level; nlsp->attr.mls.lvl = cipso.smk_level;
smack_set_catset(cipso.smk_catset, nlsp); smack_set_catset(cipso.smk_catset, nlsp);
} else { } else {
nlsp->attr.mls.lvl = smack_cipso_direct; nlsp->attr.mls.lvl = smack_cipso_direct;
smack_set_catset(smack, nlsp); smack_set_catset(smack, nlsp);
}
break;
default:
break;
} }
} }
/** /**
* smack_netlabel - Set the secattr on a socket * smack_netlabel - Set the secattr on a socket
* @sk: the socket * @sk: the socket
* @labeled: socket label scheme
* *
* Convert the outbound smack value (smk_out) to a * Convert the outbound smack value (smk_out) to a
* secattr and attach it to the socket. * secattr and attach it to the socket.
* *
* Returns 0 on success or an error code * Returns 0 on success or an error code
*/ */
static int smack_netlabel(struct sock *sk) static int smack_netlabel(struct sock *sk, int labeled)
{ {
struct socket_smack *ssp; struct socket_smack *ssp;
struct netlbl_lsm_secattr secattr; struct netlbl_lsm_secattr secattr;
int rc; int rc = 0;
ssp = sk->sk_security; ssp = sk->sk_security;
netlbl_secattr_init(&secattr); /*
smack_to_secattr(ssp->smk_out, &secattr); * Usually the netlabel code will handle changing the
rc = netlbl_sock_setattr(sk, &secattr); * packet labeling based on the label.
netlbl_secattr_destroy(&secattr); * The case of a single label host is different, because
* a single label host should never get a labeled packet
* even though the label is usually associated with a packet
* label.
*/
local_bh_disable();
bh_lock_sock_nested(sk);
if (ssp->smk_out == smack_net_ambient ||
labeled == SMACK_UNLABELED_SOCKET)
netlbl_sock_delattr(sk);
else {
netlbl_secattr_init(&secattr);
smack_to_secattr(ssp->smk_out, &secattr);
rc = netlbl_sock_setattr(sk, &secattr);
netlbl_secattr_destroy(&secattr);
}
bh_unlock_sock(sk);
local_bh_enable();
/*
* Remember the label scheme used so that it is not
* necessary to do the netlabel setting if it has not
* changed the next time through.
*
* The -EDESTADDRREQ case is an indication that there's
* a single level host involved.
*/
if (rc == 0)
ssp->smk_labeled = labeled;
return rc; return rc;
} }
...@@ -1432,7 +1457,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, ...@@ -1432,7 +1457,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
ssp->smk_in = sp; ssp->smk_in = sp;
else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) { else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
ssp->smk_out = sp; ssp->smk_out = sp;
rc = smack_netlabel(sock->sk); rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n", printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
__func__, -rc); __func__, -rc);
...@@ -1462,7 +1487,108 @@ static int smack_socket_post_create(struct socket *sock, int family, ...@@ -1462,7 +1487,108 @@ static int smack_socket_post_create(struct socket *sock, int family,
/* /*
* Set the outbound netlbl. * Set the outbound netlbl.
*/ */
return smack_netlabel(sock->sk); return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
}
/**
* smack_host_label - check host based restrictions
* @sip: the object end
*
* looks for host based access restrictions
*
* This version will only be appropriate for really small
* sets of single label hosts. Because of the masking
* it cannot shortcut out on the first match. There are
* numerious ways to address the problem, but none of them
* have been applied here.
*
* Returns the label of the far end or NULL if it's not special.
*/
static char *smack_host_label(struct sockaddr_in *sip)
{
struct smk_netlbladdr *snp;
char *bestlabel = NULL;
struct in_addr *siap = &sip->sin_addr;
struct in_addr *liap;
struct in_addr *miap;
struct in_addr bestmask;
if (siap->s_addr == 0)
return NULL;
bestmask.s_addr = 0;
for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) {
liap = &snp->smk_host.sin_addr;
miap = &snp->smk_mask;
/*
* If the addresses match after applying the list entry mask
* the entry matches the address. If it doesn't move along to
* the next entry.
*/
if ((liap->s_addr & miap->s_addr) !=
(siap->s_addr & miap->s_addr))
continue;
/*
* If the list entry mask identifies a single address
* it can't get any more specific.
*/
if (miap->s_addr == 0xffffffff)
return snp->smk_label;
/*
* If the list entry mask is less specific than the best
* already found this entry is uninteresting.
*/
if ((miap->s_addr | bestmask.s_addr) == bestmask.s_addr)
continue;
/*
* This is better than any entry found so far.
*/
bestmask.s_addr = miap->s_addr;
bestlabel = snp->smk_label;
}
return bestlabel;
}
/**
* smack_socket_connect - connect access check
* @sock: the socket
* @sap: the other end
* @addrlen: size of sap
*
* Verifies that a connection may be possible
*
* Returns 0 on success, and error code otherwise
*/
static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
int addrlen)
{
struct socket_smack *ssp = sock->sk->sk_security;
char *hostsp;
int rc;
if (sock->sk == NULL || sock->sk->sk_family != PF_INET)
return 0;
if (addrlen < sizeof(struct sockaddr_in))
return -EINVAL;
hostsp = smack_host_label((struct sockaddr_in *)sap);
if (hostsp == NULL) {
if (ssp->smk_labeled != SMACK_CIPSO_SOCKET)
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
return 0;
}
rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE);
if (rc != 0)
return rc;
if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET)
return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET);
return 0;
} }
/** /**
...@@ -2101,8 +2227,14 @@ static int smack_setprocattr(struct task_struct *p, char *name, ...@@ -2101,8 +2227,14 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (newsmack == NULL) if (newsmack == NULL)
return -EINVAL; return -EINVAL;
/*
* No process is ever allowed the web ("@") label.
*/
if (newsmack == smack_known_web.smk_known)
return -EPERM;
new = prepare_creds(); new = prepare_creds();
if (!new) if (new == NULL)
return -ENOMEM; return -ENOMEM;
new->security = newsmack; new->security = newsmack;
commit_creds(new); commit_creds(new);
...@@ -2143,6 +2275,49 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) ...@@ -2143,6 +2275,49 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE); return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE);
} }
/**
* smack_socket_sendmsg - Smack check based on destination host
* @sock: the socket
* @msghdr: the message
* @size: the size of the message
*
* Return 0 if the current subject can write to the destination
* host. This is only a question if the destination is a single
* label host.
*/
static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
struct socket_smack *ssp = sock->sk->sk_security;
char *hostsp;
int rc;
/*
* Perfectly reasonable for this to be NULL
*/
if (sip == NULL || sip->sin_family != PF_INET)
return 0;
hostsp = smack_host_label(sip);
if (hostsp == NULL) {
if (ssp->smk_labeled != SMACK_CIPSO_SOCKET)
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
return 0;
}
rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE);
if (rc != 0)
return rc;
if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET)
return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET);
return 0;
}
/** /**
* smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
* pair to smack * pair to smack
...@@ -2154,44 +2329,66 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) ...@@ -2154,44 +2329,66 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
{ {
char smack[SMK_LABELLEN]; char smack[SMK_LABELLEN];
char *sp;
int pcat; int pcat;
if ((sap->flags & NETLBL_SECATTR_MLS_LVL) == 0) { if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
/* /*
* Looks like a CIPSO packet.
* If there are flags but no level netlabel isn't * If there are flags but no level netlabel isn't
* behaving the way we expect it to. * behaving the way we expect it to.
* *
* Get the categories, if any
* Without guidance regarding the smack value * Without guidance regarding the smack value
* for the packet fall back on the network * for the packet fall back on the network
* ambient value. * ambient value.
*/ */
strncpy(sip, smack_net_ambient, SMK_MAXLEN); memset(smack, '\0', SMK_LABELLEN);
if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
for (pcat = -1;;) {
pcat = netlbl_secattr_catmap_walk(
sap->attr.mls.cat, pcat + 1);
if (pcat < 0)
break;
smack_catset_bit(pcat, smack);
}
/*
* If it is CIPSO using smack direct mapping
* we are already done. WeeHee.
*/
if (sap->attr.mls.lvl == smack_cipso_direct) {
memcpy(sip, smack, SMK_MAXLEN);
return;
}
/*
* Look it up in the supplied table if it is not
* a direct mapping.
*/
smack_from_cipso(sap->attr.mls.lvl, smack, sip);
return; return;
} }
/* if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
* Get the categories, if any /*
*/ * Looks like a fallback, which gives us a secid.
memset(smack, '\0', SMK_LABELLEN); */
if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0) sp = smack_from_secid(sap->attr.secid);
for (pcat = -1;;) { /*
pcat = netlbl_secattr_catmap_walk(sap->attr.mls.cat, * This has got to be a bug because it is
pcat + 1); * impossible to specify a fallback without
if (pcat < 0) * specifying the label, which will ensure
break; * it has a secid, and the only way to get a
smack_catset_bit(pcat, smack); * secid is from a fallback.
} */
/* BUG_ON(sp == NULL);
* If it is CIPSO using smack direct mapping strncpy(sip, sp, SMK_MAXLEN);
* we are already done. WeeHee.
*/
if (sap->attr.mls.lvl == smack_cipso_direct) {
memcpy(sip, smack, SMK_MAXLEN);
return; return;
} }
/* /*
* Look it up in the supplied table if it is not a direct mapping. * Without guidance regarding the smack value
* for the packet fall back on the network
* ambient value.
*/ */
smack_from_cipso(sap->attr.mls.lvl, smack, sip); strncpy(sip, smack_net_ambient, SMK_MAXLEN);
return; return;
} }
...@@ -2207,6 +2404,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -2207,6 +2404,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
struct netlbl_lsm_secattr secattr; struct netlbl_lsm_secattr secattr;
struct socket_smack *ssp = sk->sk_security; struct socket_smack *ssp = sk->sk_security;
char smack[SMK_LABELLEN]; char smack[SMK_LABELLEN];
char *csp;
int rc; int rc;
if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
...@@ -2215,21 +2413,24 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -2215,21 +2413,24 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
/* /*
* Translate what netlabel gave us. * Translate what netlabel gave us.
*/ */
memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&secattr); netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
if (rc == 0) if (rc == 0) {
smack_from_secattr(&secattr, smack); smack_from_secattr(&secattr, smack);
else csp = smack;
strncpy(smack, smack_net_ambient, SMK_MAXLEN); } else
csp = smack_net_ambient;
netlbl_secattr_destroy(&secattr); netlbl_secattr_destroy(&secattr);
/* /*
* Receiving a packet requires that the other end * Receiving a packet requires that the other end
* be able to write here. Read access is not required. * be able to write here. Read access is not required.
* This is the simplist possible security model * This is the simplist possible security model
* for networking. * for networking.
*/ */
rc = smk_access(smack, ssp->smk_in, MAY_WRITE); rc = smk_access(csp, ssp->smk_in, MAY_WRITE);
if (rc != 0) if (rc != 0)
netlbl_skbuff_err(skb, rc, 0); netlbl_skbuff_err(skb, rc, 0);
return rc; return rc;
...@@ -2298,7 +2499,6 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, ...@@ -2298,7 +2499,6 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
/* /*
* Translate what netlabel gave us. * Translate what netlabel gave us.
*/ */
memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&secattr); netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0) if (rc == 0)
...@@ -2341,7 +2541,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent) ...@@ -2341,7 +2541,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
ssp->smk_in = ssp->smk_out = current_security(); ssp->smk_in = ssp->smk_out = current_security();
ssp->smk_packet[0] = '\0'; ssp->smk_packet[0] = '\0';
rc = smack_netlabel(sk); rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n", printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
__func__, -rc); __func__, -rc);
...@@ -2367,7 +2567,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -2367,7 +2567,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
if (skb == NULL) if (skb == NULL)
return -EACCES; return -EACCES;
memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&skb_secattr); netlbl_secattr_init(&skb_secattr);
rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr); rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr);
if (rc == 0) if (rc == 0)
...@@ -2732,6 +2931,8 @@ struct security_operations smack_ops = { ...@@ -2732,6 +2931,8 @@ struct security_operations smack_ops = {
.unix_may_send = smack_unix_may_send, .unix_may_send = smack_unix_may_send,
.socket_post_create = smack_socket_post_create, .socket_post_create = smack_socket_post_create,
.socket_connect = smack_socket_connect,
.socket_sendmsg = smack_socket_sendmsg,
.socket_sock_rcv_skb = smack_socket_sock_rcv_skb, .socket_sock_rcv_skb = smack_socket_sock_rcv_skb,
.socket_getpeersec_stream = smack_socket_getpeersec_stream, .socket_getpeersec_stream = smack_socket_getpeersec_stream,
.socket_getpeersec_dgram = smack_socket_getpeersec_dgram, .socket_getpeersec_dgram = smack_socket_getpeersec_dgram,
...@@ -2783,7 +2984,6 @@ static __init int smack_init(void) ...@@ -2783,7 +2984,6 @@ static __init int smack_init(void)
/* /*
* Initialize locks * Initialize locks
*/ */
spin_lock_init(&smack_known_unset.smk_cipsolock);
spin_lock_init(&smack_known_huh.smk_cipsolock); spin_lock_init(&smack_known_huh.smk_cipsolock);
spin_lock_init(&smack_known_hat.smk_cipsolock); spin_lock_init(&smack_known_hat.smk_cipsolock);
spin_lock_init(&smack_known_star.smk_cipsolock); spin_lock_init(&smack_known_star.smk_cipsolock);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <net/net_namespace.h>
#include <net/netlabel.h> #include <net/netlabel.h>
#include <net/cipso_ipv4.h> #include <net/cipso_ipv4.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -38,7 +39,7 @@ enum smk_inos { ...@@ -38,7 +39,7 @@ enum smk_inos {
SMK_DOI = 5, /* CIPSO DOI */ SMK_DOI = 5, /* CIPSO DOI */
SMK_DIRECT = 6, /* CIPSO level indicating direct label */ SMK_DIRECT = 6, /* CIPSO level indicating direct label */
SMK_AMBIENT = 7, /* internet ambient label */ SMK_AMBIENT = 7, /* internet ambient label */
SMK_NLTYPE = 8, /* label scheme to use by default */ SMK_NETLBLADDR = 8, /* single label hosts */
SMK_ONLYCAP = 9, /* the only "capable" label */ SMK_ONLYCAP = 9, /* the only "capable" label */
}; };
...@@ -48,6 +49,7 @@ enum smk_inos { ...@@ -48,6 +49,7 @@ enum smk_inos {
static DEFINE_MUTEX(smack_list_lock); static DEFINE_MUTEX(smack_list_lock);
static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock); static DEFINE_MUTEX(smack_ambient_lock);
static DEFINE_MUTEX(smk_netlbladdr_lock);
/* /*
* This is the "ambient" label for network traffic. * This is the "ambient" label for network traffic.
...@@ -56,12 +58,6 @@ static DEFINE_MUTEX(smack_ambient_lock); ...@@ -56,12 +58,6 @@ static DEFINE_MUTEX(smack_ambient_lock);
*/ */
char *smack_net_ambient = smack_known_floor.smk_known; char *smack_net_ambient = smack_known_floor.smk_known;
/*
* This is the default packet marking scheme for network traffic.
* It can be reset via smackfs/nltype
*/
int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4;
/* /*
* This is the level in a CIPSO header that indicates a * This is the level in a CIPSO header that indicates a
* smack label is contained directly in the category set. * smack label is contained directly in the category set.
...@@ -79,6 +75,13 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; ...@@ -79,6 +75,13 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
*/ */
char *smack_onlycap; char *smack_onlycap;
/*
* Certain IP addresses may be designated as single label hosts.
* Packets are sent there unlabeled, but only from tasks that
* can write to the specified label.
*/
struct smk_netlbladdr *smack_netlbladdrs;
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
struct smk_list_entry *smack_list; struct smk_list_entry *smack_list;
...@@ -104,6 +107,24 @@ struct smk_list_entry *smack_list; ...@@ -104,6 +107,24 @@ struct smk_list_entry *smack_list;
#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1) #define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN) #define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
/**
* smk_netlabel_audit_set - fill a netlbl_audit struct
* @nap: structure to fill
*/
static void smk_netlabel_audit_set(struct netlbl_audit *nap)
{
nap->loginuid = audit_get_loginuid(current);
nap->sessionid = audit_get_sessionid(current);
nap->secid = smack_to_secid(current_security());
}
/*
* Values for parsing single label host rules
* "1.2.3.4 X"
* "192.168.138.129/32 abcdefghijklmnopqrstuvw"
*/
#define SMK_NETLBLADDRMIN 9
#define SMK_NETLBLADDRMAX 42
/* /*
* Seq_file read operations for /smack/load * Seq_file read operations for /smack/load
...@@ -344,13 +365,11 @@ static void smk_cipso_doi(void) ...@@ -344,13 +365,11 @@ static void smk_cipso_doi(void)
{ {
int rc; int rc;
struct cipso_v4_doi *doip; struct cipso_v4_doi *doip;
struct netlbl_audit audit_info; struct netlbl_audit nai;
audit_info.loginuid = audit_get_loginuid(current); smk_netlabel_audit_set(&nai);
audit_info.sessionid = audit_get_sessionid(current);
audit_info.secid = smack_to_secid(current_security());
rc = netlbl_cfg_map_del(NULL, PF_UNSPEC, NULL, NULL, &audit_info); rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "%s:%d remove rc = %d\n", printk(KERN_WARNING "%s:%d remove rc = %d\n",
__func__, __LINE__, rc); __func__, __LINE__, rc);
...@@ -365,15 +384,14 @@ static void smk_cipso_doi(void) ...@@ -365,15 +384,14 @@ static void smk_cipso_doi(void)
for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++) for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)
doip->tags[rc] = CIPSO_V4_TAG_INVALID; doip->tags[rc] = CIPSO_V4_TAG_INVALID;
rc = netlbl_cfg_cipsov4_add(doip, &audit_info); rc = netlbl_cfg_cipsov4_add(doip, &nai);
if (rc != 0) { if (rc != 0) {
printk(KERN_WARNING "%s:%d cipso add rc = %d\n", printk(KERN_WARNING "%s:%d cipso add rc = %d\n",
__func__, __LINE__, rc); __func__, __LINE__, rc);
kfree(doip); kfree(doip);
return; return;
} }
rc = netlbl_cfg_cipsov4_map_add(doip->doi, rc = netlbl_cfg_cipsov4_map_add(doip->doi, NULL, NULL, NULL, &nai);
NULL, NULL, NULL, &audit_info);
if (rc != 0) { if (rc != 0) {
printk(KERN_WARNING "%s:%d map add rc = %d\n", printk(KERN_WARNING "%s:%d map add rc = %d\n",
__func__, __LINE__, rc); __func__, __LINE__, rc);
...@@ -388,22 +406,19 @@ static void smk_cipso_doi(void) ...@@ -388,22 +406,19 @@ static void smk_cipso_doi(void)
static void smk_unlbl_ambient(char *oldambient) static void smk_unlbl_ambient(char *oldambient)
{ {
int rc; int rc;
struct netlbl_audit audit_info; struct netlbl_audit nai;
audit_info.loginuid = audit_get_loginuid(current); smk_netlabel_audit_set(&nai);
audit_info.sessionid = audit_get_sessionid(current);
audit_info.secid = smack_to_secid(current_security());
if (oldambient != NULL) { if (oldambient != NULL) {
rc = netlbl_cfg_map_del(oldambient, rc = netlbl_cfg_map_del(oldambient, PF_INET, NULL, NULL, &nai);
PF_UNSPEC, NULL, NULL, &audit_info);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "%s:%d remove rc = %d\n", printk(KERN_WARNING "%s:%d remove rc = %d\n",
__func__, __LINE__, rc); __func__, __LINE__, rc);
} }
rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET,
PF_INET, NULL, NULL, &audit_info); NULL, NULL, &nai);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "%s:%d add rc = %d\n", printk(KERN_WARNING "%s:%d add rc = %d\n",
__func__, __LINE__, rc); __func__, __LINE__, rc);
...@@ -614,6 +629,201 @@ static const struct file_operations smk_cipso_ops = { ...@@ -614,6 +629,201 @@ static const struct file_operations smk_cipso_ops = {
.release = seq_release, .release = seq_release,
}; };
/*
* Seq_file read operations for /smack/netlabel
*/
static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
{
if (*pos == SEQ_READ_FINISHED)
return NULL;
return smack_netlbladdrs;
}
static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next;
if (skp == NULL)
*pos = SEQ_READ_FINISHED;
return skp;
}
/*
#define BEMASK 0x80000000
*/
#define BEMASK 0x00000001
#define BEBITS (sizeof(__be32) * 8)
/*
* Print host/label pairs
*/
static int netlbladdr_seq_show(struct seq_file *s, void *v)
{
struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v;
unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
__be32 bebits;
int maskn = 0;
for (bebits = BEMASK; bebits != 0; maskn++, bebits <<= 1)
if ((skp->smk_mask.s_addr & bebits) == 0)
break;
seq_printf(s, "%u.%u.%u.%u/%d %s\n",
hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label);
return 0;
}
static void netlbladdr_seq_stop(struct seq_file *s, void *v)
{
/* No-op */
}
static struct seq_operations netlbladdr_seq_ops = {
.start = netlbladdr_seq_start,
.stop = netlbladdr_seq_stop,
.next = netlbladdr_seq_next,
.show = netlbladdr_seq_show,
};
/**
* smk_open_netlbladdr - open() for /smack/netlabel
* @inode: inode structure representing file
* @file: "netlabel" file pointer
*
* Connect our netlbladdr_seq_* operations with /smack/netlabel
* file_operations
*/
static int smk_open_netlbladdr(struct inode *inode, struct file *file)
{
return seq_open(file, &netlbladdr_seq_ops);
}
/**
* smk_write_netlbladdr - write() for /smack/netlabel
* @filp: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Accepts only one netlbladdr per write call.
* Returns number of bytes written or error code, as appropriate
*/
static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct smk_netlbladdr *skp;
struct sockaddr_in newname;
char smack[SMK_LABELLEN];
char *sp;
char data[SMK_NETLBLADDRMAX];
char *host = (char *)&newname.sin_addr.s_addr;
int rc;
struct netlbl_audit audit_info;
struct in_addr mask;
unsigned int m;
__be32 bebits = BEMASK;
__be32 nsa;
/*
* Must have privilege.
* No partial writes.
* Enough data must be present.
* "<addr/mask, as a.b.c.d/e><space><label>"
* "<addr, as a.b.c.d><space><label>"
*/
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
if (*ppos != 0)
return -EINVAL;
if (count < SMK_NETLBLADDRMIN || count > SMK_NETLBLADDRMAX)
return -EINVAL;
if (copy_from_user(data, buf, count) != 0)
return -EFAULT;
data[count] = '\0';
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%d %s",
&host[0], &host[1], &host[2], &host[3], &m, smack);
if (rc != 6) {
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
&host[0], &host[1], &host[2], &host[3], smack);
if (rc != 5)
return -EINVAL;
m = BEBITS;
}
if (m > BEBITS)
return -EINVAL;
sp = smk_import(smack, 0);
if (sp == NULL)
return -EINVAL;
for (mask.s_addr = 0; m > 0; m--) {
mask.s_addr |= bebits;
bebits <<= 1;
}
/*
* Only allow one writer at a time. Writes should be
* quite rare and small in any case.
*/
mutex_lock(&smk_netlbladdr_lock);
nsa = newname.sin_addr.s_addr;
for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next)
if (skp->smk_host.sin_addr.s_addr == nsa &&
skp->smk_mask.s_addr == mask.s_addr)
break;
smk_netlabel_audit_set(&audit_info);
if (skp == NULL) {
skp = kzalloc(sizeof(*skp), GFP_KERNEL);
if (skp == NULL)
rc = -ENOMEM;
else {
rc = 0;
skp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
skp->smk_mask.s_addr = mask.s_addr;
skp->smk_next = smack_netlbladdrs;
skp->smk_label = sp;
smack_netlbladdrs = skp;
}
} else {
rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
&skp->smk_host.sin_addr, &skp->smk_mask,
PF_INET, &audit_info);
skp->smk_label = sp;
}
/*
* Now tell netlabel about the single label nature of
* this host so that incoming packets get labeled.
*/
if (rc == 0)
rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
&skp->smk_host.sin_addr, &skp->smk_mask, PF_INET,
smack_to_secid(skp->smk_label), &audit_info);
if (rc == 0)
rc = count;
mutex_unlock(&smk_netlbladdr_lock);
return rc;
}
static const struct file_operations smk_netlbladdr_ops = {
.open = smk_open_netlbladdr,
.read = seq_read,
.llseek = seq_lseek,
.write = smk_write_netlbladdr,
.release = seq_release,
};
/** /**
* smk_read_doi - read() for /smack/doi * smk_read_doi - read() for /smack/doi
* @filp: file pointer, not actually used * @filp: file pointer, not actually used
...@@ -902,110 +1112,6 @@ static const struct file_operations smk_onlycap_ops = { ...@@ -902,110 +1112,6 @@ static const struct file_operations smk_onlycap_ops = {
.write = smk_write_onlycap, .write = smk_write_onlycap,
}; };
struct option_names {
int o_number;
char *o_name;
char *o_alias;
};
static struct option_names netlbl_choices[] = {
{ NETLBL_NLTYPE_RIPSO,
NETLBL_NLTYPE_RIPSO_NAME, "ripso" },
{ NETLBL_NLTYPE_CIPSOV4,
NETLBL_NLTYPE_CIPSOV4_NAME, "cipsov4" },
{ NETLBL_NLTYPE_CIPSOV4,
NETLBL_NLTYPE_CIPSOV4_NAME, "cipso" },
{ NETLBL_NLTYPE_CIPSOV6,
NETLBL_NLTYPE_CIPSOV6_NAME, "cipsov6" },
{ NETLBL_NLTYPE_UNLABELED,
NETLBL_NLTYPE_UNLABELED_NAME, "unlabeled" },
};
/**
* smk_read_nltype - read() for /smack/nltype
* @filp: file pointer, not actually used
* @buf: where to put the result
* @count: maximum to send along
* @ppos: where to start
*
* Returns number of bytes read or error code, as appropriate
*/
static ssize_t smk_read_nltype(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char bound[40];
ssize_t rc;
int i;
if (count < SMK_LABELLEN)
return -EINVAL;
if (*ppos != 0)
return 0;
sprintf(bound, "unknown");
for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
if (smack_net_nltype == netlbl_choices[i].o_number) {
sprintf(bound, "%s", netlbl_choices[i].o_name);
break;
}
rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound));
return rc;
}
/**
* smk_write_nltype - write() for /smack/nltype
* @filp: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Returns number of bytes written or error code, as appropriate
*/
static ssize_t smk_write_nltype(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char bound[40];
char *cp;
int i;
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
if (count >= 40)
return -EINVAL;
if (copy_from_user(bound, buf, count) != 0)
return -EFAULT;
bound[count] = '\0';
cp = strchr(bound, ' ');
if (cp != NULL)
*cp = '\0';
cp = strchr(bound, '\n');
if (cp != NULL)
*cp = '\0';
for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
if (strcmp(bound, netlbl_choices[i].o_name) == 0 ||
strcmp(bound, netlbl_choices[i].o_alias) == 0) {
smack_net_nltype = netlbl_choices[i].o_number;
return count;
}
/*
* Not a valid choice.
*/
return -EINVAL;
}
static const struct file_operations smk_nltype_ops = {
.read = smk_read_nltype,
.write = smk_write_nltype,
};
/** /**
* smk_fill_super - fill the /smackfs superblock * smk_fill_super - fill the /smackfs superblock
* @sb: the empty superblock * @sb: the empty superblock
...@@ -1032,8 +1138,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1032,8 +1138,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
{"direct", &smk_direct_ops, S_IRUGO|S_IWUSR}, {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
[SMK_AMBIENT] = [SMK_AMBIENT] =
{"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
[SMK_NLTYPE] = [SMK_NETLBLADDR] =
{"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR}, {"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
[SMK_ONLYCAP] = [SMK_ONLYCAP] =
{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR}, {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
/* last one */ {""} /* last one */ {""}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment