// SPDX-License-Identifier: GPL-2.0-or-later1/*2* SELinux NetLabel Support3*4* This file provides the necessary glue to tie NetLabel into the SELinux5* subsystem.6*7* Author: Paul Moore <[email protected]>8*/910/*11* (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 200812*/1314#include <linux/spinlock.h>15#include <linux/rcupdate.h>16#include <linux/gfp.h>17#include <linux/ip.h>18#include <linux/ipv6.h>19#include <linux/lsm_hooks.h>20#include <net/sock.h>21#include <net/netlabel.h>22#include <net/ip.h>23#include <net/ipv6.h>2425#include "objsec.h"26#include "security.h"27#include "netlabel.h"2829/**30* selinux_netlbl_sidlookup_cached - Cache a SID lookup31* @skb: the packet32* @family: the packet's address family33* @secattr: the NetLabel security attributes34* @sid: the SID35*36* Description:37* Query the SELinux security server to lookup the correct SID for the given38* security attributes. If the query is successful, cache the result to speed39* up future lookups. Returns zero on success, negative values on failure.40*41*/42static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,43u16 family,44struct netlbl_lsm_secattr *secattr,45u32 *sid)46{47int rc;4849rc = security_netlbl_secattr_to_sid(secattr, sid);50if (rc == 0 &&51(secattr->flags & NETLBL_SECATTR_CACHEABLE) &&52(secattr->flags & NETLBL_SECATTR_CACHE))53netlbl_cache_add(skb, family, secattr);5455return rc;56}5758/**59* selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr60* @sk: the socket61*62* Description:63* Generate the NetLabel security attributes for a socket, making full use of64* the socket's attribute cache. Returns a pointer to the security attributes65* on success, or an ERR_PTR on failure.66*67*/68static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)69{70int rc;71struct sk_security_struct *sksec = selinux_sock(sk);72struct netlbl_lsm_secattr *secattr;7374if (sksec->nlbl_secattr != NULL)75return sksec->nlbl_secattr;7677secattr = netlbl_secattr_alloc(GFP_ATOMIC);78if (secattr == NULL)79return ERR_PTR(-ENOMEM);8081rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);82if (rc != 0) {83netlbl_secattr_free(secattr);84return ERR_PTR(rc);85}86sksec->nlbl_secattr = secattr;8788return secattr;89}9091/**92* selinux_netlbl_sock_getattr - Get the cached NetLabel secattr93* @sk: the socket94* @sid: the SID95*96* Query the socket's cached secattr and if the SID matches the cached value97* return the cache, otherwise return NULL.98*99*/100static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr(101const struct sock *sk,102u32 sid)103{104struct sk_security_struct *sksec = selinux_sock(sk);105struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;106107if (secattr == NULL)108return NULL;109110if ((secattr->flags & NETLBL_SECATTR_SECID) &&111(secattr->attr.secid == sid))112return secattr;113114return NULL;115}116117/**118* selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache119*120* Description:121* Invalidate the NetLabel security attribute mapping cache.122*123*/124void selinux_netlbl_cache_invalidate(void)125{126netlbl_cache_invalidate();127}128129/**130* selinux_netlbl_err - Handle a NetLabel packet error131* @skb: the packet132* @family: the packet's address family133* @error: the error code134* @gateway: true if host is acting as a gateway, false otherwise135*136* Description:137* When a packet is dropped due to a call to avc_has_perm() pass the error138* code to the NetLabel subsystem so any protocol specific processing can be139* done. This is safe to call even if you are unsure if NetLabel labeling is140* present on the packet, NetLabel is smart enough to only act when it should.141*142*/143void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)144{145netlbl_skbuff_err(skb, family, error, gateway);146}147148/**149* selinux_netlbl_sk_security_free - Free the NetLabel fields150* @sksec: the sk_security_struct151*152* Description:153* Free all of the memory in the NetLabel fields of a sk_security_struct.154*155*/156void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)157{158if (!sksec->nlbl_secattr)159return;160161netlbl_secattr_free(sksec->nlbl_secattr);162sksec->nlbl_secattr = NULL;163sksec->nlbl_state = NLBL_UNSET;164}165166/**167* selinux_netlbl_sk_security_reset - Reset the NetLabel fields168* @sksec: the sk_security_struct169*170* Description:171* Called when the NetLabel state of a sk_security_struct needs to be reset.172* The caller is responsible for all the NetLabel sk_security_struct locking.173*174*/175void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec)176{177sksec->nlbl_state = NLBL_UNSET;178}179180/**181* selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel182* @skb: the packet183* @family: protocol family184* @type: NetLabel labeling protocol type185* @sid: the SID186*187* Description:188* Call the NetLabel mechanism to get the security attributes of the given189* packet and use those attributes to determine the correct context/SID to190* assign to the packet. Returns zero on success, negative values on failure.191*192*/193int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,194u16 family,195u32 *type,196u32 *sid)197{198int rc;199struct netlbl_lsm_secattr secattr;200201if (!netlbl_enabled()) {202*type = NETLBL_NLTYPE_NONE;203*sid = SECSID_NULL;204return 0;205}206207netlbl_secattr_init(&secattr);208rc = netlbl_skbuff_getattr(skb, family, &secattr);209if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)210rc = selinux_netlbl_sidlookup_cached(skb, family,211&secattr, sid);212else213*sid = SECSID_NULL;214*type = secattr.type;215netlbl_secattr_destroy(&secattr);216217return rc;218}219220/**221* selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid222* @skb: the packet223* @family: protocol family224* @sid: the SID225*226* Description227* Call the NetLabel mechanism to set the label of a packet using @sid.228* Returns zero on success, negative values on failure.229*230*/231int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,232u16 family,233u32 sid)234{235int rc;236struct netlbl_lsm_secattr secattr_storage;237struct netlbl_lsm_secattr *secattr = NULL;238struct sock *sk;239240/* if this is a locally generated packet check to see if it is already241* being labeled by it's parent socket, if it is just exit */242sk = skb_to_full_sk(skb);243if (sk != NULL) {244struct sk_security_struct *sksec = selinux_sock(sk);245246if (sksec->nlbl_state != NLBL_REQSKB)247return 0;248secattr = selinux_netlbl_sock_getattr(sk, sid);249}250if (secattr == NULL) {251secattr = &secattr_storage;252netlbl_secattr_init(secattr);253rc = security_netlbl_sid_to_secattr(sid, secattr);254if (rc != 0)255goto skbuff_setsid_return;256}257258rc = netlbl_skbuff_setattr(skb, family, secattr);259260skbuff_setsid_return:261if (secattr == &secattr_storage)262netlbl_secattr_destroy(secattr);263return rc;264}265266/**267* selinux_netlbl_sctp_assoc_request - Label an incoming sctp association.268* @asoc: incoming association.269* @skb: the packet.270*271* Description:272* A new incoming connection is represented by @asoc, ......273* Returns zero on success, negative values on failure.274*275*/276int selinux_netlbl_sctp_assoc_request(struct sctp_association *asoc,277struct sk_buff *skb)278{279int rc;280struct netlbl_lsm_secattr secattr;281struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);282struct sockaddr_in addr4;283struct sockaddr_in6 addr6;284285if (asoc->base.sk->sk_family != PF_INET &&286asoc->base.sk->sk_family != PF_INET6)287return 0;288289netlbl_secattr_init(&secattr);290rc = security_netlbl_sid_to_secattr(asoc->secid, &secattr);291if (rc != 0)292goto assoc_request_return;293294/* Move skb hdr address info to a struct sockaddr and then call295* netlbl_conn_setattr().296*/297if (ip_hdr(skb)->version == 4) {298addr4.sin_family = AF_INET;299addr4.sin_addr.s_addr = ip_hdr(skb)->saddr;300rc = netlbl_conn_setattr(asoc->base.sk, (void *)&addr4, &secattr);301} else if (IS_ENABLED(CONFIG_IPV6) && ip_hdr(skb)->version == 6) {302addr6.sin6_family = AF_INET6;303addr6.sin6_addr = ipv6_hdr(skb)->saddr;304rc = netlbl_conn_setattr(asoc->base.sk, (void *)&addr6, &secattr);305} else {306rc = -EAFNOSUPPORT;307}308309if (rc == 0)310sksec->nlbl_state = NLBL_LABELED;311312assoc_request_return:313netlbl_secattr_destroy(&secattr);314return rc;315}316317/**318* selinux_netlbl_inet_conn_request - Label an incoming stream connection319* @req: incoming connection request socket320* @family: the request socket's address family321*322* Description:323* A new incoming connection request is represented by @req, we need to label324* the new request_sock here and the stack will ensure the on-the-wire label325* will get preserved when a full sock is created once the connection handshake326* is complete. Returns zero on success, negative values on failure.327*328*/329int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)330{331int rc;332struct netlbl_lsm_secattr secattr;333334if (family != PF_INET && family != PF_INET6)335return 0;336337netlbl_secattr_init(&secattr);338rc = security_netlbl_sid_to_secattr(req->secid, &secattr);339if (rc != 0)340goto inet_conn_request_return;341rc = netlbl_req_setattr(req, &secattr);342inet_conn_request_return:343netlbl_secattr_destroy(&secattr);344return rc;345}346347/**348* selinux_netlbl_inet_csk_clone - Initialize the newly created sock349* @sk: the new sock350* @family: the sock's address family351*352* Description:353* A new connection has been established using @sk, we've already labeled the354* socket via the request_sock struct in selinux_netlbl_inet_conn_request() but355* we need to set the NetLabel state here since we now have a sock structure.356*357*/358void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)359{360struct sk_security_struct *sksec = selinux_sock(sk);361362if (family == PF_INET || family == PF_INET6)363sksec->nlbl_state = NLBL_LABELED;364else365sksec->nlbl_state = NLBL_UNSET;366}367368/**369* selinux_netlbl_sctp_sk_clone - Copy state to the newly created sock370* @sk: current sock371* @newsk: the new sock372*373* Description:374* Called whenever a new socket is created by accept(2) or sctp_peeloff(3).375*/376void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)377{378struct sk_security_struct *sksec = selinux_sock(sk);379struct sk_security_struct *newsksec = selinux_sock(newsk);380381newsksec->nlbl_state = sksec->nlbl_state;382}383384/**385* selinux_netlbl_socket_post_create - Label a socket using NetLabel386* @sk: the sock to label387* @family: protocol family388*389* Description:390* Attempt to label a socket using the NetLabel mechanism using the given391* SID. Returns zero values on success, negative values on failure.392*393*/394int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)395{396int rc;397struct sk_security_struct *sksec = selinux_sock(sk);398struct netlbl_lsm_secattr *secattr;399400if (family != PF_INET && family != PF_INET6)401return 0;402403secattr = selinux_netlbl_sock_genattr(sk);404if (IS_ERR(secattr))405return PTR_ERR(secattr);406/* On socket creation, replacement of IP options is safe even if407* the caller does not hold the socket lock.408*/409rc = netlbl_sock_setattr(sk, family, secattr, true);410switch (rc) {411case 0:412sksec->nlbl_state = NLBL_LABELED;413break;414case -EDESTADDRREQ:415sksec->nlbl_state = NLBL_REQSKB;416rc = 0;417break;418}419420return rc;421}422423/**424* selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel425* @sksec: the sock's sk_security_struct426* @skb: the packet427* @family: protocol family428* @ad: the audit data429*430* Description:431* Fetch the NetLabel security attributes from @skb and perform an access check432* against the receiving socket. Returns zero on success, negative values on433* error.434*435*/436int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,437struct sk_buff *skb,438u16 family,439struct common_audit_data *ad)440{441int rc;442u32 nlbl_sid;443u32 perm;444struct netlbl_lsm_secattr secattr;445446if (!netlbl_enabled())447return 0;448449netlbl_secattr_init(&secattr);450rc = netlbl_skbuff_getattr(skb, family, &secattr);451if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)452rc = selinux_netlbl_sidlookup_cached(skb, family,453&secattr, &nlbl_sid);454else455nlbl_sid = SECINITSID_UNLABELED;456netlbl_secattr_destroy(&secattr);457if (rc != 0)458return rc;459460switch (sksec->sclass) {461case SECCLASS_UDP_SOCKET:462perm = UDP_SOCKET__RECVFROM;463break;464case SECCLASS_TCP_SOCKET:465perm = TCP_SOCKET__RECVFROM;466break;467default:468perm = RAWIP_SOCKET__RECVFROM;469}470471rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);472if (rc == 0)473return 0;474475if (nlbl_sid != SECINITSID_UNLABELED)476netlbl_skbuff_err(skb, family, rc, 0);477return rc;478}479480/**481* selinux_netlbl_option - Is this a NetLabel option482* @level: the socket level or protocol483* @optname: the socket option name484*485* Description:486* Returns true if @level and @optname refer to a NetLabel option.487* Helper for selinux_netlbl_socket_setsockopt().488*/489static inline int selinux_netlbl_option(int level, int optname)490{491return (level == IPPROTO_IP && optname == IP_OPTIONS) ||492(level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS);493}494495/**496* selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel497* @sock: the socket498* @level: the socket level or protocol499* @optname: the socket option name500*501* Description:502* Check the setsockopt() call and if the user is trying to replace the IP503* options on a socket and a NetLabel is in place for the socket deny the504* access; otherwise allow the access. Returns zero when the access is505* allowed, -EACCES when denied, and other negative values on error.506*507*/508int selinux_netlbl_socket_setsockopt(struct socket *sock,509int level,510int optname)511{512int rc = 0;513struct sock *sk = sock->sk;514struct sk_security_struct *sksec = selinux_sock(sk);515struct netlbl_lsm_secattr secattr;516517if (selinux_netlbl_option(level, optname) &&518(sksec->nlbl_state == NLBL_LABELED ||519sksec->nlbl_state == NLBL_CONNLABELED)) {520netlbl_secattr_init(&secattr);521lock_sock(sk);522/* call the netlabel function directly as we want to see the523* on-the-wire label that is assigned via the socket's options524* and not the cached netlabel/lsm attributes */525rc = netlbl_sock_getattr(sk, &secattr);526release_sock(sk);527if (rc == 0)528rc = -EACCES;529else if (rc == -ENOMSG)530rc = 0;531netlbl_secattr_destroy(&secattr);532}533534return rc;535}536537/**538* selinux_netlbl_socket_connect_helper - Help label a client-side socket on539* connect540* @sk: the socket to label541* @addr: the destination address542*543* Description:544* Attempt to label a connected socket with NetLabel using the given address.545* Returns zero values on success, negative values on failure.546*547*/548static int selinux_netlbl_socket_connect_helper(struct sock *sk,549struct sockaddr *addr)550{551int rc;552struct sk_security_struct *sksec = selinux_sock(sk);553struct netlbl_lsm_secattr *secattr;554555/* connected sockets are allowed to disconnect when the address family556* is set to AF_UNSPEC, if that is what is happening we want to reset557* the socket */558if (addr->sa_family == AF_UNSPEC) {559netlbl_sock_delattr(sk);560sksec->nlbl_state = NLBL_REQSKB;561rc = 0;562return rc;563}564secattr = selinux_netlbl_sock_genattr(sk);565if (IS_ERR(secattr))566return PTR_ERR(secattr);567568rc = netlbl_conn_setattr(sk, addr, secattr);569if (rc == 0)570sksec->nlbl_state = NLBL_CONNLABELED;571572return rc;573}574575/**576* selinux_netlbl_socket_connect_locked - Label a client-side socket on577* connect578* @sk: the socket to label579* @addr: the destination address580*581* Description:582* Attempt to label a connected socket that already has the socket locked583* with NetLabel using the given address.584* Returns zero values on success, negative values on failure.585*586*/587int selinux_netlbl_socket_connect_locked(struct sock *sk,588struct sockaddr *addr)589{590struct sk_security_struct *sksec = selinux_sock(sk);591592if (sksec->nlbl_state != NLBL_REQSKB &&593sksec->nlbl_state != NLBL_CONNLABELED)594return 0;595596return selinux_netlbl_socket_connect_helper(sk, addr);597}598599/**600* selinux_netlbl_socket_connect - Label a client-side socket on connect601* @sk: the socket to label602* @addr: the destination address603*604* Description:605* Attempt to label a connected socket with NetLabel using the given address.606* Returns zero values on success, negative values on failure.607*608*/609int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)610{611int rc;612613lock_sock(sk);614rc = selinux_netlbl_socket_connect_locked(sk, addr);615release_sock(sk);616617return rc;618}619620621