Commit 21abb1ec authored by Casey Schaufler's avatar Casey Schaufler

Smack: IPv6 host labeling

IPv6 appears to be (finally) coming of age with the
influx of autonomous devices. In support of this, add
the ability to associate a Smack label with IPv6 addresses.

This patch also cleans up some of the conditional
compilation associated with the introduction of
secmark processing. It's now more obvious which bit
of code goes with which feature.
Signed-off-by: default avatarCasey Schaufler <casey@schaufler-ca.com>
parent ca70d27e
...@@ -28,6 +28,10 @@ Smack kernels use the CIPSO IP option. Some network ...@@ -28,6 +28,10 @@ Smack kernels use the CIPSO IP option. Some network
configurations are intolerant of IP options and can impede configurations are intolerant of IP options and can impede
access to systems that use them as Smack does. access to systems that use them as Smack does.
Smack is used in the Tizen operating system. Please
go to http://wiki.tizen.org for information about how
Smack is used in Tizen.
The current git repository for Smack user space is: The current git repository for Smack user space is:
git://github.com/smack-team/smack.git git://github.com/smack-team/smack.git
...@@ -108,6 +112,8 @@ in the smackfs filesystem. This pseudo-filesystem is mounted ...@@ -108,6 +112,8 @@ in the smackfs filesystem. This pseudo-filesystem is mounted
on /sys/fs/smackfs. on /sys/fs/smackfs.
access access
Provided for backward compatibility. The access2 interface
is preferred and should be used instead.
This interface reports whether a subject with the specified This interface reports whether a subject with the specified
Smack label has a particular access to an object with a Smack label has a particular access to an object with a
specified Smack label. Write a fixed format access rule to specified Smack label. Write a fixed format access rule to
...@@ -136,6 +142,8 @@ change-rule ...@@ -136,6 +142,8 @@ change-rule
those in the fourth string. If there is no such rule it will be those in the fourth string. If there is no such rule it will be
created using the access specified in the third and the fourth strings. created using the access specified in the third and the fourth strings.
cipso cipso
Provided for backward compatibility. The cipso2 interface
is preferred and should be used instead.
This interface allows a specific CIPSO header to be assigned This interface allows a specific CIPSO header to be assigned
to a Smack label. The format accepted on write is: to a Smack label. The format accepted on write is:
"%24s%4d%4d"["%4d"]... "%24s%4d%4d"["%4d"]...
...@@ -157,7 +165,19 @@ direct ...@@ -157,7 +165,19 @@ direct
doi doi
This contains the CIPSO domain of interpretation used in This contains the CIPSO domain of interpretation used in
network packets. network packets.
ipv6host
This interface allows specific IPv6 internet addresses to be
treated as single label hosts. Packets are sent to single
label hosts only from processes that have Smack write access
to the host label. All packets received from single label hosts
are given the specified label. The format accepted on write is:
"%h:%h:%h:%h:%h:%h:%h:%h label" or
"%h:%h:%h:%h:%h:%h:%h:%h/%d label".
The "::" address shortcut is not supported.
If label is "-DELETE" a matched entry will be deleted.
load load
Provided for backward compatibility. The load2 interface
is preferred and should be used instead.
This interface allows access control rules in addition to This interface allows access control rules in addition to
the system defined rules to be specified. The format accepted the system defined rules to be specified. The format accepted
on write is: on write is:
...@@ -181,6 +201,8 @@ load2 ...@@ -181,6 +201,8 @@ load2
permissions that are not allowed. The string "r-x--" would permissions that are not allowed. The string "r-x--" would
specify read and execute access. specify read and execute access.
load-self load-self
Provided for backward compatibility. The load-self2 interface
is preferred and should be used instead.
This interface allows process specific access rules to be This interface allows process specific access rules to be
defined. These rules are only consulted if access would defined. These rules are only consulted if access would
otherwise be permitted, and are intended to provide additional otherwise be permitted, and are intended to provide additional
...@@ -205,6 +227,8 @@ netlabel ...@@ -205,6 +227,8 @@ netlabel
received from single label hosts are given the specified received from single label hosts are given the specified
label. The format accepted on write is: label. The format accepted on write is:
"%d.%d.%d.%d label" or "%d.%d.%d.%d/%d label". "%d.%d.%d.%d label" or "%d.%d.%d.%d/%d label".
If the label specified is "-CIPSO" the address is treated
as a host that supports CIPSO headers.
onlycap onlycap
This contains labels processes must have for CAP_MAC_ADMIN This contains labels processes must have for CAP_MAC_ADMIN
and CAP_MAC_OVERRIDE to be effective. If this file is empty and CAP_MAC_OVERRIDE to be effective. If this file is empty
...@@ -232,7 +256,8 @@ unconfined ...@@ -232,7 +256,8 @@ unconfined
is dangerous and can ruin the proper labeling of your system. is dangerous and can ruin the proper labeling of your system.
It should never be used in production. It should never be used in production.
You can add access rules in /etc/smack/accesses. They take the form: If you are using the smackload utility
you can add access rules in /etc/smack/accesses. They take the form:
subjectlabel objectlabel access subjectlabel objectlabel access
......
...@@ -17,11 +17,26 @@ ...@@ -17,11 +17,26 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/lsm_hooks.h> #include <linux/lsm_hooks.h>
#include <linux/in.h> #include <linux/in.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <linux/in6.h>
#endif /* CONFIG_IPV6 */
#include <net/netlabel.h> #include <net/netlabel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/lsm_audit.h> #include <linux/lsm_audit.h>
/*
* Use IPv6 port labeling if IPv6 is enabled and secmarks
* are not being used.
*/
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
#define SMACK_IPV6_PORT_LABELING 1
#endif
#if IS_ENABLED(CONFIG_IPV6) && defined(CONFIG_SECURITY_SMACK_NETFILTER)
#define SMACK_IPV6_SECMARK_LABELING 1
#endif
/* /*
* Smack labels were limited to 23 characters for a long time. * Smack labels were limited to 23 characters for a long time.
*/ */
...@@ -118,15 +133,30 @@ struct smack_rule { ...@@ -118,15 +133,30 @@ struct smack_rule {
}; };
/* /*
* An entry in the table identifying hosts. * An entry in the table identifying IPv4 hosts.
*/ */
struct smk_netlbladdr { struct smk_net4addr {
struct list_head list; struct list_head list;
struct sockaddr_in smk_host; /* network address */ struct in_addr smk_host; /* network address */
struct in_addr smk_mask; /* network mask */ struct in_addr smk_mask; /* network mask */
int smk_masks; /* mask size */
struct smack_known *smk_label; /* label */
};
#if IS_ENABLED(CONFIG_IPV6)
/*
* An entry in the table identifying IPv6 hosts.
*/
struct smk_net6addr {
struct list_head list;
struct in6_addr smk_host; /* network address */
struct in6_addr smk_mask; /* network mask */
int smk_masks; /* mask size */
struct smack_known *smk_label; /* label */ struct smack_known *smk_label; /* label */
}; };
#endif /* CONFIG_IPV6 */
#ifdef SMACK_IPV6_PORT_LABELING
/* /*
* An entry in the table identifying ports. * An entry in the table identifying ports.
*/ */
...@@ -137,6 +167,7 @@ struct smk_port_label { ...@@ -137,6 +167,7 @@ struct smk_port_label {
struct smack_known *smk_in; /* inbound label */ struct smack_known *smk_in; /* inbound label */
struct smack_known *smk_out; /* outgoing label */ struct smack_known *smk_out; /* outgoing label */
}; };
#endif /* SMACK_IPV6_PORT_LABELING */
struct smack_onlycap { struct smack_onlycap {
struct list_head list; struct list_head list;
...@@ -170,6 +201,7 @@ enum { ...@@ -170,6 +201,7 @@ enum {
#define SMK_FSROOT "smackfsroot=" #define SMK_FSROOT "smackfsroot="
#define SMK_FSTRANS "smackfstransmute=" #define SMK_FSTRANS "smackfstransmute="
#define SMACK_DELETE_OPTION "-DELETE"
#define SMACK_CIPSO_OPTION "-CIPSO" #define SMACK_CIPSO_OPTION "-CIPSO"
/* /*
...@@ -252,10 +284,6 @@ struct smk_audit_info { ...@@ -252,10 +284,6 @@ struct smk_audit_info {
struct smack_audit_data sad; struct smack_audit_data sad;
#endif #endif
}; };
/*
* These functions are in smack_lsm.c
*/
struct inode_smack *new_inode_smack(struct smack_known *);
/* /*
* These functions are in smack_access.c * These functions are in smack_access.c
...@@ -285,7 +313,6 @@ extern struct smack_known *smack_syslog_label; ...@@ -285,7 +313,6 @@ extern struct smack_known *smack_syslog_label;
#ifdef CONFIG_SECURITY_SMACK_BRINGUP #ifdef CONFIG_SECURITY_SMACK_BRINGUP
extern struct smack_known *smack_unconfined; extern struct smack_known *smack_unconfined;
#endif #endif
extern struct smack_known smack_cipso_option;
extern int smack_ptrace_rule; extern int smack_ptrace_rule;
extern struct smack_known smack_known_floor; extern struct smack_known smack_known_floor;
...@@ -297,7 +324,10 @@ extern struct smack_known smack_known_web; ...@@ -297,7 +324,10 @@ extern struct smack_known smack_known_web;
extern struct mutex smack_known_lock; extern struct mutex smack_known_lock;
extern struct list_head smack_known_list; extern struct list_head smack_known_list;
extern struct list_head smk_netlbladdr_list; extern struct list_head smk_net4addr_list;
#if IS_ENABLED(CONFIG_IPV6)
extern struct list_head smk_net6addr_list;
#endif /* CONFIG_IPV6 */
extern struct mutex smack_onlycap_lock; extern struct mutex smack_onlycap_lock;
extern struct list_head smack_onlycap_list; extern struct list_head smack_onlycap_list;
......
...@@ -51,9 +51,9 @@ ...@@ -51,9 +51,9 @@
#define SMK_RECEIVING 1 #define SMK_RECEIVING 1
#define SMK_SENDING 2 #define SMK_SENDING 2
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER) #ifdef SMACK_IPV6_PORT_LABELING
LIST_HEAD(smk_ipv6_port_list); LIST_HEAD(smk_ipv6_port_list);
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */ #endif
static struct kmem_cache *smack_inode_cache; static struct kmem_cache *smack_inode_cache;
int smack_enabled; int smack_enabled;
...@@ -2272,7 +2272,7 @@ static void smack_sk_free_security(struct sock *sk) ...@@ -2272,7 +2272,7 @@ static void smack_sk_free_security(struct sock *sk)
} }
/** /**
* smack_host_label - check host based restrictions * smack_ipv4host_label - check host based restrictions
* @sip: the object end * @sip: the object end
* *
* looks for host based access restrictions * looks for host based access restrictions
...@@ -2283,30 +2283,96 @@ static void smack_sk_free_security(struct sock *sk) ...@@ -2283,30 +2283,96 @@ static void smack_sk_free_security(struct sock *sk)
* *
* Returns the label of the far end or NULL if it's not special. * Returns the label of the far end or NULL if it's not special.
*/ */
static struct smack_known *smack_host_label(struct sockaddr_in *sip) static struct smack_known *smack_ipv4host_label(struct sockaddr_in *sip)
{ {
struct smk_netlbladdr *snp; struct smk_net4addr *snp;
struct in_addr *siap = &sip->sin_addr; struct in_addr *siap = &sip->sin_addr;
if (siap->s_addr == 0) if (siap->s_addr == 0)
return NULL; return NULL;
list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) list_for_each_entry_rcu(snp, &smk_net4addr_list, list)
/*
* we break after finding the first match because
* the list is sorted from longest to shortest mask
* so we have found the most specific match
*/
if (snp->smk_host.s_addr ==
(siap->s_addr & snp->smk_mask.s_addr))
return snp->smk_label;
return NULL;
}
#if IS_ENABLED(CONFIG_IPV6)
/*
* smk_ipv6_localhost - Check for local ipv6 host address
* @sip: the address
*
* Returns boolean true if this is the localhost address
*/
static bool smk_ipv6_localhost(struct sockaddr_in6 *sip)
{
__be16 *be16p = (__be16 *)&sip->sin6_addr;
__be32 *be32p = (__be32 *)&sip->sin6_addr;
if (be32p[0] == 0 && be32p[1] == 0 && be32p[2] == 0 && be16p[6] == 0 &&
ntohs(be16p[7]) == 1)
return true;
return false;
}
/**
* smack_ipv6host_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. The caller is responsible for ensuring that the RCU read lock is
* taken before calling this function.
*
* Returns the label of the far end or NULL if it's not special.
*/
static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
{
struct smk_net6addr *snp;
struct in6_addr *sap = &sip->sin6_addr;
int i;
int found = 0;
/*
* It's local. Don't look for a host label.
*/
if (smk_ipv6_localhost(sip))
return NULL;
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
/* /*
* we break after finding the first match because * we break after finding the first match because
* the list is sorted from longest to shortest mask * the list is sorted from longest to shortest mask
* so we have found the most specific match * so we have found the most specific match
*/ */
if ((&snp->smk_host.sin_addr)->s_addr == for (found = 1, i = 0; i < 8; i++) {
(siap->s_addr & (&snp->smk_mask)->s_addr)) { /*
/* we have found the special CIPSO option */ * If the label is NULL the entry has
if (snp->smk_label == &smack_cipso_option) * been renounced. Ignore it.
return NULL; */
return snp->smk_label; if (snp->smk_label == NULL)
continue;
if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
snp->smk_host.s6_addr16[i]) {
found = 0;
break;
}
} }
if (found)
return snp->smk_label;
}
return NULL; return NULL;
} }
#endif /* CONFIG_IPV6 */
/** /**
* smack_netlabel - Set the secattr on a socket * smack_netlabel - Set the secattr on a socket
...@@ -2370,7 +2436,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) ...@@ -2370,7 +2436,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
struct smk_audit_info ad; struct smk_audit_info ad;
rcu_read_lock(); rcu_read_lock();
hkp = smack_host_label(sap); hkp = smack_ipv4host_label(sap);
if (hkp != NULL) { if (hkp != NULL) {
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
struct lsm_network_audit net; struct lsm_network_audit net;
...@@ -2395,7 +2461,42 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) ...@@ -2395,7 +2461,42 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
return smack_netlabel(sk, sk_lbl); return smack_netlabel(sk, sk_lbl);
} }
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER) #if IS_ENABLED(CONFIG_IPV6)
/**
* smk_ipv6_check - check Smack access
* @subject: subject Smack label
* @object: object Smack label
* @address: address
* @act: the action being taken
*
* Check an IPv6 access
*/
static int smk_ipv6_check(struct smack_known *subject,
struct smack_known *object,
struct sockaddr_in6 *address, int act)
{
#ifdef CONFIG_AUDIT
struct lsm_network_audit net;
#endif
struct smk_audit_info ad;
int rc;
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = PF_INET6;
ad.a.u.net->dport = ntohs(address->sin6_port);
if (act == SMK_RECEIVING)
ad.a.u.net->v6info.saddr = address->sin6_addr;
else
ad.a.u.net->v6info.daddr = address->sin6_addr;
#endif
rc = smk_access(subject, object, MAY_WRITE, &ad);
rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc);
return rc;
}
#endif /* CONFIG_IPV6 */
#ifdef SMACK_IPV6_PORT_LABELING
/** /**
* smk_ipv6_port_label - Smack port access table management * smk_ipv6_port_label - Smack port access table management
* @sock: socket * @sock: socket
...@@ -2479,48 +2580,43 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) ...@@ -2479,48 +2580,43 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
int act) int act)
{ {
__be16 *bep;
__be32 *be32p;
struct smk_port_label *spp; struct smk_port_label *spp;
struct socket_smack *ssp = sk->sk_security; struct socket_smack *ssp = sk->sk_security;
struct smack_known *skp; struct smack_known *skp = NULL;
unsigned short port = 0; unsigned short port;
struct smack_known *object; struct smack_known *object;
struct smk_audit_info ad;
int rc;
#ifdef CONFIG_AUDIT
struct lsm_network_audit net;
#endif
if (act == SMK_RECEIVING) { if (act == SMK_RECEIVING) {
skp = smack_net_ambient; skp = smack_ipv6host_label(address);
object = ssp->smk_in; object = ssp->smk_in;
} else { } else {
skp = ssp->smk_out; skp = ssp->smk_out;
object = smack_net_ambient; object = smack_ipv6host_label(address);
} }
/* /*
* Get the IP address and port from the address. * The other end is a single label host.
*/ */
port = ntohs(address->sin6_port); if (skp != NULL && object != NULL)
bep = (__be16 *)(&address->sin6_addr); return smk_ipv6_check(skp, object, address, act);
be32p = (__be32 *)(&address->sin6_addr); if (skp == NULL)
skp = smack_net_ambient;
if (object == NULL)
object = smack_net_ambient;
/* /*
* It's remote, so port lookup does no good. * It's remote, so port lookup does no good.
*/ */
if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1) if (!smk_ipv6_localhost(address))
goto auditout; return smk_ipv6_check(skp, object, address, act);
/* /*
* It's local so the send check has to have passed. * It's local so the send check has to have passed.
*/ */
if (act == SMK_RECEIVING) { if (act == SMK_RECEIVING)
skp = &smack_known_web; return 0;
goto auditout;
}
port = ntohs(address->sin6_port);
list_for_each_entry(spp, &smk_ipv6_port_list, list) { list_for_each_entry(spp, &smk_ipv6_port_list, list) {
if (spp->smk_port != port) if (spp->smk_port != port)
continue; continue;
...@@ -2530,22 +2626,9 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, ...@@ -2530,22 +2626,9 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
break; break;
} }
auditout: return smk_ipv6_check(skp, object, address, act);
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = sk->sk_family;
ad.a.u.net->dport = port;
if (act == SMK_RECEIVING)
ad.a.u.net->v6info.saddr = address->sin6_addr;
else
ad.a.u.net->v6info.daddr = address->sin6_addr;
#endif
rc = smk_access(skp, object, MAY_WRITE, &ad);
rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc);
return rc;
} }
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */ #endif /* SMACK_IPV6_PORT_LABELING */
/** /**
* smack_inode_setsecurity - set smack xattrs * smack_inode_setsecurity - set smack xattrs
...@@ -2606,10 +2689,10 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, ...@@ -2606,10 +2689,10 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
} else } else
return -EOPNOTSUPP; return -EOPNOTSUPP;
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER) #ifdef SMACK_IPV6_PORT_LABELING
if (sock->sk->sk_family == PF_INET6) if (sock->sk->sk_family == PF_INET6)
smk_ipv6_port_label(sock, NULL); smk_ipv6_port_label(sock, NULL);
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */ #endif
return 0; return 0;
} }
...@@ -2651,7 +2734,7 @@ static int smack_socket_post_create(struct socket *sock, int family, ...@@ -2651,7 +2734,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
} }
#ifndef CONFIG_SECURITY_SMACK_NETFILTER #ifdef SMACK_IPV6_PORT_LABELING
/** /**
* smack_socket_bind - record port binding information. * smack_socket_bind - record port binding information.
* @sock: the socket * @sock: the socket
...@@ -2665,14 +2748,11 @@ static int smack_socket_post_create(struct socket *sock, int family, ...@@ -2665,14 +2748,11 @@ static int smack_socket_post_create(struct socket *sock, int family,
static int smack_socket_bind(struct socket *sock, struct sockaddr *address, static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
int addrlen) int addrlen)
{ {
#if IS_ENABLED(CONFIG_IPV6)
if (sock->sk != NULL && sock->sk->sk_family == PF_INET6) if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
smk_ipv6_port_label(sock, address); smk_ipv6_port_label(sock, address);
#endif
return 0; return 0;
} }
#endif /* !CONFIG_SECURITY_SMACK_NETFILTER */ #endif /* SMACK_IPV6_PORT_LABELING */
/** /**
* smack_socket_connect - connect access check * smack_socket_connect - connect access check
...@@ -2688,6 +2768,13 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, ...@@ -2688,6 +2768,13 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
int addrlen) int addrlen)
{ {
int rc = 0; int rc = 0;
#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
#endif
#ifdef SMACK_IPV6_SECMARK_LABELING
struct smack_known *rsp;
struct socket_smack *ssp = sock->sk->sk_security;
#endif
if (sock->sk == NULL) if (sock->sk == NULL)
return 0; return 0;
...@@ -2701,10 +2788,15 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, ...@@ -2701,10 +2788,15 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
case PF_INET6: case PF_INET6:
if (addrlen < sizeof(struct sockaddr_in6)) if (addrlen < sizeof(struct sockaddr_in6))
return -EINVAL; return -EINVAL;
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER) #ifdef SMACK_IPV6_SECMARK_LABELING
rc = smk_ipv6_port_check(sock->sk, (struct sockaddr_in6 *)sap, rsp = smack_ipv6host_label(sip);
if (rsp != NULL)
rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
SMK_CONNECTING); SMK_CONNECTING);
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */ #endif
#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING);
#endif
break; break;
} }
return rc; return rc;
...@@ -3590,9 +3682,13 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, ...@@ -3590,9 +3682,13 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size) int size)
{ {
struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER) #if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name; struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */ #endif
#ifdef SMACK_IPV6_SECMARK_LABELING
struct socket_smack *ssp = sock->sk->sk_security;
struct smack_known *rsp;
#endif
int rc = 0; int rc = 0;
/* /*
...@@ -3606,9 +3702,15 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, ...@@ -3606,9 +3702,15 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
rc = smack_netlabel_send(sock->sk, sip); rc = smack_netlabel_send(sock->sk, sip);
break; break;
case AF_INET6: case AF_INET6:
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER) #ifdef SMACK_IPV6_SECMARK_LABELING
rsp = smack_ipv6host_label(sap);
if (rsp != NULL)
rc = smk_ipv6_check(ssp->smk_out, rsp, sap,
SMK_CONNECTING);
#endif
#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING); rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */ #endif
break; break;
} }
return rc; return rc;
...@@ -3822,10 +3924,12 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3822,10 +3924,12 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
proto = smk_skb_to_addr_ipv6(skb, &sadd); proto = smk_skb_to_addr_ipv6(skb, &sadd);
if (proto != IPPROTO_UDP && proto != IPPROTO_TCP) if (proto != IPPROTO_UDP && proto != IPPROTO_TCP)
break; break;
#ifdef CONFIG_SECURITY_SMACK_NETFILTER #ifdef SMACK_IPV6_SECMARK_LABELING
if (skb && skb->secmark != 0) if (skb && skb->secmark != 0)
skp = smack_from_secid(skb->secmark); skp = smack_from_secid(skb->secmark);
else else
skp = smack_ipv6host_label(&sadd);
if (skp == NULL)
skp = smack_net_ambient; skp = smack_net_ambient;
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
...@@ -3836,9 +3940,10 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3836,9 +3940,10 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in, rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in,
MAY_WRITE, rc); MAY_WRITE, rc);
#else /* CONFIG_SECURITY_SMACK_NETFILTER */ #endif /* SMACK_IPV6_SECMARK_LABELING */
#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING); rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */ #endif /* SMACK_IPV6_PORT_LABELING */
break; break;
#endif /* CONFIG_IPV6 */ #endif /* CONFIG_IPV6 */
} }
...@@ -3936,13 +4041,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, ...@@ -3936,13 +4041,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
} }
netlbl_secattr_destroy(&secattr); netlbl_secattr_destroy(&secattr);
break; break;
#if IS_ENABLED(CONFIG_IPV6)
case PF_INET6: case PF_INET6:
#ifdef CONFIG_SECURITY_SMACK_NETFILTER #ifdef SMACK_IPV6_SECMARK_LABELING
s = skb->secmark; s = skb->secmark;
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */ #endif
break; break;
#endif /* CONFIG_IPV6 */
} }
*secid = s; *secid = s;
if (s == 0) if (s == 0)
...@@ -4065,7 +4168,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -4065,7 +4168,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
hdr = ip_hdr(skb); hdr = ip_hdr(skb);
addr.sin_addr.s_addr = hdr->saddr; addr.sin_addr.s_addr = hdr->saddr;
rcu_read_lock(); rcu_read_lock();
hskp = smack_host_label(&addr); hskp = smack_ipv4host_label(&addr);
rcu_read_unlock(); rcu_read_unlock();
if (hskp == NULL) if (hskp == NULL)
...@@ -4517,9 +4620,9 @@ struct security_hook_list smack_hooks[] = { ...@@ -4517,9 +4620,9 @@ struct security_hook_list smack_hooks[] = {
LSM_HOOK_INIT(unix_may_send, smack_unix_may_send), LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
LSM_HOOK_INIT(socket_post_create, smack_socket_post_create), LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
#ifndef CONFIG_SECURITY_SMACK_NETFILTER #ifdef SMACK_IPV6_PORT_LABELING
LSM_HOOK_INIT(socket_bind, smack_socket_bind), LSM_HOOK_INIT(socket_bind, smack_socket_bind),
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */ #endif
LSM_HOOK_INIT(socket_connect, smack_socket_connect), LSM_HOOK_INIT(socket_connect, smack_socket_connect),
LSM_HOOK_INIT(socket_sendmsg, smack_socket_sendmsg), LSM_HOOK_INIT(socket_sendmsg, smack_socket_sendmsg),
LSM_HOOK_INIT(socket_sock_rcv_skb, smack_socket_sock_rcv_skb), LSM_HOOK_INIT(socket_sock_rcv_skb, smack_socket_sock_rcv_skb),
...@@ -4614,7 +4717,16 @@ static __init int smack_init(void) ...@@ -4614,7 +4717,16 @@ static __init int smack_init(void)
return -ENOMEM; return -ENOMEM;
} }
printk(KERN_INFO "Smack: Initializing.\n"); pr_info("Smack: Initializing.\n");
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
pr_info("Smack: Netfilter enabled.\n");
#endif
#ifdef SMACK_IPV6_PORT_LABELING
pr_info("Smack: IPv6 port labeling enabled.\n");
#endif
#ifdef SMACK_IPV6_SECMARK_LABELING
pr_info("Smack: IPv6 Netfilter enabled.\n");
#endif
/* /*
* Set the security state for the initial task. * Set the security state for the initial task.
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/magic.h> #include <linux/magic.h>
#include "smack.h" #include "smack.h"
#define BEBITS (sizeof(__be32) * 8)
/* /*
* smackfs pseudo filesystem. * smackfs pseudo filesystem.
*/ */
...@@ -40,7 +41,7 @@ enum smk_inos { ...@@ -40,7 +41,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_NETLBLADDR = 8, /* single label hosts */ SMK_NET4ADDR = 8, /* single label hosts */
SMK_ONLYCAP = 9, /* the only "capable" label */ SMK_ONLYCAP = 9, /* the only "capable" label */
SMK_LOGGING = 10, /* logging */ SMK_LOGGING = 10, /* logging */
SMK_LOAD_SELF = 11, /* task specific rules */ SMK_LOAD_SELF = 11, /* task specific rules */
...@@ -57,6 +58,9 @@ enum smk_inos { ...@@ -57,6 +58,9 @@ enum smk_inos {
#ifdef CONFIG_SECURITY_SMACK_BRINGUP #ifdef CONFIG_SECURITY_SMACK_BRINGUP
SMK_UNCONFINED = 22, /* define an unconfined label */ SMK_UNCONFINED = 22, /* define an unconfined label */
#endif #endif
#if IS_ENABLED(CONFIG_IPV6)
SMK_NET6ADDR = 23, /* single label IPv6 hosts */
#endif /* CONFIG_IPV6 */
}; };
/* /*
...@@ -64,7 +68,10 @@ enum smk_inos { ...@@ -64,7 +68,10 @@ enum smk_inos {
*/ */
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); static DEFINE_MUTEX(smk_net4addr_lock);
#if IS_ENABLED(CONFIG_IPV6)
static DEFINE_MUTEX(smk_net6addr_lock);
#endif /* CONFIG_IPV6 */
/* /*
* This is the "ambient" label for network traffic. * This is the "ambient" label for network traffic.
...@@ -118,7 +125,10 @@ int smack_ptrace_rule = SMACK_PTRACE_DEFAULT; ...@@ -118,7 +125,10 @@ int smack_ptrace_rule = SMACK_PTRACE_DEFAULT;
* can write to the specified label. * can write to the specified label.
*/ */
LIST_HEAD(smk_netlbladdr_list); LIST_HEAD(smk_net4addr_list);
#if IS_ENABLED(CONFIG_IPV6)
LIST_HEAD(smk_net6addr_list);
#endif /* CONFIG_IPV6 */
/* /*
* Rule lists are maintained for each label. * Rule lists are maintained for each label.
...@@ -140,11 +150,6 @@ struct smack_parsed_rule { ...@@ -140,11 +150,6 @@ struct smack_parsed_rule {
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
struct smack_known smack_cipso_option = {
.smk_known = SMACK_CIPSO_OPTION,
.smk_secid = 0,
};
/* /*
* Values for parsing cipso rules * Values for parsing cipso rules
* SMK_DIGITLEN: Length of a digit field in a rule. * SMK_DIGITLEN: Length of a digit field in a rule.
...@@ -1047,92 +1052,90 @@ static const struct file_operations smk_cipso2_ops = { ...@@ -1047,92 +1052,90 @@ static const struct file_operations smk_cipso2_ops = {
* Seq_file read operations for /smack/netlabel * Seq_file read operations for /smack/netlabel
*/ */
static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) static void *net4addr_seq_start(struct seq_file *s, loff_t *pos)
{ {
return smk_seq_start(s, pos, &smk_netlbladdr_list); return smk_seq_start(s, pos, &smk_net4addr_list);
} }
static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) static void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
{ {
return smk_seq_next(s, v, pos, &smk_netlbladdr_list); return smk_seq_next(s, v, pos, &smk_net4addr_list);
} }
#define BEBITS (sizeof(__be32) * 8)
/* /*
* Print host/label pairs * Print host/label pairs
*/ */
static int netlbladdr_seq_show(struct seq_file *s, void *v) static int net4addr_seq_show(struct seq_file *s, void *v)
{ {
struct list_head *list = v; struct list_head *list = v;
struct smk_netlbladdr *skp = struct smk_net4addr *skp =
list_entry_rcu(list, struct smk_netlbladdr, list); list_entry_rcu(list, struct smk_net4addr, list);
unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; char *kp = SMACK_CIPSO_OPTION;
int maskn;
u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
seq_printf(s, "%u.%u.%u.%u/%d %s\n", if (skp->smk_label != NULL)
hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known); kp = skp->smk_label->smk_known;
seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr,
skp->smk_masks, kp);
return 0; return 0;
} }
static const struct seq_operations netlbladdr_seq_ops = { static const struct seq_operations net4addr_seq_ops = {
.start = netlbladdr_seq_start, .start = net4addr_seq_start,
.next = netlbladdr_seq_next, .next = net4addr_seq_next,
.show = netlbladdr_seq_show, .show = net4addr_seq_show,
.stop = smk_seq_stop, .stop = smk_seq_stop,
}; };
/** /**
* smk_open_netlbladdr - open() for /smack/netlabel * smk_open_net4addr - open() for /smack/netlabel
* @inode: inode structure representing file * @inode: inode structure representing file
* @file: "netlabel" file pointer * @file: "netlabel" file pointer
* *
* Connect our netlbladdr_seq_* operations with /smack/netlabel * Connect our net4addr_seq_* operations with /smack/netlabel
* file_operations * file_operations
*/ */
static int smk_open_netlbladdr(struct inode *inode, struct file *file) static int smk_open_net4addr(struct inode *inode, struct file *file)
{ {
return seq_open(file, &netlbladdr_seq_ops); return seq_open(file, &net4addr_seq_ops);
} }
/** /**
* smk_netlbladdr_insert * smk_net4addr_insert
* @new : netlabel to insert * @new : netlabel to insert
* *
* This helper insert netlabel in the smack_netlbladdrs list * This helper insert netlabel in the smack_net4addrs list
* sorted by netmask length (longest to smallest) * sorted by netmask length (longest to smallest)
* locked by &smk_netlbladdr_lock in smk_write_netlbladdr * locked by &smk_net4addr_lock in smk_write_net4addr
* *
*/ */
static void smk_netlbladdr_insert(struct smk_netlbladdr *new) static void smk_net4addr_insert(struct smk_net4addr *new)
{ {
struct smk_netlbladdr *m, *m_next; struct smk_net4addr *m;
struct smk_net4addr *m_next;
if (list_empty(&smk_netlbladdr_list)) { if (list_empty(&smk_net4addr_list)) {
list_add_rcu(&new->list, &smk_netlbladdr_list); list_add_rcu(&new->list, &smk_net4addr_list);
return; return;
} }
m = list_entry_rcu(smk_netlbladdr_list.next, m = list_entry_rcu(smk_net4addr_list.next,
struct smk_netlbladdr, list); struct smk_net4addr, list);
/* the comparison '>' is a bit hacky, but works */ /* the comparison '>' is a bit hacky, but works */
if (new->smk_mask.s_addr > m->smk_mask.s_addr) { if (new->smk_masks > m->smk_masks) {
list_add_rcu(&new->list, &smk_netlbladdr_list); list_add_rcu(&new->list, &smk_net4addr_list);
return; return;
} }
list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) { list_for_each_entry_rcu(m, &smk_net4addr_list, list) {
if (list_is_last(&m->list, &smk_netlbladdr_list)) { if (list_is_last(&m->list, &smk_net4addr_list)) {
list_add_rcu(&new->list, &m->list); list_add_rcu(&new->list, &m->list);
return; return;
} }
m_next = list_entry_rcu(m->list.next, m_next = list_entry_rcu(m->list.next,
struct smk_netlbladdr, list); struct smk_net4addr, list);
if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) { if (new->smk_masks > m_next->smk_masks) {
list_add_rcu(&new->list, &m->list); list_add_rcu(&new->list, &m->list);
return; return;
} }
...@@ -1141,28 +1144,29 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new) ...@@ -1141,28 +1144,29 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
/** /**
* smk_write_netlbladdr - write() for /smack/netlabel * smk_write_net4addr - write() for /smack/netlabel
* @file: file pointer, not actually used * @file: file pointer, not actually used
* @buf: where to get the data from * @buf: where to get the data from
* @count: bytes sent * @count: bytes sent
* @ppos: where to start * @ppos: where to start
* *
* Accepts only one netlbladdr per write call. * Accepts only one net4addr per write call.
* Returns number of bytes written or error code, as appropriate * Returns number of bytes written or error code, as appropriate
*/ */
static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct smk_netlbladdr *snp; struct smk_net4addr *snp;
struct sockaddr_in newname; struct sockaddr_in newname;
char *smack; char *smack;
struct smack_known *skp; struct smack_known *skp = NULL;
char *data; char *data;
char *host = (char *)&newname.sin_addr.s_addr; char *host = (char *)&newname.sin_addr.s_addr;
int rc; int rc;
struct netlbl_audit audit_info; struct netlbl_audit audit_info;
struct in_addr mask; struct in_addr mask;
unsigned int m; unsigned int m;
unsigned int masks;
int found; int found;
u32 mask_bits = (1<<31); u32 mask_bits = (1<<31);
__be32 nsa; __be32 nsa;
...@@ -1200,7 +1204,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, ...@@ -1200,7 +1204,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
data[count] = '\0'; data[count] = '\0';
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s", rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",
&host[0], &host[1], &host[2], &host[3], &m, smack); &host[0], &host[1], &host[2], &host[3], &masks, smack);
if (rc != 6) { if (rc != 6) {
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s", rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
&host[0], &host[1], &host[2], &host[3], smack); &host[0], &host[1], &host[2], &host[3], smack);
...@@ -1209,8 +1213,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, ...@@ -1209,8 +1213,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
goto free_out; goto free_out;
} }
m = BEBITS; m = BEBITS;
masks = 32;
} }
if (m > BEBITS) { if (masks > BEBITS) {
rc = -EINVAL; rc = -EINVAL;
goto free_out; goto free_out;
} }
...@@ -1225,16 +1230,16 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, ...@@ -1225,16 +1230,16 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
goto free_out; goto free_out;
} }
} else { } else {
/* check known options */ /*
if (strcmp(smack, smack_cipso_option.smk_known) == 0) * Only the -CIPSO option is supported for IPv4
skp = &smack_cipso_option; */
else { if (strcmp(smack, SMACK_CIPSO_OPTION) != 0) {
rc = -EINVAL; rc = -EINVAL;
goto free_out; goto free_out;
} }
} }
for (temp_mask = 0; m > 0; m--) { for (m = masks, temp_mask = 0; m > 0; m--) {
temp_mask |= mask_bits; temp_mask |= mask_bits;
mask_bits >>= 1; mask_bits >>= 1;
} }
...@@ -1245,14 +1250,13 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, ...@@ -1245,14 +1250,13 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* Only allow one writer at a time. Writes should be * Only allow one writer at a time. Writes should be
* quite rare and small in any case. * quite rare and small in any case.
*/ */
mutex_lock(&smk_netlbladdr_lock); mutex_lock(&smk_net4addr_lock);
nsa = newname.sin_addr.s_addr; nsa = newname.sin_addr.s_addr;
/* try to find if the prefix is already in the list */ /* try to find if the prefix is already in the list */
found = 0; found = 0;
list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) { list_for_each_entry_rcu(snp, &smk_net4addr_list, list) {
if (snp->smk_host.sin_addr.s_addr == nsa && if (snp->smk_host.s_addr == nsa && snp->smk_masks == masks) {
snp->smk_mask.s_addr == mask.s_addr) {
found = 1; found = 1;
break; break;
} }
...@@ -1265,17 +1269,20 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, ...@@ -1265,17 +1269,20 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
rc = -ENOMEM; rc = -ENOMEM;
else { else {
rc = 0; rc = 0;
snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr; snp->smk_host.s_addr = newname.sin_addr.s_addr;
snp->smk_mask.s_addr = mask.s_addr; snp->smk_mask.s_addr = mask.s_addr;
snp->smk_label = skp; snp->smk_label = skp;
smk_netlbladdr_insert(snp); snp->smk_masks = masks;
smk_net4addr_insert(snp);
} }
} else { } else {
/* we delete the unlabeled entry, only if the previous label /*
* wasn't the special CIPSO option */ * Delete the unlabeled entry, only if the previous label
if (snp->smk_label != &smack_cipso_option) * wasn't the special CIPSO option
*/
if (snp->smk_label != NULL)
rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
&snp->smk_host.sin_addr, &snp->smk_mask, &snp->smk_host, &snp->smk_mask,
PF_INET, &audit_info); PF_INET, &audit_info);
else else
rc = 0; rc = 0;
...@@ -1287,15 +1294,279 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, ...@@ -1287,15 +1294,279 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* this host so that incoming packets get labeled. * this host so that incoming packets get labeled.
* but only if we didn't get the special CIPSO option * but only if we didn't get the special CIPSO option
*/ */
if (rc == 0 && skp != &smack_cipso_option) if (rc == 0 && skp != NULL)
rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
&snp->smk_host.sin_addr, &snp->smk_mask, PF_INET, &snp->smk_host, &snp->smk_mask, PF_INET,
snp->smk_label->smk_secid, &audit_info); snp->smk_label->smk_secid, &audit_info);
if (rc == 0) if (rc == 0)
rc = count; rc = count;
mutex_unlock(&smk_netlbladdr_lock); mutex_unlock(&smk_net4addr_lock);
free_out:
kfree(smack);
free_data_out:
kfree(data);
return rc;
}
static const struct file_operations smk_net4addr_ops = {
.open = smk_open_net4addr,
.read = seq_read,
.llseek = seq_lseek,
.write = smk_write_net4addr,
.release = seq_release,
};
#if IS_ENABLED(CONFIG_IPV6)
/*
* Seq_file read operations for /smack/netlabel6
*/
static void *net6addr_seq_start(struct seq_file *s, loff_t *pos)
{
return smk_seq_start(s, pos, &smk_net6addr_list);
}
static void *net6addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
return smk_seq_next(s, v, pos, &smk_net6addr_list);
}
/*
* Print host/label pairs
*/
static int net6addr_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smk_net6addr *skp =
list_entry(list, struct smk_net6addr, list);
if (skp->smk_label != NULL)
seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks,
skp->smk_label->smk_known);
return 0;
}
static const struct seq_operations net6addr_seq_ops = {
.start = net6addr_seq_start,
.next = net6addr_seq_next,
.show = net6addr_seq_show,
.stop = smk_seq_stop,
};
/**
* smk_open_net6addr - open() for /smack/netlabel
* @inode: inode structure representing file
* @file: "netlabel" file pointer
*
* Connect our net6addr_seq_* operations with /smack/netlabel
* file_operations
*/
static int smk_open_net6addr(struct inode *inode, struct file *file)
{
return seq_open(file, &net6addr_seq_ops);
}
/**
* smk_net6addr_insert
* @new : entry to insert
*
* This inserts an entry in the smack_net6addrs list
* sorted by netmask length (longest to smallest)
* locked by &smk_net6addr_lock in smk_write_net6addr
*
*/
static void smk_net6addr_insert(struct smk_net6addr *new)
{
struct smk_net6addr *m_next;
struct smk_net6addr *m;
if (list_empty(&smk_net6addr_list)) {
list_add_rcu(&new->list, &smk_net6addr_list);
return;
}
m = list_entry_rcu(smk_net6addr_list.next,
struct smk_net6addr, list);
if (new->smk_masks > m->smk_masks) {
list_add_rcu(&new->list, &smk_net6addr_list);
return;
}
list_for_each_entry_rcu(m, &smk_net6addr_list, list) {
if (list_is_last(&m->list, &smk_net6addr_list)) {
list_add_rcu(&new->list, &m->list);
return;
}
m_next = list_entry_rcu(m->list.next,
struct smk_net6addr, list);
if (new->smk_masks > m_next->smk_masks) {
list_add_rcu(&new->list, &m->list);
return;
}
}
}
/**
* smk_write_net6addr - write() for /smack/netlabel
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Accepts only one net6addr per write call.
* Returns number of bytes written or error code, as appropriate
*/
static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct smk_net6addr *snp;
struct in6_addr newname;
struct in6_addr fullmask;
struct smack_known *skp = NULL;
char *smack;
char *data;
int rc = 0;
int found = 0;
int i;
unsigned int scanned[8];
unsigned int m;
unsigned int mask = 128;
/*
* Must have privilege.
* No partial writes.
* Enough data must be present.
* "<addr/mask, as a:b:c:d:e:f:g:h/e><space><label>"
* "<addr, as a:b:c:d:e:f:g:h><space><label>"
*/
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
if (*ppos != 0)
return -EINVAL;
if (count < SMK_NETLBLADDRMIN)
return -EINVAL;
data = kzalloc(count + 1, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
if (copy_from_user(data, buf, count) != 0) {
rc = -EFAULT;
goto free_data_out;
}
smack = kzalloc(count + 1, GFP_KERNEL);
if (smack == NULL) {
rc = -ENOMEM;
goto free_data_out;
}
data[count] = '\0';
i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s",
&scanned[0], &scanned[1], &scanned[2], &scanned[3],
&scanned[4], &scanned[5], &scanned[6], &scanned[7],
&mask, smack);
if (i != 10) {
i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x %s",
&scanned[0], &scanned[1], &scanned[2],
&scanned[3], &scanned[4], &scanned[5],
&scanned[6], &scanned[7], smack);
if (i != 9) {
rc = -EINVAL;
goto free_out;
}
}
if (mask > 128) {
rc = -EINVAL;
goto free_out;
}
for (i = 0; i < 8; i++) {
if (scanned[i] > 0xffff) {
rc = -EINVAL;
goto free_out;
}
newname.s6_addr16[i] = htons(scanned[i]);
}
/*
* If smack begins with '-', it is an option, don't import it
*/
if (smack[0] != '-') {
skp = smk_import_entry(smack, 0);
if (skp == NULL) {
rc = -EINVAL;
goto free_out;
}
} else {
/*
* Only -DELETE is supported for IPv6
*/
if (strcmp(smack, SMACK_DELETE_OPTION) != 0) {
rc = -EINVAL;
goto free_out;
}
}
for (i = 0, m = mask; i < 8; i++) {
if (m >= 16) {
fullmask.s6_addr16[i] = 0xffff;
m -= 16;
} else if (m > 0) {
fullmask.s6_addr16[i] = (1 << m) - 1;
m = 0;
} else
fullmask.s6_addr16[i] = 0;
newname.s6_addr16[i] &= fullmask.s6_addr16[i];
}
/*
* Only allow one writer at a time. Writes should be
* quite rare and small in any case.
*/
mutex_lock(&smk_net6addr_lock);
/*
* Try to find the prefix in the list
*/
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
if (mask != snp->smk_masks)
continue;
for (found = 1, i = 0; i < 8; i++) {
if (newname.s6_addr16[i] !=
snp->smk_host.s6_addr16[i]) {
found = 0;
break;
}
}
if (found == 1)
break;
}
if (found == 0) {
snp = kzalloc(sizeof(*snp), GFP_KERNEL);
if (snp == NULL)
rc = -ENOMEM;
else {
snp->smk_host = newname;
snp->smk_mask = fullmask;
snp->smk_masks = mask;
snp->smk_label = skp;
smk_net6addr_insert(snp);
}
} else {
snp->smk_label = skp;
}
if (rc == 0)
rc = count;
mutex_unlock(&smk_net6addr_lock);
free_out: free_out:
kfree(smack); kfree(smack);
...@@ -1305,13 +1576,14 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, ...@@ -1305,13 +1576,14 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
return rc; return rc;
} }
static const struct file_operations smk_netlbladdr_ops = { static const struct file_operations smk_net6addr_ops = {
.open = smk_open_netlbladdr, .open = smk_open_net6addr,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.write = smk_write_netlbladdr, .write = smk_write_net6addr,
.release = seq_release, .release = seq_release,
}; };
#endif /* CONFIG_IPV6 */
/** /**
* smk_read_doi - read() for /smack/doi * smk_read_doi - read() for /smack/doi
...@@ -2515,8 +2787,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2515,8 +2787,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_NETLBLADDR] = { [SMK_NET4ADDR] = {
"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR}, "netlabel", &smk_net4addr_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},
[SMK_LOGGING] = { [SMK_LOGGING] = {
...@@ -2548,6 +2820,10 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2548,6 +2820,10 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
[SMK_UNCONFINED] = { [SMK_UNCONFINED] = {
"unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR}, "unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
#endif #endif
#if IS_ENABLED(CONFIG_IPV6)
[SMK_NET6ADDR] = {
"ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
#endif /* CONFIG_IPV6 */
/* 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