mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
a430d95c5e
-----BEGIN PGP SIGNATURE----- iQJIBAABCAAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmbiGGAUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXPU8BAA1+A15pmS34I9pq7c8TmRz3rNEs/a zrW1aWJ0X/+axNS7sW3Pwtt1EKuaOhskKU8gNSieRhljC8rgXIVjZzLw6Atgcr5k upulGbU9TXyVisYN+PWv9/84ito6/nYsKb7Mg3nUVsdodtIFVnsk1fxYLPHQEBig Pl3i26U3VqH93Kz0W5vs/QR2uduPB8ZyscdTgcbrY9Vv1Y7IDZ2g9QsJVKLvbQKL qcPK1JkHa+sBPJxDqS9A40zgbLbdPQgWQzsXX3dz822w1Ga7FIHSqxMBA6HwHZ+L kV4P58wVfavhwt/cQSKMWI/yiGPMMd0B6yD+m8ojOvGfOfRCWxGMmEMqHNuZ3m7k Bfll5ZgZTY8phUUhiNf3nxO3F3MM/5bHdhPOj3RReqbAbS6uWr4/fThPDYY/zIo6 NCY3HGxx3Ae64uQ01gC2p/czC50jDsMwlbXiZbrgdBhjBm/CVk5ozb80mLVcGrLB +6XMzzSbC8IaNAH2fDmUJ2ABdwyNPgsSOTGZVzIanpxu1SU2/yk3SMxkp8fv5s36 wLeODUVcLgsjVV538Mkm6PGTE4TlXaH9yi6apMyJAGp0vPYx5c3Xxk2y5A5cur5p hcrbDiX2QgeqFbwsz36incmPmbef2NU2c8feR8XLtPJuwNIeRcMSje0pnkaFlRmb TAUJ1sDQAzZ8Fy0= =HIAO -----END PGP SIGNATURE----- Merge tag 'lsm-pr-20240911' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm Pull lsm updates from Paul Moore: - Move the LSM framework to static calls This transitions the vast majority of the LSM callbacks into static calls. Those callbacks which haven't been converted were left as-is due to the general ugliness of the changes required to support the static call conversion; we can revisit those callbacks at a future date. - Add the Integrity Policy Enforcement (IPE) LSM This adds a new LSM, Integrity Policy Enforcement (IPE). There is plenty of documentation about IPE in this patches, so I'll refrain from going into too much detail here, but the basic motivation behind IPE is to provide a mechanism such that administrators can restrict execution to only those binaries which come from integrity protected storage, e.g. a dm-verity protected filesystem. You will notice that IPE requires additional LSM hooks in the initramfs, dm-verity, and fs-verity code, with the associated patches carrying ACK/review tags from the associated maintainers. We couldn't find an obvious maintainer for the initramfs code, but the IPE patchset has been widely posted over several years. Both Deven Bowers and Fan Wu have contributed to IPE's development over the past several years, with Fan Wu agreeing to serve as the IPE maintainer moving forward. Once IPE is accepted into your tree, I'll start working with Fan to ensure he has the necessary accounts, keys, etc. so that he can start submitting IPE pull requests to you directly during the next merge window. - Move the lifecycle management of the LSM blobs to the LSM framework Management of the LSM blobs (the LSM state buffers attached to various kernel structs, typically via a void pointer named "security" or similar) has been mixed, some blobs were allocated/managed by individual LSMs, others were managed by the LSM framework itself. Starting with this pull we move management of all the LSM blobs, minus the XFRM blob, into the framework itself, improving consistency across LSMs, and reducing the amount of duplicated code across LSMs. Due to some additional work required to migrate the XFRM blob, it has been left as a todo item for a later date; from a practical standpoint this omission should have little impact as only SELinux provides a XFRM LSM implementation. - Fix problems with the LSM's handling of F_SETOWN The LSM hook for the fcntl(F_SETOWN) operation had a couple of problems: it was racy with itself, and it was disconnected from the associated DAC related logic in such a way that the LSM state could be updated in cases where the DAC state would not. We fix both of these problems by moving the security_file_set_fowner() hook into the same section of code where the DAC attributes are updated. Not only does this resolve the DAC/LSM synchronization issue, but as that code block is protected by a lock, it also resolve the race condition. - Fix potential problems with the security_inode_free() LSM hook Due to use of RCU to protect inodes and the placement of the LSM hook associated with freeing the inode, there is a bit of a challenge when it comes to managing any LSM state associated with an inode. The VFS folks are not open to relocating the LSM hook so we have to get creative when it comes to releasing an inode's LSM state. Traditionally we have used a single LSM callback within the hook that is triggered when the inode is "marked for death", but not actually released due to RCU. Unfortunately, this causes problems for LSMs which want to take an action when the inode's associated LSM state is actually released; so we add an additional LSM callback, inode_free_security_rcu(), that is called when the inode's LSM state is released in the RCU free callback. - Refactor two LSM hooks to better fit the LSM return value patterns The vast majority of the LSM hooks follow the "return 0 on success, negative values on failure" pattern, however, there are a small handful that have unique return value behaviors which has caused confusion in the past and makes it difficult for the BPF verifier to properly vet BPF LSM programs. This includes patches to convert two of these"special" LSM hooks to the common 0/-ERRNO pattern. - Various cleanups and improvements A handful of patches to remove redundant code, better leverage the IS_ERR_OR_NULL() helper, add missing "static" markings, and do some minor style fixups. * tag 'lsm-pr-20240911' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm: (40 commits) security: Update file_set_fowner documentation fs: Fix file_set_fowner LSM hook inconsistencies lsm: Use IS_ERR_OR_NULL() helper function lsm: remove LSM_COUNT and LSM_CONFIG_COUNT ipe: Remove duplicated include in ipe.c lsm: replace indirect LSM hook calls with static calls lsm: count the LSMs enabled at compile time kernel: Add helper macros for loop unrolling init/main.c: Initialize early LSMs after arch code, static keys and calls. MAINTAINERS: add IPE entry with Fan Wu as maintainer documentation: add IPE documentation ipe: kunit test for parser scripts: add boot policy generation program ipe: enable support for fs-verity as a trust provider fsverity: expose verified fsverity built-in signatures to LSMs lsm: add security_inode_setintegrity() hook ipe: add support for dm-verity as a trust provider dm-verity: expose root hash digest and signature data to LSMs block,lsm: add LSM blob and new LSM hooks for block devices ipe: add permissive toggle ...
620 lines
16 KiB
C
620 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* SELinux NetLabel Support
|
|
*
|
|
* This file provides the necessary glue to tie NetLabel into the SELinux
|
|
* subsystem.
|
|
*
|
|
* Author: Paul Moore <paul@paul-moore.com>
|
|
*/
|
|
|
|
/*
|
|
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
|
|
*/
|
|
|
|
#include <linux/spinlock.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/ipv6.h>
|
|
#include <linux/lsm_hooks.h>
|
|
#include <net/sock.h>
|
|
#include <net/netlabel.h>
|
|
#include <net/ip.h>
|
|
#include <net/ipv6.h>
|
|
|
|
#include "objsec.h"
|
|
#include "security.h"
|
|
#include "netlabel.h"
|
|
|
|
/**
|
|
* selinux_netlbl_sidlookup_cached - Cache a SID lookup
|
|
* @skb: the packet
|
|
* @family: the packet's address family
|
|
* @secattr: the NetLabel security attributes
|
|
* @sid: the SID
|
|
*
|
|
* Description:
|
|
* Query the SELinux security server to lookup the correct SID for the given
|
|
* security attributes. If the query is successful, cache the result to speed
|
|
* up future lookups. Returns zero on success, negative values on failure.
|
|
*
|
|
*/
|
|
static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
|
|
u16 family,
|
|
struct netlbl_lsm_secattr *secattr,
|
|
u32 *sid)
|
|
{
|
|
int rc;
|
|
|
|
rc = security_netlbl_secattr_to_sid(secattr, sid);
|
|
if (rc == 0 &&
|
|
(secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
|
|
(secattr->flags & NETLBL_SECATTR_CACHE))
|
|
netlbl_cache_add(skb, family, secattr);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
|
|
* @sk: the socket
|
|
*
|
|
* Description:
|
|
* Generate the NetLabel security attributes for a socket, making full use of
|
|
* the socket's attribute cache. Returns a pointer to the security attributes
|
|
* on success, or an ERR_PTR on failure.
|
|
*
|
|
*/
|
|
static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
|
|
{
|
|
int rc;
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
struct netlbl_lsm_secattr *secattr;
|
|
|
|
if (sksec->nlbl_secattr != NULL)
|
|
return sksec->nlbl_secattr;
|
|
|
|
secattr = netlbl_secattr_alloc(GFP_ATOMIC);
|
|
if (secattr == NULL)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
|
|
if (rc != 0) {
|
|
netlbl_secattr_free(secattr);
|
|
return ERR_PTR(rc);
|
|
}
|
|
sksec->nlbl_secattr = secattr;
|
|
|
|
return secattr;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_sock_getattr - Get the cached NetLabel secattr
|
|
* @sk: the socket
|
|
* @sid: the SID
|
|
*
|
|
* Query the socket's cached secattr and if the SID matches the cached value
|
|
* return the cache, otherwise return NULL.
|
|
*
|
|
*/
|
|
static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr(
|
|
const struct sock *sk,
|
|
u32 sid)
|
|
{
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;
|
|
|
|
if (secattr == NULL)
|
|
return NULL;
|
|
|
|
if ((secattr->flags & NETLBL_SECATTR_SECID) &&
|
|
(secattr->attr.secid == sid))
|
|
return secattr;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
|
|
*
|
|
* Description:
|
|
* Invalidate the NetLabel security attribute mapping cache.
|
|
*
|
|
*/
|
|
void selinux_netlbl_cache_invalidate(void)
|
|
{
|
|
netlbl_cache_invalidate();
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_err - Handle a NetLabel packet error
|
|
* @skb: the packet
|
|
* @family: the packet's address family
|
|
* @error: the error code
|
|
* @gateway: true if host is acting as a gateway, false otherwise
|
|
*
|
|
* Description:
|
|
* When a packet is dropped due to a call to avc_has_perm() pass the error
|
|
* code to the NetLabel subsystem so any protocol specific processing can be
|
|
* done. This is safe to call even if you are unsure if NetLabel labeling is
|
|
* present on the packet, NetLabel is smart enough to only act when it should.
|
|
*
|
|
*/
|
|
void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
|
|
{
|
|
netlbl_skbuff_err(skb, family, error, gateway);
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_sk_security_free - Free the NetLabel fields
|
|
* @sksec: the sk_security_struct
|
|
*
|
|
* Description:
|
|
* Free all of the memory in the NetLabel fields of a sk_security_struct.
|
|
*
|
|
*/
|
|
void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
|
|
{
|
|
if (!sksec->nlbl_secattr)
|
|
return;
|
|
|
|
netlbl_secattr_free(sksec->nlbl_secattr);
|
|
sksec->nlbl_secattr = NULL;
|
|
sksec->nlbl_state = NLBL_UNSET;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
|
|
* @sksec: the sk_security_struct
|
|
*
|
|
* Description:
|
|
* Called when the NetLabel state of a sk_security_struct needs to be reset.
|
|
* The caller is responsible for all the NetLabel sk_security_struct locking.
|
|
*
|
|
*/
|
|
void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec)
|
|
{
|
|
sksec->nlbl_state = NLBL_UNSET;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
|
|
* @skb: the packet
|
|
* @family: protocol family
|
|
* @type: NetLabel labeling protocol type
|
|
* @sid: the SID
|
|
*
|
|
* Description:
|
|
* Call the NetLabel mechanism to get the security attributes of the given
|
|
* packet and use those attributes to determine the correct context/SID to
|
|
* assign to the packet. Returns zero on success, negative values on failure.
|
|
*
|
|
*/
|
|
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
|
|
u16 family,
|
|
u32 *type,
|
|
u32 *sid)
|
|
{
|
|
int rc;
|
|
struct netlbl_lsm_secattr secattr;
|
|
|
|
if (!netlbl_enabled()) {
|
|
*type = NETLBL_NLTYPE_NONE;
|
|
*sid = SECSID_NULL;
|
|
return 0;
|
|
}
|
|
|
|
netlbl_secattr_init(&secattr);
|
|
rc = netlbl_skbuff_getattr(skb, family, &secattr);
|
|
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
|
|
rc = selinux_netlbl_sidlookup_cached(skb, family,
|
|
&secattr, sid);
|
|
else
|
|
*sid = SECSID_NULL;
|
|
*type = secattr.type;
|
|
netlbl_secattr_destroy(&secattr);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
|
|
* @skb: the packet
|
|
* @family: protocol family
|
|
* @sid: the SID
|
|
*
|
|
* Description
|
|
* Call the NetLabel mechanism to set the label of a packet using @sid.
|
|
* Returns zero on success, negative values on failure.
|
|
*
|
|
*/
|
|
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
|
|
u16 family,
|
|
u32 sid)
|
|
{
|
|
int rc;
|
|
struct netlbl_lsm_secattr secattr_storage;
|
|
struct netlbl_lsm_secattr *secattr = NULL;
|
|
struct sock *sk;
|
|
|
|
/* if this is a locally generated packet check to see if it is already
|
|
* being labeled by it's parent socket, if it is just exit */
|
|
sk = skb_to_full_sk(skb);
|
|
if (sk != NULL) {
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
|
|
if (sksec->nlbl_state != NLBL_REQSKB)
|
|
return 0;
|
|
secattr = selinux_netlbl_sock_getattr(sk, sid);
|
|
}
|
|
if (secattr == NULL) {
|
|
secattr = &secattr_storage;
|
|
netlbl_secattr_init(secattr);
|
|
rc = security_netlbl_sid_to_secattr(sid, secattr);
|
|
if (rc != 0)
|
|
goto skbuff_setsid_return;
|
|
}
|
|
|
|
rc = netlbl_skbuff_setattr(skb, family, secattr);
|
|
|
|
skbuff_setsid_return:
|
|
if (secattr == &secattr_storage)
|
|
netlbl_secattr_destroy(secattr);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_sctp_assoc_request - Label an incoming sctp association.
|
|
* @asoc: incoming association.
|
|
* @skb: the packet.
|
|
*
|
|
* Description:
|
|
* A new incoming connection is represented by @asoc, ......
|
|
* Returns zero on success, negative values on failure.
|
|
*
|
|
*/
|
|
int selinux_netlbl_sctp_assoc_request(struct sctp_association *asoc,
|
|
struct sk_buff *skb)
|
|
{
|
|
int rc;
|
|
struct netlbl_lsm_secattr secattr;
|
|
struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
|
|
struct sockaddr_in addr4;
|
|
struct sockaddr_in6 addr6;
|
|
|
|
if (asoc->base.sk->sk_family != PF_INET &&
|
|
asoc->base.sk->sk_family != PF_INET6)
|
|
return 0;
|
|
|
|
netlbl_secattr_init(&secattr);
|
|
rc = security_netlbl_sid_to_secattr(asoc->secid, &secattr);
|
|
if (rc != 0)
|
|
goto assoc_request_return;
|
|
|
|
/* Move skb hdr address info to a struct sockaddr and then call
|
|
* netlbl_conn_setattr().
|
|
*/
|
|
if (ip_hdr(skb)->version == 4) {
|
|
addr4.sin_family = AF_INET;
|
|
addr4.sin_addr.s_addr = ip_hdr(skb)->saddr;
|
|
rc = netlbl_conn_setattr(asoc->base.sk, (void *)&addr4, &secattr);
|
|
} else if (IS_ENABLED(CONFIG_IPV6) && ip_hdr(skb)->version == 6) {
|
|
addr6.sin6_family = AF_INET6;
|
|
addr6.sin6_addr = ipv6_hdr(skb)->saddr;
|
|
rc = netlbl_conn_setattr(asoc->base.sk, (void *)&addr6, &secattr);
|
|
} else {
|
|
rc = -EAFNOSUPPORT;
|
|
}
|
|
|
|
if (rc == 0)
|
|
sksec->nlbl_state = NLBL_LABELED;
|
|
|
|
assoc_request_return:
|
|
netlbl_secattr_destroy(&secattr);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_inet_conn_request - Label an incoming stream connection
|
|
* @req: incoming connection request socket
|
|
* @family: the request socket's address family
|
|
*
|
|
* Description:
|
|
* A new incoming connection request is represented by @req, we need to label
|
|
* the new request_sock here and the stack will ensure the on-the-wire label
|
|
* will get preserved when a full sock is created once the connection handshake
|
|
* is complete. Returns zero on success, negative values on failure.
|
|
*
|
|
*/
|
|
int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
|
|
{
|
|
int rc;
|
|
struct netlbl_lsm_secattr secattr;
|
|
|
|
if (family != PF_INET && family != PF_INET6)
|
|
return 0;
|
|
|
|
netlbl_secattr_init(&secattr);
|
|
rc = security_netlbl_sid_to_secattr(req->secid, &secattr);
|
|
if (rc != 0)
|
|
goto inet_conn_request_return;
|
|
rc = netlbl_req_setattr(req, &secattr);
|
|
inet_conn_request_return:
|
|
netlbl_secattr_destroy(&secattr);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_inet_csk_clone - Initialize the newly created sock
|
|
* @sk: the new sock
|
|
* @family: the sock's address family
|
|
*
|
|
* Description:
|
|
* A new connection has been established using @sk, we've already labeled the
|
|
* socket via the request_sock struct in selinux_netlbl_inet_conn_request() but
|
|
* we need to set the NetLabel state here since we now have a sock structure.
|
|
*
|
|
*/
|
|
void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
|
|
{
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
|
|
if (family == PF_INET || family == PF_INET6)
|
|
sksec->nlbl_state = NLBL_LABELED;
|
|
else
|
|
sksec->nlbl_state = NLBL_UNSET;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_sctp_sk_clone - Copy state to the newly created sock
|
|
* @sk: current sock
|
|
* @newsk: the new sock
|
|
*
|
|
* Description:
|
|
* Called whenever a new socket is created by accept(2) or sctp_peeloff(3).
|
|
*/
|
|
void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
|
|
{
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
struct sk_security_struct *newsksec = selinux_sock(newsk);
|
|
|
|
newsksec->nlbl_state = sksec->nlbl_state;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_socket_post_create - Label a socket using NetLabel
|
|
* @sk: the sock to label
|
|
* @family: protocol family
|
|
*
|
|
* Description:
|
|
* Attempt to label a socket using the NetLabel mechanism using the given
|
|
* SID. Returns zero values on success, negative values on failure.
|
|
*
|
|
*/
|
|
int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
|
|
{
|
|
int rc;
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
struct netlbl_lsm_secattr *secattr;
|
|
|
|
if (family != PF_INET && family != PF_INET6)
|
|
return 0;
|
|
|
|
secattr = selinux_netlbl_sock_genattr(sk);
|
|
if (IS_ERR(secattr))
|
|
return PTR_ERR(secattr);
|
|
/* On socket creation, replacement of IP options is safe even if
|
|
* the caller does not hold the socket lock.
|
|
*/
|
|
rc = netlbl_sock_setattr(sk, family, secattr, true);
|
|
switch (rc) {
|
|
case 0:
|
|
sksec->nlbl_state = NLBL_LABELED;
|
|
break;
|
|
case -EDESTADDRREQ:
|
|
sksec->nlbl_state = NLBL_REQSKB;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
|
|
* @sksec: the sock's sk_security_struct
|
|
* @skb: the packet
|
|
* @family: protocol family
|
|
* @ad: the audit data
|
|
*
|
|
* Description:
|
|
* Fetch the NetLabel security attributes from @skb and perform an access check
|
|
* against the receiving socket. Returns zero on success, negative values on
|
|
* error.
|
|
*
|
|
*/
|
|
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
|
|
struct sk_buff *skb,
|
|
u16 family,
|
|
struct common_audit_data *ad)
|
|
{
|
|
int rc;
|
|
u32 nlbl_sid;
|
|
u32 perm;
|
|
struct netlbl_lsm_secattr secattr;
|
|
|
|
if (!netlbl_enabled())
|
|
return 0;
|
|
|
|
netlbl_secattr_init(&secattr);
|
|
rc = netlbl_skbuff_getattr(skb, family, &secattr);
|
|
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
|
|
rc = selinux_netlbl_sidlookup_cached(skb, family,
|
|
&secattr, &nlbl_sid);
|
|
else
|
|
nlbl_sid = SECINITSID_UNLABELED;
|
|
netlbl_secattr_destroy(&secattr);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
switch (sksec->sclass) {
|
|
case SECCLASS_UDP_SOCKET:
|
|
perm = UDP_SOCKET__RECVFROM;
|
|
break;
|
|
case SECCLASS_TCP_SOCKET:
|
|
perm = TCP_SOCKET__RECVFROM;
|
|
break;
|
|
default:
|
|
perm = RAWIP_SOCKET__RECVFROM;
|
|
}
|
|
|
|
rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
|
|
if (rc == 0)
|
|
return 0;
|
|
|
|
if (nlbl_sid != SECINITSID_UNLABELED)
|
|
netlbl_skbuff_err(skb, family, rc, 0);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_option - Is this a NetLabel option
|
|
* @level: the socket level or protocol
|
|
* @optname: the socket option name
|
|
*
|
|
* Description:
|
|
* Returns true if @level and @optname refer to a NetLabel option.
|
|
* Helper for selinux_netlbl_socket_setsockopt().
|
|
*/
|
|
static inline int selinux_netlbl_option(int level, int optname)
|
|
{
|
|
return (level == IPPROTO_IP && optname == IP_OPTIONS) ||
|
|
(level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS);
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
|
|
* @sock: the socket
|
|
* @level: the socket level or protocol
|
|
* @optname: the socket option name
|
|
*
|
|
* Description:
|
|
* Check the setsockopt() call and if the user is trying to replace the IP
|
|
* options on a socket and a NetLabel is in place for the socket deny the
|
|
* access; otherwise allow the access. Returns zero when the access is
|
|
* allowed, -EACCES when denied, and other negative values on error.
|
|
*
|
|
*/
|
|
int selinux_netlbl_socket_setsockopt(struct socket *sock,
|
|
int level,
|
|
int optname)
|
|
{
|
|
int rc = 0;
|
|
struct sock *sk = sock->sk;
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
struct netlbl_lsm_secattr secattr;
|
|
|
|
if (selinux_netlbl_option(level, optname) &&
|
|
(sksec->nlbl_state == NLBL_LABELED ||
|
|
sksec->nlbl_state == NLBL_CONNLABELED)) {
|
|
netlbl_secattr_init(&secattr);
|
|
lock_sock(sk);
|
|
/* call the netlabel function directly as we want to see the
|
|
* on-the-wire label that is assigned via the socket's options
|
|
* and not the cached netlabel/lsm attributes */
|
|
rc = netlbl_sock_getattr(sk, &secattr);
|
|
release_sock(sk);
|
|
if (rc == 0)
|
|
rc = -EACCES;
|
|
else if (rc == -ENOMSG)
|
|
rc = 0;
|
|
netlbl_secattr_destroy(&secattr);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_socket_connect_helper - Help label a client-side socket on
|
|
* connect
|
|
* @sk: the socket to label
|
|
* @addr: the destination address
|
|
*
|
|
* Description:
|
|
* Attempt to label a connected socket with NetLabel using the given address.
|
|
* Returns zero values on success, negative values on failure.
|
|
*
|
|
*/
|
|
static int selinux_netlbl_socket_connect_helper(struct sock *sk,
|
|
struct sockaddr *addr)
|
|
{
|
|
int rc;
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
struct netlbl_lsm_secattr *secattr;
|
|
|
|
/* connected sockets are allowed to disconnect when the address family
|
|
* is set to AF_UNSPEC, if that is what is happening we want to reset
|
|
* the socket */
|
|
if (addr->sa_family == AF_UNSPEC) {
|
|
netlbl_sock_delattr(sk);
|
|
sksec->nlbl_state = NLBL_REQSKB;
|
|
rc = 0;
|
|
return rc;
|
|
}
|
|
secattr = selinux_netlbl_sock_genattr(sk);
|
|
if (IS_ERR(secattr))
|
|
return PTR_ERR(secattr);
|
|
|
|
rc = netlbl_conn_setattr(sk, addr, secattr);
|
|
if (rc == 0)
|
|
sksec->nlbl_state = NLBL_CONNLABELED;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_socket_connect_locked - Label a client-side socket on
|
|
* connect
|
|
* @sk: the socket to label
|
|
* @addr: the destination address
|
|
*
|
|
* Description:
|
|
* Attempt to label a connected socket that already has the socket locked
|
|
* with NetLabel using the given address.
|
|
* Returns zero values on success, negative values on failure.
|
|
*
|
|
*/
|
|
int selinux_netlbl_socket_connect_locked(struct sock *sk,
|
|
struct sockaddr *addr)
|
|
{
|
|
struct sk_security_struct *sksec = selinux_sock(sk);
|
|
|
|
if (sksec->nlbl_state != NLBL_REQSKB &&
|
|
sksec->nlbl_state != NLBL_CONNLABELED)
|
|
return 0;
|
|
|
|
return selinux_netlbl_socket_connect_helper(sk, addr);
|
|
}
|
|
|
|
/**
|
|
* selinux_netlbl_socket_connect - Label a client-side socket on connect
|
|
* @sk: the socket to label
|
|
* @addr: the destination address
|
|
*
|
|
* Description:
|
|
* Attempt to label a connected socket with NetLabel using the given address.
|
|
* Returns zero values on success, negative values on failure.
|
|
*
|
|
*/
|
|
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
|
|
{
|
|
int rc;
|
|
|
|
lock_sock(sk);
|
|
rc = selinux_netlbl_socket_connect_locked(sk, addr);
|
|
release_sock(sk);
|
|
|
|
return rc;
|
|
}
|