18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SELinux NetLabel Support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file provides the necessary glue to tie NetLabel into the SELinux
68c2ecf20Sopenharmony_ci * subsystem.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Paul Moore <paul@paul-moore.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
168c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
178c2ecf20Sopenharmony_ci#include <linux/gfp.h>
188c2ecf20Sopenharmony_ci#include <linux/ip.h>
198c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
208c2ecf20Sopenharmony_ci#include <net/sock.h>
218c2ecf20Sopenharmony_ci#include <net/netlabel.h>
228c2ecf20Sopenharmony_ci#include <net/ip.h>
238c2ecf20Sopenharmony_ci#include <net/ipv6.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "objsec.h"
268c2ecf20Sopenharmony_ci#include "security.h"
278c2ecf20Sopenharmony_ci#include "netlabel.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/**
308c2ecf20Sopenharmony_ci * selinux_netlbl_sidlookup_cached - Cache a SID lookup
318c2ecf20Sopenharmony_ci * @skb: the packet
328c2ecf20Sopenharmony_ci * @secattr: the NetLabel security attributes
338c2ecf20Sopenharmony_ci * @sid: the SID
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * Description:
368c2ecf20Sopenharmony_ci * Query the SELinux security server to lookup the correct SID for the given
378c2ecf20Sopenharmony_ci * security attributes.  If the query is successful, cache the result to speed
388c2ecf20Sopenharmony_ci * up future lookups.  Returns zero on success, negative values on failure.
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cistatic int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
428c2ecf20Sopenharmony_ci					   u16 family,
438c2ecf20Sopenharmony_ci					   struct netlbl_lsm_secattr *secattr,
448c2ecf20Sopenharmony_ci					   u32 *sid)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int rc;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	rc = security_netlbl_secattr_to_sid(&selinux_state, secattr, sid);
498c2ecf20Sopenharmony_ci	if (rc == 0 &&
508c2ecf20Sopenharmony_ci	    (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
518c2ecf20Sopenharmony_ci	    (secattr->flags & NETLBL_SECATTR_CACHE))
528c2ecf20Sopenharmony_ci		netlbl_cache_add(skb, family, secattr);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return rc;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/**
588c2ecf20Sopenharmony_ci * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
598c2ecf20Sopenharmony_ci * @sk: the socket
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * Description:
628c2ecf20Sopenharmony_ci * Generate the NetLabel security attributes for a socket, making full use of
638c2ecf20Sopenharmony_ci * the socket's attribute cache.  Returns a pointer to the security attributes
648c2ecf20Sopenharmony_ci * on success, NULL on failure.
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistatic struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	int rc;
708c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
718c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr *secattr;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (sksec->nlbl_secattr != NULL)
748c2ecf20Sopenharmony_ci		return sksec->nlbl_secattr;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	secattr = netlbl_secattr_alloc(GFP_ATOMIC);
778c2ecf20Sopenharmony_ci	if (secattr == NULL)
788c2ecf20Sopenharmony_ci		return NULL;
798c2ecf20Sopenharmony_ci	rc = security_netlbl_sid_to_secattr(&selinux_state, sksec->sid,
808c2ecf20Sopenharmony_ci					    secattr);
818c2ecf20Sopenharmony_ci	if (rc != 0) {
828c2ecf20Sopenharmony_ci		netlbl_secattr_free(secattr);
838c2ecf20Sopenharmony_ci		return NULL;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci	sksec->nlbl_secattr = secattr;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return secattr;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/**
918c2ecf20Sopenharmony_ci * selinux_netlbl_sock_getattr - Get the cached NetLabel secattr
928c2ecf20Sopenharmony_ci * @sk: the socket
938c2ecf20Sopenharmony_ci * @sid: the SID
948c2ecf20Sopenharmony_ci *
958c2ecf20Sopenharmony_ci * Query the socket's cached secattr and if the SID matches the cached value
968c2ecf20Sopenharmony_ci * return the cache, otherwise return NULL.
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_cistatic struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr(
1008c2ecf20Sopenharmony_ci							const struct sock *sk,
1018c2ecf20Sopenharmony_ci							u32 sid)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
1048c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (secattr == NULL)
1078c2ecf20Sopenharmony_ci		return NULL;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if ((secattr->flags & NETLBL_SECATTR_SECID) &&
1108c2ecf20Sopenharmony_ci	    (secattr->attr.secid == sid))
1118c2ecf20Sopenharmony_ci		return secattr;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return NULL;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
1188c2ecf20Sopenharmony_ci *
1198c2ecf20Sopenharmony_ci * Description:
1208c2ecf20Sopenharmony_ci * Invalidate the NetLabel security attribute mapping cache.
1218c2ecf20Sopenharmony_ci *
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_civoid selinux_netlbl_cache_invalidate(void)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	netlbl_cache_invalidate();
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/**
1298c2ecf20Sopenharmony_ci * selinux_netlbl_err - Handle a NetLabel packet error
1308c2ecf20Sopenharmony_ci * @skb: the packet
1318c2ecf20Sopenharmony_ci * @error: the error code
1328c2ecf20Sopenharmony_ci * @gateway: true if host is acting as a gateway, false otherwise
1338c2ecf20Sopenharmony_ci *
1348c2ecf20Sopenharmony_ci * Description:
1358c2ecf20Sopenharmony_ci * When a packet is dropped due to a call to avc_has_perm() pass the error
1368c2ecf20Sopenharmony_ci * code to the NetLabel subsystem so any protocol specific processing can be
1378c2ecf20Sopenharmony_ci * done.  This is safe to call even if you are unsure if NetLabel labeling is
1388c2ecf20Sopenharmony_ci * present on the packet, NetLabel is smart enough to only act when it should.
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_civoid selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	netlbl_skbuff_err(skb, family, error, gateway);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/**
1478c2ecf20Sopenharmony_ci * selinux_netlbl_sk_security_free - Free the NetLabel fields
1488c2ecf20Sopenharmony_ci * @sksec: the sk_security_struct
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci * Description:
1518c2ecf20Sopenharmony_ci * Free all of the memory in the NetLabel fields of a sk_security_struct.
1528c2ecf20Sopenharmony_ci *
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_civoid selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	if (sksec->nlbl_secattr != NULL)
1578c2ecf20Sopenharmony_ci		netlbl_secattr_free(sksec->nlbl_secattr);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/**
1618c2ecf20Sopenharmony_ci * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
1628c2ecf20Sopenharmony_ci * @sksec: the sk_security_struct
1638c2ecf20Sopenharmony_ci * @family: the socket family
1648c2ecf20Sopenharmony_ci *
1658c2ecf20Sopenharmony_ci * Description:
1668c2ecf20Sopenharmony_ci * Called when the NetLabel state of a sk_security_struct needs to be reset.
1678c2ecf20Sopenharmony_ci * The caller is responsible for all the NetLabel sk_security_struct locking.
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_civoid selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	sksec->nlbl_state = NLBL_UNSET;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/**
1768c2ecf20Sopenharmony_ci * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
1778c2ecf20Sopenharmony_ci * @skb: the packet
1788c2ecf20Sopenharmony_ci * @family: protocol family
1798c2ecf20Sopenharmony_ci * @type: NetLabel labeling protocol type
1808c2ecf20Sopenharmony_ci * @sid: the SID
1818c2ecf20Sopenharmony_ci *
1828c2ecf20Sopenharmony_ci * Description:
1838c2ecf20Sopenharmony_ci * Call the NetLabel mechanism to get the security attributes of the given
1848c2ecf20Sopenharmony_ci * packet and use those attributes to determine the correct context/SID to
1858c2ecf20Sopenharmony_ci * assign to the packet.  Returns zero on success, negative values on failure.
1868c2ecf20Sopenharmony_ci *
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_ciint selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
1898c2ecf20Sopenharmony_ci				 u16 family,
1908c2ecf20Sopenharmony_ci				 u32 *type,
1918c2ecf20Sopenharmony_ci				 u32 *sid)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	int rc;
1948c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr secattr;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!netlbl_enabled()) {
1978c2ecf20Sopenharmony_ci		*sid = SECSID_NULL;
1988c2ecf20Sopenharmony_ci		return 0;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	netlbl_secattr_init(&secattr);
2028c2ecf20Sopenharmony_ci	rc = netlbl_skbuff_getattr(skb, family, &secattr);
2038c2ecf20Sopenharmony_ci	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
2048c2ecf20Sopenharmony_ci		rc = selinux_netlbl_sidlookup_cached(skb, family,
2058c2ecf20Sopenharmony_ci						     &secattr, sid);
2068c2ecf20Sopenharmony_ci	else
2078c2ecf20Sopenharmony_ci		*sid = SECSID_NULL;
2088c2ecf20Sopenharmony_ci	*type = secattr.type;
2098c2ecf20Sopenharmony_ci	netlbl_secattr_destroy(&secattr);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return rc;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/**
2158c2ecf20Sopenharmony_ci * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
2168c2ecf20Sopenharmony_ci * @skb: the packet
2178c2ecf20Sopenharmony_ci * @family: protocol family
2188c2ecf20Sopenharmony_ci * @sid: the SID
2198c2ecf20Sopenharmony_ci *
2208c2ecf20Sopenharmony_ci * Description
2218c2ecf20Sopenharmony_ci * Call the NetLabel mechanism to set the label of a packet using @sid.
2228c2ecf20Sopenharmony_ci * Returns zero on success, negative values on failure.
2238c2ecf20Sopenharmony_ci *
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_ciint selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
2268c2ecf20Sopenharmony_ci				 u16 family,
2278c2ecf20Sopenharmony_ci				 u32 sid)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	int rc;
2308c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr secattr_storage;
2318c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr *secattr = NULL;
2328c2ecf20Sopenharmony_ci	struct sock *sk;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* if this is a locally generated packet check to see if it is already
2358c2ecf20Sopenharmony_ci	 * being labeled by it's parent socket, if it is just exit */
2368c2ecf20Sopenharmony_ci	sk = skb_to_full_sk(skb);
2378c2ecf20Sopenharmony_ci	if (sk != NULL) {
2388c2ecf20Sopenharmony_ci		struct sk_security_struct *sksec = sk->sk_security;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		if (sksec->nlbl_state != NLBL_REQSKB)
2418c2ecf20Sopenharmony_ci			return 0;
2428c2ecf20Sopenharmony_ci		secattr = selinux_netlbl_sock_getattr(sk, sid);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci	if (secattr == NULL) {
2458c2ecf20Sopenharmony_ci		secattr = &secattr_storage;
2468c2ecf20Sopenharmony_ci		netlbl_secattr_init(secattr);
2478c2ecf20Sopenharmony_ci		rc = security_netlbl_sid_to_secattr(&selinux_state, sid,
2488c2ecf20Sopenharmony_ci						    secattr);
2498c2ecf20Sopenharmony_ci		if (rc != 0)
2508c2ecf20Sopenharmony_ci			goto skbuff_setsid_return;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	rc = netlbl_skbuff_setattr(skb, family, secattr);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ciskbuff_setsid_return:
2568c2ecf20Sopenharmony_ci	if (secattr == &secattr_storage)
2578c2ecf20Sopenharmony_ci		netlbl_secattr_destroy(secattr);
2588c2ecf20Sopenharmony_ci	return rc;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/**
2628c2ecf20Sopenharmony_ci * selinux_netlbl_sctp_assoc_request - Label an incoming sctp association.
2638c2ecf20Sopenharmony_ci * @ep: incoming association endpoint.
2648c2ecf20Sopenharmony_ci * @skb: the packet.
2658c2ecf20Sopenharmony_ci *
2668c2ecf20Sopenharmony_ci * Description:
2678c2ecf20Sopenharmony_ci * A new incoming connection is represented by @ep, ......
2688c2ecf20Sopenharmony_ci * Returns zero on success, negative values on failure.
2698c2ecf20Sopenharmony_ci *
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_ciint selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
2728c2ecf20Sopenharmony_ci				     struct sk_buff *skb)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	int rc;
2758c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr secattr;
2768c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = ep->base.sk->sk_security;
2778c2ecf20Sopenharmony_ci	struct sockaddr_in addr4;
2788c2ecf20Sopenharmony_ci	struct sockaddr_in6 addr6;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (ep->base.sk->sk_family != PF_INET &&
2818c2ecf20Sopenharmony_ci				ep->base.sk->sk_family != PF_INET6)
2828c2ecf20Sopenharmony_ci		return 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	netlbl_secattr_init(&secattr);
2858c2ecf20Sopenharmony_ci	rc = security_netlbl_sid_to_secattr(&selinux_state,
2868c2ecf20Sopenharmony_ci					    ep->secid, &secattr);
2878c2ecf20Sopenharmony_ci	if (rc != 0)
2888c2ecf20Sopenharmony_ci		goto assoc_request_return;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Move skb hdr address info to a struct sockaddr and then call
2918c2ecf20Sopenharmony_ci	 * netlbl_conn_setattr().
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci	if (ip_hdr(skb)->version == 4) {
2948c2ecf20Sopenharmony_ci		addr4.sin_family = AF_INET;
2958c2ecf20Sopenharmony_ci		addr4.sin_addr.s_addr = ip_hdr(skb)->saddr;
2968c2ecf20Sopenharmony_ci		rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr4, &secattr);
2978c2ecf20Sopenharmony_ci	} else if (IS_ENABLED(CONFIG_IPV6) && ip_hdr(skb)->version == 6) {
2988c2ecf20Sopenharmony_ci		addr6.sin6_family = AF_INET6;
2998c2ecf20Sopenharmony_ci		addr6.sin6_addr = ipv6_hdr(skb)->saddr;
3008c2ecf20Sopenharmony_ci		rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr6, &secattr);
3018c2ecf20Sopenharmony_ci	} else {
3028c2ecf20Sopenharmony_ci		rc = -EAFNOSUPPORT;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (rc == 0)
3068c2ecf20Sopenharmony_ci		sksec->nlbl_state = NLBL_LABELED;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ciassoc_request_return:
3098c2ecf20Sopenharmony_ci	netlbl_secattr_destroy(&secattr);
3108c2ecf20Sopenharmony_ci	return rc;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/**
3148c2ecf20Sopenharmony_ci * selinux_netlbl_inet_conn_request - Label an incoming stream connection
3158c2ecf20Sopenharmony_ci * @req: incoming connection request socket
3168c2ecf20Sopenharmony_ci *
3178c2ecf20Sopenharmony_ci * Description:
3188c2ecf20Sopenharmony_ci * A new incoming connection request is represented by @req, we need to label
3198c2ecf20Sopenharmony_ci * the new request_sock here and the stack will ensure the on-the-wire label
3208c2ecf20Sopenharmony_ci * will get preserved when a full sock is created once the connection handshake
3218c2ecf20Sopenharmony_ci * is complete.  Returns zero on success, negative values on failure.
3228c2ecf20Sopenharmony_ci *
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_ciint selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	int rc;
3278c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr secattr;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (family != PF_INET && family != PF_INET6)
3308c2ecf20Sopenharmony_ci		return 0;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	netlbl_secattr_init(&secattr);
3338c2ecf20Sopenharmony_ci	rc = security_netlbl_sid_to_secattr(&selinux_state, req->secid,
3348c2ecf20Sopenharmony_ci					    &secattr);
3358c2ecf20Sopenharmony_ci	if (rc != 0)
3368c2ecf20Sopenharmony_ci		goto inet_conn_request_return;
3378c2ecf20Sopenharmony_ci	rc = netlbl_req_setattr(req, &secattr);
3388c2ecf20Sopenharmony_ciinet_conn_request_return:
3398c2ecf20Sopenharmony_ci	netlbl_secattr_destroy(&secattr);
3408c2ecf20Sopenharmony_ci	return rc;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/**
3448c2ecf20Sopenharmony_ci * selinux_netlbl_inet_csk_clone - Initialize the newly created sock
3458c2ecf20Sopenharmony_ci * @sk: the new sock
3468c2ecf20Sopenharmony_ci *
3478c2ecf20Sopenharmony_ci * Description:
3488c2ecf20Sopenharmony_ci * A new connection has been established using @sk, we've already labeled the
3498c2ecf20Sopenharmony_ci * socket via the request_sock struct in selinux_netlbl_inet_conn_request() but
3508c2ecf20Sopenharmony_ci * we need to set the NetLabel state here since we now have a sock structure.
3518c2ecf20Sopenharmony_ci *
3528c2ecf20Sopenharmony_ci */
3538c2ecf20Sopenharmony_civoid selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (family == PF_INET)
3588c2ecf20Sopenharmony_ci		sksec->nlbl_state = NLBL_LABELED;
3598c2ecf20Sopenharmony_ci	else
3608c2ecf20Sopenharmony_ci		sksec->nlbl_state = NLBL_UNSET;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci/**
3648c2ecf20Sopenharmony_ci * selinux_netlbl_sctp_sk_clone - Copy state to the newly created sock
3658c2ecf20Sopenharmony_ci * @sk: current sock
3668c2ecf20Sopenharmony_ci * @newsk: the new sock
3678c2ecf20Sopenharmony_ci *
3688c2ecf20Sopenharmony_ci * Description:
3698c2ecf20Sopenharmony_ci * Called whenever a new socket is created by accept(2) or sctp_peeloff(3).
3708c2ecf20Sopenharmony_ci */
3718c2ecf20Sopenharmony_civoid selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
3748c2ecf20Sopenharmony_ci	struct sk_security_struct *newsksec = newsk->sk_security;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	newsksec->nlbl_state = sksec->nlbl_state;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/**
3808c2ecf20Sopenharmony_ci * selinux_netlbl_socket_post_create - Label a socket using NetLabel
3818c2ecf20Sopenharmony_ci * @sock: the socket to label
3828c2ecf20Sopenharmony_ci * @family: protocol family
3838c2ecf20Sopenharmony_ci *
3848c2ecf20Sopenharmony_ci * Description:
3858c2ecf20Sopenharmony_ci * Attempt to label a socket using the NetLabel mechanism using the given
3868c2ecf20Sopenharmony_ci * SID.  Returns zero values on success, negative values on failure.
3878c2ecf20Sopenharmony_ci *
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_ciint selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	int rc;
3928c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
3938c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr *secattr;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (family != PF_INET && family != PF_INET6)
3968c2ecf20Sopenharmony_ci		return 0;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	secattr = selinux_netlbl_sock_genattr(sk);
3998c2ecf20Sopenharmony_ci	if (secattr == NULL)
4008c2ecf20Sopenharmony_ci		return -ENOMEM;
4018c2ecf20Sopenharmony_ci	rc = netlbl_sock_setattr(sk, family, secattr);
4028c2ecf20Sopenharmony_ci	switch (rc) {
4038c2ecf20Sopenharmony_ci	case 0:
4048c2ecf20Sopenharmony_ci		sksec->nlbl_state = NLBL_LABELED;
4058c2ecf20Sopenharmony_ci		break;
4068c2ecf20Sopenharmony_ci	case -EDESTADDRREQ:
4078c2ecf20Sopenharmony_ci		sksec->nlbl_state = NLBL_REQSKB;
4088c2ecf20Sopenharmony_ci		rc = 0;
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return rc;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/**
4168c2ecf20Sopenharmony_ci * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
4178c2ecf20Sopenharmony_ci * @sksec: the sock's sk_security_struct
4188c2ecf20Sopenharmony_ci * @skb: the packet
4198c2ecf20Sopenharmony_ci * @family: protocol family
4208c2ecf20Sopenharmony_ci * @ad: the audit data
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * Description:
4238c2ecf20Sopenharmony_ci * Fetch the NetLabel security attributes from @skb and perform an access check
4248c2ecf20Sopenharmony_ci * against the receiving socket.  Returns zero on success, negative values on
4258c2ecf20Sopenharmony_ci * error.
4268c2ecf20Sopenharmony_ci *
4278c2ecf20Sopenharmony_ci */
4288c2ecf20Sopenharmony_ciint selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
4298c2ecf20Sopenharmony_ci				struct sk_buff *skb,
4308c2ecf20Sopenharmony_ci				u16 family,
4318c2ecf20Sopenharmony_ci				struct common_audit_data *ad)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	int rc;
4348c2ecf20Sopenharmony_ci	u32 nlbl_sid;
4358c2ecf20Sopenharmony_ci	u32 perm;
4368c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr secattr;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (!netlbl_enabled())
4398c2ecf20Sopenharmony_ci		return 0;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	netlbl_secattr_init(&secattr);
4428c2ecf20Sopenharmony_ci	rc = netlbl_skbuff_getattr(skb, family, &secattr);
4438c2ecf20Sopenharmony_ci	if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
4448c2ecf20Sopenharmony_ci		rc = selinux_netlbl_sidlookup_cached(skb, family,
4458c2ecf20Sopenharmony_ci						     &secattr, &nlbl_sid);
4468c2ecf20Sopenharmony_ci	else
4478c2ecf20Sopenharmony_ci		nlbl_sid = SECINITSID_UNLABELED;
4488c2ecf20Sopenharmony_ci	netlbl_secattr_destroy(&secattr);
4498c2ecf20Sopenharmony_ci	if (rc != 0)
4508c2ecf20Sopenharmony_ci		return rc;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	switch (sksec->sclass) {
4538c2ecf20Sopenharmony_ci	case SECCLASS_UDP_SOCKET:
4548c2ecf20Sopenharmony_ci		perm = UDP_SOCKET__RECVFROM;
4558c2ecf20Sopenharmony_ci		break;
4568c2ecf20Sopenharmony_ci	case SECCLASS_TCP_SOCKET:
4578c2ecf20Sopenharmony_ci		perm = TCP_SOCKET__RECVFROM;
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci	default:
4608c2ecf20Sopenharmony_ci		perm = RAWIP_SOCKET__RECVFROM;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	rc = avc_has_perm(&selinux_state,
4648c2ecf20Sopenharmony_ci			  sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
4658c2ecf20Sopenharmony_ci	if (rc == 0)
4668c2ecf20Sopenharmony_ci		return 0;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (nlbl_sid != SECINITSID_UNLABELED)
4698c2ecf20Sopenharmony_ci		netlbl_skbuff_err(skb, family, rc, 0);
4708c2ecf20Sopenharmony_ci	return rc;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/**
4748c2ecf20Sopenharmony_ci * selinux_netlbl_option - Is this a NetLabel option
4758c2ecf20Sopenharmony_ci * @level: the socket level or protocol
4768c2ecf20Sopenharmony_ci * @optname: the socket option name
4778c2ecf20Sopenharmony_ci *
4788c2ecf20Sopenharmony_ci * Description:
4798c2ecf20Sopenharmony_ci * Returns true if @level and @optname refer to a NetLabel option.
4808c2ecf20Sopenharmony_ci * Helper for selinux_netlbl_socket_setsockopt().
4818c2ecf20Sopenharmony_ci */
4828c2ecf20Sopenharmony_cistatic inline int selinux_netlbl_option(int level, int optname)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	return (level == IPPROTO_IP && optname == IP_OPTIONS) ||
4858c2ecf20Sopenharmony_ci		(level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS);
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci/**
4898c2ecf20Sopenharmony_ci * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
4908c2ecf20Sopenharmony_ci * @sock: the socket
4918c2ecf20Sopenharmony_ci * @level: the socket level or protocol
4928c2ecf20Sopenharmony_ci * @optname: the socket option name
4938c2ecf20Sopenharmony_ci *
4948c2ecf20Sopenharmony_ci * Description:
4958c2ecf20Sopenharmony_ci * Check the setsockopt() call and if the user is trying to replace the IP
4968c2ecf20Sopenharmony_ci * options on a socket and a NetLabel is in place for the socket deny the
4978c2ecf20Sopenharmony_ci * access; otherwise allow the access.  Returns zero when the access is
4988c2ecf20Sopenharmony_ci * allowed, -EACCES when denied, and other negative values on error.
4998c2ecf20Sopenharmony_ci *
5008c2ecf20Sopenharmony_ci */
5018c2ecf20Sopenharmony_ciint selinux_netlbl_socket_setsockopt(struct socket *sock,
5028c2ecf20Sopenharmony_ci				     int level,
5038c2ecf20Sopenharmony_ci				     int optname)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	int rc = 0;
5068c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
5078c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
5088c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr secattr;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (selinux_netlbl_option(level, optname) &&
5118c2ecf20Sopenharmony_ci	    (sksec->nlbl_state == NLBL_LABELED ||
5128c2ecf20Sopenharmony_ci	     sksec->nlbl_state == NLBL_CONNLABELED)) {
5138c2ecf20Sopenharmony_ci		netlbl_secattr_init(&secattr);
5148c2ecf20Sopenharmony_ci		lock_sock(sk);
5158c2ecf20Sopenharmony_ci		/* call the netlabel function directly as we want to see the
5168c2ecf20Sopenharmony_ci		 * on-the-wire label that is assigned via the socket's options
5178c2ecf20Sopenharmony_ci		 * and not the cached netlabel/lsm attributes */
5188c2ecf20Sopenharmony_ci		rc = netlbl_sock_getattr(sk, &secattr);
5198c2ecf20Sopenharmony_ci		release_sock(sk);
5208c2ecf20Sopenharmony_ci		if (rc == 0)
5218c2ecf20Sopenharmony_ci			rc = -EACCES;
5228c2ecf20Sopenharmony_ci		else if (rc == -ENOMSG)
5238c2ecf20Sopenharmony_ci			rc = 0;
5248c2ecf20Sopenharmony_ci		netlbl_secattr_destroy(&secattr);
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return rc;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/**
5318c2ecf20Sopenharmony_ci * selinux_netlbl_socket_connect_helper - Help label a client-side socket on
5328c2ecf20Sopenharmony_ci * connect
5338c2ecf20Sopenharmony_ci * @sk: the socket to label
5348c2ecf20Sopenharmony_ci * @addr: the destination address
5358c2ecf20Sopenharmony_ci *
5368c2ecf20Sopenharmony_ci * Description:
5378c2ecf20Sopenharmony_ci * Attempt to label a connected socket with NetLabel using the given address.
5388c2ecf20Sopenharmony_ci * Returns zero values on success, negative values on failure.
5398c2ecf20Sopenharmony_ci *
5408c2ecf20Sopenharmony_ci */
5418c2ecf20Sopenharmony_cistatic int selinux_netlbl_socket_connect_helper(struct sock *sk,
5428c2ecf20Sopenharmony_ci						struct sockaddr *addr)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	int rc;
5458c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
5468c2ecf20Sopenharmony_ci	struct netlbl_lsm_secattr *secattr;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* connected sockets are allowed to disconnect when the address family
5498c2ecf20Sopenharmony_ci	 * is set to AF_UNSPEC, if that is what is happening we want to reset
5508c2ecf20Sopenharmony_ci	 * the socket */
5518c2ecf20Sopenharmony_ci	if (addr->sa_family == AF_UNSPEC) {
5528c2ecf20Sopenharmony_ci		netlbl_sock_delattr(sk);
5538c2ecf20Sopenharmony_ci		sksec->nlbl_state = NLBL_REQSKB;
5548c2ecf20Sopenharmony_ci		rc = 0;
5558c2ecf20Sopenharmony_ci		return rc;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci	secattr = selinux_netlbl_sock_genattr(sk);
5588c2ecf20Sopenharmony_ci	if (secattr == NULL) {
5598c2ecf20Sopenharmony_ci		rc = -ENOMEM;
5608c2ecf20Sopenharmony_ci		return rc;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci	rc = netlbl_conn_setattr(sk, addr, secattr);
5638c2ecf20Sopenharmony_ci	if (rc == 0)
5648c2ecf20Sopenharmony_ci		sksec->nlbl_state = NLBL_CONNLABELED;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	return rc;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/**
5708c2ecf20Sopenharmony_ci * selinux_netlbl_socket_connect_locked - Label a client-side socket on
5718c2ecf20Sopenharmony_ci * connect
5728c2ecf20Sopenharmony_ci * @sk: the socket to label
5738c2ecf20Sopenharmony_ci * @addr: the destination address
5748c2ecf20Sopenharmony_ci *
5758c2ecf20Sopenharmony_ci * Description:
5768c2ecf20Sopenharmony_ci * Attempt to label a connected socket that already has the socket locked
5778c2ecf20Sopenharmony_ci * with NetLabel using the given address.
5788c2ecf20Sopenharmony_ci * Returns zero values on success, negative values on failure.
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci */
5818c2ecf20Sopenharmony_ciint selinux_netlbl_socket_connect_locked(struct sock *sk,
5828c2ecf20Sopenharmony_ci					 struct sockaddr *addr)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct sk_security_struct *sksec = sk->sk_security;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (sksec->nlbl_state != NLBL_REQSKB &&
5878c2ecf20Sopenharmony_ci	    sksec->nlbl_state != NLBL_CONNLABELED)
5888c2ecf20Sopenharmony_ci		return 0;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	return selinux_netlbl_socket_connect_helper(sk, addr);
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci/**
5948c2ecf20Sopenharmony_ci * selinux_netlbl_socket_connect - Label a client-side socket on connect
5958c2ecf20Sopenharmony_ci * @sk: the socket to label
5968c2ecf20Sopenharmony_ci * @addr: the destination address
5978c2ecf20Sopenharmony_ci *
5988c2ecf20Sopenharmony_ci * Description:
5998c2ecf20Sopenharmony_ci * Attempt to label a connected socket with NetLabel using the given address.
6008c2ecf20Sopenharmony_ci * Returns zero values on success, negative values on failure.
6018c2ecf20Sopenharmony_ci *
6028c2ecf20Sopenharmony_ci */
6038c2ecf20Sopenharmony_ciint selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	int rc;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	lock_sock(sk);
6088c2ecf20Sopenharmony_ci	rc = selinux_netlbl_socket_connect_locked(sk, addr);
6098c2ecf20Sopenharmony_ci	release_sock(sk);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return rc;
6128c2ecf20Sopenharmony_ci}
613