/*1* SELinux NetLabel Support2*3* This file provides the necessary glue to tie NetLabel into the SELinux4* subsystem.5*6* Author: Paul Moore <[email protected]>7*8*/910/*11* (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 200812*13* This program is free software; you can redistribute it and/or modify14* it under the terms of the GNU General Public License as published by15* the Free Software Foundation; either version 2 of the License, or16* (at your option) any later version.17*18* This program is distributed in the hope that it will be useful,19* but WITHOUT ANY WARRANTY; without even the implied warranty of20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See21* the GNU General Public License for more details.22*23* You should have received a copy of the GNU General Public License24* along with this program; if not, write to the Free Software25* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA26*27*/2829#include <linux/spinlock.h>30#include <linux/rcupdate.h>31#include <linux/gfp.h>32#include <linux/ip.h>33#include <linux/ipv6.h>34#include <net/sock.h>35#include <net/netlabel.h>36#include <net/ip.h>37#include <net/ipv6.h>3839#include "objsec.h"40#include "security.h"41#include "netlabel.h"4243/**44* selinux_netlbl_sidlookup_cached - Cache a SID lookup45* @skb: the packet46* @secattr: the NetLabel security attributes47* @sid: the SID48*49* Description:50* Query the SELinux security server to lookup the correct SID for the given51* security attributes. If the query is successful, cache the result to speed52* up future lookups. Returns zero on success, negative values on failure.53*54*/55static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,56struct netlbl_lsm_secattr *secattr,57u32 *sid)58{59int rc;6061rc = security_netlbl_secattr_to_sid(secattr, sid);62if (rc == 0 &&63(secattr->flags & NETLBL_SECATTR_CACHEABLE) &&64(secattr->flags & NETLBL_SECATTR_CACHE))65netlbl_cache_add(skb, secattr);6667return rc;68}6970/**71* selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr72* @sk: the socket73*74* Description:75* Generate the NetLabel security attributes for a socket, making full use of76* the socket's attribute cache. Returns a pointer to the security attributes77* on success, NULL on failure.78*79*/80static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)81{82int rc;83struct sk_security_struct *sksec = sk->sk_security;84struct netlbl_lsm_secattr *secattr;8586if (sksec->nlbl_secattr != NULL)87return sksec->nlbl_secattr;8889secattr = netlbl_secattr_alloc(GFP_ATOMIC);90if (secattr == NULL)91return NULL;92rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);93if (rc != 0) {94netlbl_secattr_free(secattr);95return NULL;96}97sksec->nlbl_secattr = secattr;9899return secattr;100}101102/**103* selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache104*105* Description:106* Invalidate the NetLabel security attribute mapping cache.107*108*/109void selinux_netlbl_cache_invalidate(void)110{111netlbl_cache_invalidate();112}113114/**115* selinux_netlbl_err - Handle a NetLabel packet error116* @skb: the packet117* @error: the error code118* @gateway: true if host is acting as a gateway, false otherwise119*120* Description:121* When a packet is dropped due to a call to avc_has_perm() pass the error122* code to the NetLabel subsystem so any protocol specific processing can be123* done. This is safe to call even if you are unsure if NetLabel labeling is124* present on the packet, NetLabel is smart enough to only act when it should.125*126*/127void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)128{129netlbl_skbuff_err(skb, error, gateway);130}131132/**133* selinux_netlbl_sk_security_free - Free the NetLabel fields134* @sksec: the sk_security_struct135*136* Description:137* Free all of the memory in the NetLabel fields of a sk_security_struct.138*139*/140void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)141{142if (sksec->nlbl_secattr != NULL)143netlbl_secattr_free(sksec->nlbl_secattr);144}145146/**147* selinux_netlbl_sk_security_reset - Reset the NetLabel fields148* @sksec: the sk_security_struct149* @family: the socket family150*151* Description:152* Called when the NetLabel state of a sk_security_struct needs to be reset.153* The caller is responsible for all the NetLabel sk_security_struct locking.154*155*/156void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec)157{158sksec->nlbl_state = NLBL_UNSET;159}160161/**162* selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel163* @skb: the packet164* @family: protocol family165* @type: NetLabel labeling protocol type166* @sid: the SID167*168* Description:169* Call the NetLabel mechanism to get the security attributes of the given170* packet and use those attributes to determine the correct context/SID to171* assign to the packet. Returns zero on success, negative values on failure.172*173*/174int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,175u16 family,176u32 *type,177u32 *sid)178{179int rc;180struct netlbl_lsm_secattr secattr;181182if (!netlbl_enabled()) {183*sid = SECSID_NULL;184return 0;185}186187netlbl_secattr_init(&secattr);188rc = netlbl_skbuff_getattr(skb, family, &secattr);189if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)190rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);191else192*sid = SECSID_NULL;193*type = secattr.type;194netlbl_secattr_destroy(&secattr);195196return rc;197}198199/**200* selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid201* @skb: the packet202* @family: protocol family203* @sid: the SID204*205* Description206* Call the NetLabel mechanism to set the label of a packet using @sid.207* Returns zero on success, negative values on failure.208*209*/210int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,211u16 family,212u32 sid)213{214int rc;215struct netlbl_lsm_secattr secattr_storage;216struct netlbl_lsm_secattr *secattr = NULL;217struct sock *sk;218219/* if this is a locally generated packet check to see if it is already220* being labeled by it's parent socket, if it is just exit */221sk = skb->sk;222if (sk != NULL) {223struct sk_security_struct *sksec = sk->sk_security;224if (sksec->nlbl_state != NLBL_REQSKB)225return 0;226secattr = sksec->nlbl_secattr;227}228if (secattr == NULL) {229secattr = &secattr_storage;230netlbl_secattr_init(secattr);231rc = security_netlbl_sid_to_secattr(sid, secattr);232if (rc != 0)233goto skbuff_setsid_return;234}235236rc = netlbl_skbuff_setattr(skb, family, secattr);237238skbuff_setsid_return:239if (secattr == &secattr_storage)240netlbl_secattr_destroy(secattr);241return rc;242}243244/**245* selinux_netlbl_inet_conn_request - Label an incoming stream connection246* @req: incoming connection request socket247*248* Description:249* A new incoming connection request is represented by @req, we need to label250* the new request_sock here and the stack will ensure the on-the-wire label251* will get preserved when a full sock is created once the connection handshake252* is complete. Returns zero on success, negative values on failure.253*254*/255int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)256{257int rc;258struct netlbl_lsm_secattr secattr;259260if (family != PF_INET)261return 0;262263netlbl_secattr_init(&secattr);264rc = security_netlbl_sid_to_secattr(req->secid, &secattr);265if (rc != 0)266goto inet_conn_request_return;267rc = netlbl_req_setattr(req, &secattr);268inet_conn_request_return:269netlbl_secattr_destroy(&secattr);270return rc;271}272273/**274* selinux_netlbl_inet_csk_clone - Initialize the newly created sock275* @sk: the new sock276*277* Description:278* A new connection has been established using @sk, we've already labeled the279* socket via the request_sock struct in selinux_netlbl_inet_conn_request() but280* we need to set the NetLabel state here since we now have a sock structure.281*282*/283void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)284{285struct sk_security_struct *sksec = sk->sk_security;286287if (family == PF_INET)288sksec->nlbl_state = NLBL_LABELED;289else290sksec->nlbl_state = NLBL_UNSET;291}292293/**294* selinux_netlbl_socket_post_create - Label a socket using NetLabel295* @sock: the socket to label296* @family: protocol family297*298* Description:299* Attempt to label a socket using the NetLabel mechanism using the given300* SID. Returns zero values on success, negative values on failure.301*302*/303int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)304{305int rc;306struct sk_security_struct *sksec = sk->sk_security;307struct netlbl_lsm_secattr *secattr;308309if (family != PF_INET)310return 0;311312secattr = selinux_netlbl_sock_genattr(sk);313if (secattr == NULL)314return -ENOMEM;315rc = netlbl_sock_setattr(sk, family, secattr);316switch (rc) {317case 0:318sksec->nlbl_state = NLBL_LABELED;319break;320case -EDESTADDRREQ:321sksec->nlbl_state = NLBL_REQSKB;322rc = 0;323break;324}325326return rc;327}328329/**330* selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel331* @sksec: the sock's sk_security_struct332* @skb: the packet333* @family: protocol family334* @ad: the audit data335*336* Description:337* Fetch the NetLabel security attributes from @skb and perform an access check338* against the receiving socket. Returns zero on success, negative values on339* error.340*341*/342int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,343struct sk_buff *skb,344u16 family,345struct common_audit_data *ad)346{347int rc;348u32 nlbl_sid;349u32 perm;350struct netlbl_lsm_secattr secattr;351352if (!netlbl_enabled())353return 0;354355netlbl_secattr_init(&secattr);356rc = netlbl_skbuff_getattr(skb, family, &secattr);357if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)358rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);359else360nlbl_sid = SECINITSID_UNLABELED;361netlbl_secattr_destroy(&secattr);362if (rc != 0)363return rc;364365switch (sksec->sclass) {366case SECCLASS_UDP_SOCKET:367perm = UDP_SOCKET__RECVFROM;368break;369case SECCLASS_TCP_SOCKET:370perm = TCP_SOCKET__RECVFROM;371break;372default:373perm = RAWIP_SOCKET__RECVFROM;374}375376rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);377if (rc == 0)378return 0;379380if (nlbl_sid != SECINITSID_UNLABELED)381netlbl_skbuff_err(skb, rc, 0);382return rc;383}384385/**386* selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel387* @sock: the socket388* @level: the socket level or protocol389* @optname: the socket option name390*391* Description:392* Check the setsockopt() call and if the user is trying to replace the IP393* options on a socket and a NetLabel is in place for the socket deny the394* access; otherwise allow the access. Returns zero when the access is395* allowed, -EACCES when denied, and other negative values on error.396*397*/398int selinux_netlbl_socket_setsockopt(struct socket *sock,399int level,400int optname)401{402int rc = 0;403struct sock *sk = sock->sk;404struct sk_security_struct *sksec = sk->sk_security;405struct netlbl_lsm_secattr secattr;406407if (level == IPPROTO_IP && optname == IP_OPTIONS &&408(sksec->nlbl_state == NLBL_LABELED ||409sksec->nlbl_state == NLBL_CONNLABELED)) {410netlbl_secattr_init(&secattr);411lock_sock(sk);412rc = netlbl_sock_getattr(sk, &secattr);413release_sock(sk);414if (rc == 0)415rc = -EACCES;416else if (rc == -ENOMSG)417rc = 0;418netlbl_secattr_destroy(&secattr);419}420421return rc;422}423424/**425* selinux_netlbl_socket_connect - Label a client-side socket on connect426* @sk: the socket to label427* @addr: the destination address428*429* Description:430* Attempt to label a connected socket with NetLabel using the given address.431* Returns zero values on success, negative values on failure.432*433*/434int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)435{436int rc;437struct sk_security_struct *sksec = sk->sk_security;438struct netlbl_lsm_secattr *secattr;439440if (sksec->nlbl_state != NLBL_REQSKB &&441sksec->nlbl_state != NLBL_CONNLABELED)442return 0;443444local_bh_disable();445bh_lock_sock_nested(sk);446447/* connected sockets are allowed to disconnect when the address family448* is set to AF_UNSPEC, if that is what is happening we want to reset449* the socket */450if (addr->sa_family == AF_UNSPEC) {451netlbl_sock_delattr(sk);452sksec->nlbl_state = NLBL_REQSKB;453rc = 0;454goto socket_connect_return;455}456secattr = selinux_netlbl_sock_genattr(sk);457if (secattr == NULL) {458rc = -ENOMEM;459goto socket_connect_return;460}461rc = netlbl_conn_setattr(sk, addr, secattr);462if (rc == 0)463sksec->nlbl_state = NLBL_CONNLABELED;464465socket_connect_return:466bh_unlock_sock(sk);467local_bh_enable();468return rc;469}470471472