Smack LSM changes for Linux 5.10

Two kernel test robot suggested clean-ups.
 Teach Smack to use the IPv4 netlabel cache.
 This results in a 12-14% improvement on TCP benchmarks.
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCAA1FiEEC+9tH1YyUwIQzUIeOKUVfIxDyBEFAl+EkO0XHGNhc2V5QHNj
 aGF1Zmxlci1jYS5jb20ACgkQOKUVfIxDyBFmWBAAqJzkYd1Pd3g0vewZXGe5mRxs
 Qqt6i3HMJ7yAPIJN3445DyHUB6Sad2jmGXGullROfUtJYFkTbxqIj0XT+RUrJFap
 qf54Sgzt/ucNTvZ45s5mV6iYdL2ol7NSB9kQOMVMV0BZf2GXknYv0/rP23wI/upo
 Ylk41UYZNE5RDwoH90sy9aUUJ6VHhMYJOj33aUXz6rwaO1Ck6xvu1tCDudJpERkU
 kUmgpYzkNVGmg+GQIOT/xr7C7LRFxfnbSX0UL5TYPEKC23tYfkSBoClgCpNSSYSh
 3+7+YNRjMjxuKYJEPTUliDeDMQLxQB/3tLMD2c92nADtTJyRkTZelSgFhDFiOW0L
 ln5otQ0aGuDvkmLmBLk/jcq3eKctztqJIQdxQG9K3kJdohz1t/onJJt3cwuWZIo5
 T8dKz/mhga11u0ii6Zk+ecDdpagcxrQ8FDKcjI1tgXtTKAEvjfhVwAbj/9X55BGY
 T3M52b6RuE/ZRPD/BKXMaAUTNI0jwYbJyZYm+5GY/fk6lg0CRNaPBTVr0hj1Ia4P
 7akSD1gYUSTblFvjGa+hBsnmUDHo7Htl+kXJ3v6U8bSPFgFsO7hLbZJR6nRbxDvV
 k/KejR7RsORkbTFczWGngTKGI2UUNgMJf3Km9WU5bLqU3PpM32Hlal3CLMm3dQTB
 /3Gfl6aCy7Ct72tNilw=
 =EgBn
 -----END PGP SIGNATURE-----

Merge tag 'Smack-for-5.10' of git://github.com/cschaufler/smack-next

Pull smack updates from Casey Schaufler:
 "Two minor fixes and one performance enhancement to Smack. The
  performance improvement is significant and the new code is more like
  its counterpart in SELinux.

   - Two kernel test robot suggested clean-ups.

   - Teach Smack to use the IPv4 netlabel cache. This results in a
     12-14% improvement on TCP benchmarks"

* tag 'Smack-for-5.10' of git://github.com/cschaufler/smack-next:
  Smack: Remove unnecessary variable initialization
  Smack: Fix build when NETWORK_SECMARK is not set
  Smack: Use the netlabel cache
  Smack: Set socket labels only once
  Smack: Consolidate uses of secmark into a function
This commit is contained in:
Linus Torvalds 2020-10-13 16:18:51 -07:00
commit 99a6740f88
4 changed files with 200 additions and 149 deletions

View File

@ -100,7 +100,12 @@ struct socket_smack {
struct smack_known *smk_out; /* outbound label */
struct smack_known *smk_in; /* inbound label */
struct smack_known *smk_packet; /* TCP peer label */
int smk_state; /* netlabel socket states */
};
#define SMK_NETLBL_UNSET 0
#define SMK_NETLBL_UNLABELED 1
#define SMK_NETLBL_LABELED 2
#define SMK_NETLBL_REQSKB 3
/*
* Inode smack data
@ -196,19 +201,6 @@ enum {
#define SMACK_DELETE_OPTION "-DELETE"
#define SMACK_CIPSO_OPTION "-CIPSO"
/*
* 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
/*
* CIPSO defaults.
*/
@ -305,6 +297,7 @@ struct smack_known *smk_find_entry(const char *);
bool smack_privileged(int cap);
bool smack_privileged_cred(int cap, const struct cred *cred);
void smk_destroy_label_list(struct list_head *list);
int smack_populate_secattr(struct smack_known *skp);
/*
* Shared data.

View File

@ -510,6 +510,42 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
return 0;
}
/**
* smack_populate_secattr - fill in the smack_known netlabel information
* @skp: pointer to the structure to fill
*
* Populate the netlabel secattr structure for a Smack label.
*
* Returns 0 unless creating the category mapping fails
*/
int smack_populate_secattr(struct smack_known *skp)
{
int slen;
skp->smk_netlabel.attr.secid = skp->smk_secid;
skp->smk_netlabel.domain = skp->smk_known;
skp->smk_netlabel.cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
if (skp->smk_netlabel.cache != NULL) {
skp->smk_netlabel.flags |= NETLBL_SECATTR_CACHE;
skp->smk_netlabel.cache->free = NULL;
skp->smk_netlabel.cache->data = skp;
}
skp->smk_netlabel.flags |= NETLBL_SECATTR_SECID |
NETLBL_SECATTR_MLS_LVL |
NETLBL_SECATTR_DOMAIN;
/*
* If direct labeling works use it.
* Otherwise use mapped labeling.
*/
slen = strlen(skp->smk_known);
if (slen < SMK_CIPSOLEN)
return smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
&skp->smk_netlabel, slen);
return smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid,
&skp->smk_netlabel, sizeof(skp->smk_secid));
}
/**
* smk_import_entry - import a label, return the list entry
* @string: a text string that might be a Smack label
@ -523,7 +559,6 @@ struct smack_known *smk_import_entry(const char *string, int len)
{
struct smack_known *skp;
char *smack;
int slen;
int rc;
smack = smk_parse_smack(string, len);
@ -544,21 +579,8 @@ struct smack_known *smk_import_entry(const char *string, int len)
skp->smk_known = smack;
skp->smk_secid = smack_next_secid++;
skp->smk_netlabel.domain = skp->smk_known;
skp->smk_netlabel.flags =
NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
/*
* If direct labeling works use it.
* Otherwise use mapped labeling.
*/
slen = strlen(smack);
if (slen < SMK_CIPSOLEN)
rc = smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
&skp->smk_netlabel, slen);
else
rc = smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid,
&skp->smk_netlabel, sizeof(skp->smk_secid));
rc = smack_populate_secattr(skp);
if (rc >= 0) {
INIT_LIST_HEAD(&skp->smk_rules);
mutex_init(&skp->smk_rules_lock);
@ -569,9 +591,6 @@ struct smack_known *smk_import_entry(const char *string, int len)
smk_insert_entry(skp);
goto unlockout;
}
/*
* smk_netlbl_mls failed.
*/
kfree(skp);
skp = ERR_PTR(rc);
freeout:

View File

@ -2383,38 +2383,31 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
}
/**
* smack_netlabel - Set the secattr on a socket
* smack_netlbl_add - Set the secattr on a socket
* @sk: the socket
* @labeled: socket label scheme
*
* Convert the outbound smack value (smk_out) to a
* secattr and attach it to the socket.
* Attach the outbound smack value (smk_out) to the socket.
*
* Returns 0 on success or an error code
*/
static int smack_netlabel(struct sock *sk, int labeled)
static int smack_netlbl_add(struct sock *sk)
{
struct smack_known *skp;
struct socket_smack *ssp = sk->sk_security;
int rc = 0;
struct smack_known *skp = ssp->smk_out;
int rc;
/*
* Usually the netlabel code will handle changing the
* packet labeling based on the label.
* 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 {
skp = ssp->smk_out;
rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
switch (rc) {
case 0:
ssp->smk_state = SMK_NETLBL_LABELED;
break;
case -EDESTADDRREQ:
ssp->smk_state = SMK_NETLBL_REQSKB;
rc = 0;
break;
}
bh_unlock_sock(sk);
@ -2424,7 +2417,31 @@ static int smack_netlabel(struct sock *sk, int labeled)
}
/**
* smack_netlbel_send - Set the secattr on a socket and perform access checks
* smack_netlbl_delete - Remove the secattr from a socket
* @sk: the socket
*
* Remove the outbound smack value from a socket
*/
static void smack_netlbl_delete(struct sock *sk)
{
struct socket_smack *ssp = sk->sk_security;
/*
* Take the label off the socket if one is set.
*/
if (ssp->smk_state != SMK_NETLBL_LABELED)
return;
local_bh_disable();
bh_lock_sock_nested(sk);
netlbl_sock_delattr(sk);
bh_unlock_sock(sk);
local_bh_enable();
ssp->smk_state = SMK_NETLBL_UNLABELED;
}
/**
* smk_ipv4_check - Perform IPv4 host access checks
* @sk: the socket
* @sap: the destination address
*
@ -2434,11 +2451,10 @@ static int smack_netlabel(struct sock *sk, int labeled)
* Returns 0 on success or an error code.
*
*/
static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
static int smk_ipv4_check(struct sock *sk, struct sockaddr_in *sap)
{
struct smack_known *skp;
int rc;
int sk_lbl;
int rc = 0;
struct smack_known *hkp;
struct socket_smack *ssp = sk->sk_security;
struct smk_audit_info ad;
@ -2454,19 +2470,18 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
ad.a.u.net->dport = sap->sin_port;
ad.a.u.net->v4info.daddr = sap->sin_addr.s_addr;
#endif
sk_lbl = SMACK_UNLABELED_SOCKET;
skp = ssp->smk_out;
rc = smk_access(skp, hkp, MAY_WRITE, &ad);
rc = smk_bu_note("IPv4 host check", skp, hkp, MAY_WRITE, rc);
} else {
sk_lbl = SMACK_CIPSO_SOCKET;
rc = 0;
/*
* Clear the socket netlabel if it's set.
*/
if (!rc)
smack_netlbl_delete(sk);
}
rcu_read_unlock();
if (rc != 0)
return rc;
return smack_netlabel(sk, sk_lbl);
return rc;
}
/**
@ -2703,7 +2718,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
ssp->smk_out = skp;
if (sock->sk->sk_family == PF_INET) {
rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
rc = smack_netlbl_add(sock->sk);
if (rc != 0)
printk(KERN_WARNING
"Smack: \"%s\" netlbl error %d.\n",
@ -2754,7 +2769,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
/*
* Set the outbound netlbl.
*/
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
return smack_netlbl_add(sock->sk);
}
/**
@ -2845,7 +2860,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
}
if (sap->sa_family != AF_INET || addrlen < sizeof(struct sockaddr_in))
return 0;
rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap);
rc = smk_ipv4_check(sock->sk, (struct sockaddr_in *)sap);
return rc;
}
@ -3663,7 +3678,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
if (msg->msg_namelen < sizeof(struct sockaddr_in) ||
sip->sin_family != AF_INET)
return -EINVAL;
rc = smack_netlabel_send(sock->sk, sip);
rc = smk_ipv4_check(sock->sk, sip);
break;
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
@ -3700,6 +3715,18 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
int acat;
int kcat;
/*
* Netlabel found it in the cache.
*/
if ((sap->flags & NETLBL_SECATTR_CACHE) != 0)
return (struct smack_known *)sap->cache->data;
if ((sap->flags & NETLBL_SECATTR_SECID) != 0)
/*
* Looks like a fallback, which gives us a secid.
*/
return smack_from_secid(sap->attr.secid);
if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
/*
* Looks like a CIPSO packet.
@ -3747,11 +3774,6 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
return &smack_known_web;
return &smack_known_star;
}
if ((sap->flags & NETLBL_SECATTR_SECID) != 0)
/*
* Looks like a fallback, which gives us a secid.
*/
return smack_from_secid(sap->attr.secid);
/*
* Without guidance regarding the smack value
* for the packet fall back on the network
@ -3810,6 +3832,62 @@ static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr_in6 *sip)
}
#endif /* CONFIG_IPV6 */
/**
* smack_from_skb - Smack data from the secmark in an skb
* @skb: packet
*
* Returns smack_known of the secmark or NULL if that won't work.
*/
#ifdef CONFIG_NETWORK_SECMARK
static struct smack_known *smack_from_skb(struct sk_buff *skb)
{
if (skb == NULL || skb->secmark == 0)
return NULL;
return smack_from_secid(skb->secmark);
}
#else
static inline struct smack_known *smack_from_skb(struct sk_buff *skb)
{
return NULL;
}
#endif
/**
* smack_from_netlbl - Smack data from the IP options in an skb
* @sk: socket data came in on
* @family: address family
* @skb: packet
*
* Find the Smack label in the IP options. If it hasn't been
* added to the netlabel cache, add it here.
*
* Returns smack_known of the IP options or NULL if that won't work.
*/
static struct smack_known *smack_from_netlbl(struct sock *sk, u16 family,
struct sk_buff *skb)
{
struct netlbl_lsm_secattr secattr;
struct socket_smack *ssp = NULL;
struct smack_known *skp = NULL;
int rc;
netlbl_secattr_init(&secattr);
if (sk)
ssp = sk->sk_security;
if (netlbl_skbuff_getattr(skb, family, &secattr) == 0) {
skp = smack_from_secattr(&secattr, ssp);
if (secattr.flags & NETLBL_SECATTR_CACHEABLE)
rc = netlbl_cache_add(skb, family, &skp->smk_netlabel);
}
netlbl_secattr_destroy(&secattr);
return skp;
}
/**
* smack_socket_sock_rcv_skb - Smack packet delivery access check
* @sk: socket
@ -3819,7 +3897,6 @@ static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr_in6 *sip)
*/
static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
struct netlbl_lsm_secattr secattr;
struct socket_smack *ssp = sk->sk_security;
struct smack_known *skp = NULL;
int rc = 0;
@ -3838,33 +3915,18 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
switch (family) {
case PF_INET:
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
/*
* If there is a secmark use it rather than the CIPSO label.
* If there is no secmark fall back to CIPSO.
* The secmark is assumed to reflect policy better.
*/
if (skb && skb->secmark != 0) {
skp = smack_from_secid(skb->secmark);
goto access_check;
skp = smack_from_skb(skb);
if (skp == NULL) {
skp = smack_from_netlbl(sk, family, skb);
if (skp == NULL)
skp = smack_net_ambient;
}
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
/*
* Translate what netlabel gave us.
*/
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0)
skp = smack_from_secattr(&secattr, ssp);
else
skp = smack_net_ambient;
netlbl_secattr_destroy(&secattr);
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
access_check:
#endif
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = family;
@ -3890,16 +3952,14 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
proto != IPPROTO_TCP && proto != IPPROTO_DCCP)
break;
#ifdef SMACK_IPV6_SECMARK_LABELING
if (skb && skb->secmark != 0)
skp = smack_from_secid(skb->secmark);
else if (smk_ipv6_localhost(&sadd))
break;
else
skp = smack_from_skb(skb);
if (skp == NULL) {
if (smk_ipv6_localhost(&sadd))
break;
skp = smack_ipv6host_label(&sadd);
if (skp == NULL)
skp = smack_net_ambient;
if (skb == NULL)
break;
if (skp == NULL)
skp = smack_net_ambient;
}
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = family;
@ -3971,12 +4031,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
struct sk_buff *skb, u32 *secid)
{
struct netlbl_lsm_secattr secattr;
struct socket_smack *ssp = NULL;
struct smack_known *skp;
struct sock *sk = NULL;
int family = PF_UNSPEC;
u32 s = 0; /* 0 is the invalid secid */
int rc;
if (skb != NULL) {
if (skb->protocol == htons(ETH_P_IP))
@ -3995,27 +4054,25 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
s = ssp->smk_out->smk_secid;
break;
case PF_INET:
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
s = skb->secmark;
if (s != 0)
skp = smack_from_skb(skb);
if (skp) {
s = skp->smk_secid;
break;
#endif
}
/*
* Translate what netlabel gave us.
*/
if (sock != NULL && sock->sk != NULL)
ssp = sock->sk->sk_security;
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0) {
skp = smack_from_secattr(&secattr, ssp);
if (sock != NULL)
sk = sock->sk;
skp = smack_from_netlbl(sk, family, skb);
if (skp != NULL)
s = skp->smk_secid;
}
netlbl_secattr_destroy(&secattr);
break;
case PF_INET6:
#ifdef SMACK_IPV6_SECMARK_LABELING
s = skb->secmark;
skp = smack_from_skb(skb);
if (skp)
s = skp->smk_secid;
#endif
break;
}
@ -4063,7 +4120,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
u16 family = sk->sk_family;
struct smack_known *skp;
struct socket_smack *ssp = sk->sk_security;
struct netlbl_lsm_secattr secattr;
struct sockaddr_in addr;
struct iphdr *hdr;
struct smack_known *hskp;
@ -4087,29 +4143,17 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
}
#endif /* CONFIG_IPV6 */
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
/*
* If there is a secmark use it rather than the CIPSO label.
* If there is no secmark fall back to CIPSO.
* The secmark is assumed to reflect policy better.
*/
if (skb && skb->secmark != 0) {
skp = smack_from_secid(skb->secmark);
goto access_check;
skp = smack_from_skb(skb);
if (skp == NULL) {
skp = smack_from_netlbl(sk, family, skb);
if (skp == NULL)
skp = &smack_known_huh;
}
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0)
skp = smack_from_secattr(&secattr, ssp);
else
skp = &smack_known_huh;
netlbl_secattr_destroy(&secattr);
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
access_check:
#endif
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);

View File

@ -922,6 +922,10 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat;
skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl;
rc = count;
/*
* This mapping may have been cached, so clear the cache.
*/
netlbl_cache_invalidate();
}
out:
@ -2950,15 +2954,6 @@ static struct file_system_type smk_fs_type = {
static struct vfsmount *smackfs_mount;
static int __init smk_preset_netlabel(struct smack_known *skp)
{
skp->smk_netlabel.domain = skp->smk_known;
skp->smk_netlabel.flags =
NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
return smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
&skp->smk_netlabel, strlen(skp->smk_known));
}
/**
* init_smk_fs - get the smackfs superblock
*
@ -2997,19 +2992,19 @@ static int __init init_smk_fs(void)
smk_cipso_doi();
smk_unlbl_ambient(NULL);
rc = smk_preset_netlabel(&smack_known_floor);
rc = smack_populate_secattr(&smack_known_floor);
if (err == 0 && rc < 0)
err = rc;
rc = smk_preset_netlabel(&smack_known_hat);
rc = smack_populate_secattr(&smack_known_hat);
if (err == 0 && rc < 0)
err = rc;
rc = smk_preset_netlabel(&smack_known_huh);
rc = smack_populate_secattr(&smack_known_huh);
if (err == 0 && rc < 0)
err = rc;
rc = smk_preset_netlabel(&smack_known_star);
rc = smack_populate_secattr(&smack_known_star);
if (err == 0 && rc < 0)
err = rc;
rc = smk_preset_netlabel(&smack_known_web);
rc = smack_populate_secattr(&smack_known_web);
if (err == 0 && rc < 0)
err = rc;