selinux/stable-4.17 PR 20180403

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCAAyFiEEcQCq365ubpQNLgrWVeRaWujKfIoFAlrD6XoUHHBhdWxAcGF1
 bC1tb29yZS5jb20ACgkQVeRaWujKfIpy9RAAjwhkNBNJhw1UlGggVvst8lzJBdMp
 XxL7cg+1TcZkB12yrghILg+gY4j5PzY4GJo1gvllWIHsT8Ud6cQTI/AzeYR2OfZ3
 mHv3gtyzmHsPGBdqhmgC7R10tpyXFXwDc3VLMtuuDiUl/seFEaJWOMYP7zj+tRil
 XoOCyoV9bb1wb7vNAzQikK8yhz3fu72Y5QOODLfaYeYojMKs8Q8pMZgi68oVQUXk
 SmS2mj0k2P3UqeOSk+8phJQhilm32m0tE0YnLvzAhblJLqeS2DUNnWORP1j4oQ/Q
 aOOu4ZQ9PA1N7VAIGceuf2HZHhnrFzWdvggp2bxegcRSIfUZ84FuZbrj60RUz2ja
 V6GmKYACnyd28TAWdnzjKEd4dc36LSPxnaj8hcrvyO2V34ozVEsvIEIJREoXRUJS
 heJ9HT+VIvmguzRCIPPeC1ZYopIt8M1kTRrszigU80TuZjIP0VJHLGQn/rgRQzuO
 cV5gmJ6TSGn1l54H13koBzgUCo0cAub8Nl+288qek+jLWoHnKwzLB+1HCWuyeCHt
 2q6wdFfenYH0lXdIzCeC7NNHRKCrPNwkZ/32d4ZQf4cu5tAn8bOk8dSHchoAfZG8
 p7N6jPPoxmi2F/GRKrTiUNZvQpyvgX3hjtJS6ljOTSYgRhjeNYeCP8U+BlOpLVQy
 U4KzB9wOAngTEpo=
 =p2Sh
 -----END PGP SIGNATURE-----

Merge tag 'selinux-pr-20180403' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull SELinux updates from Paul Moore:
 "A bigger than usual pull request for SELinux, 13 patches (lucky!)
  along with a scary looking diffstat.

  Although if you look a bit closer, excluding the usual minor
  tweaks/fixes, there are really only two significant changes in this
  pull request: the addition of proper SELinux access controls for SCTP
  and the encapsulation of a lot of internal SELinux state.

  The SCTP changes are the result of a multi-month effort (maybe even a
  year or longer?) between the SELinux folks and the SCTP folks to add
  proper SELinux controls. A special thanks go to Richard for seeing
  this through and keeping the effort moving forward.

  The state encapsulation work is a bit of janitorial work that came out
  of some early work on SELinux namespacing. The question of namespacing
  is still an open one, but I believe there is some real value in the
  encapsulation work so we've split that out and are now sending that up
  to you"

* tag 'selinux-pr-20180403' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: wrap AVC state
  selinux: wrap selinuxfs state
  selinux: fix handling of uninitialized selinux state in get_bools/classes
  selinux: Update SELinux SCTP documentation
  selinux: Fix ltp test connect-syscall failure
  selinux: rename the {is,set}_enforcing() functions
  selinux: wrap global selinux state
  selinux: fix typo in selinux_netlbl_sctp_sk_clone declaration
  selinux: Add SCTP support
  sctp: Add LSM hooks
  sctp: Add ip option support
  security: Add support for SCTP security hooks
  netlabel: If PF_INET6, check sk_buff ip header version
This commit is contained in:
Linus Torvalds 2018-04-06 15:39:26 -07:00
commit 9eda2d2dca
43 changed files with 2953 additions and 1269 deletions

View file

@ -0,0 +1,175 @@
SCTP LSM Support
================
For security module support, three SCTP specific hooks have been implemented::
security_sctp_assoc_request()
security_sctp_bind_connect()
security_sctp_sk_clone()
Also the following security hook has been utilised::
security_inet_conn_established()
The usage of these hooks are described below with the SELinux implementation
described in ``Documentation/security/SELinux-sctp.rst``
security_sctp_assoc_request()
-----------------------------
Passes the ``@ep`` and ``@chunk->skb`` of the association INIT packet to the
security module. Returns 0 on success, error on failure.
::
@ep - pointer to sctp endpoint structure.
@skb - pointer to skbuff of association packet.
security_sctp_bind_connect()
-----------------------------
Passes one or more ipv4/ipv6 addresses to the security module for validation
based on the ``@optname`` that will result in either a bind or connect
service as shown in the permission check tables below.
Returns 0 on success, error on failure.
::
@sk - Pointer to sock structure.
@optname - Name of the option to validate.
@address - One or more ipv4 / ipv6 addresses.
@addrlen - The total length of address(s). This is calculated on each
ipv4 or ipv6 address using sizeof(struct sockaddr_in) or
sizeof(struct sockaddr_in6).
------------------------------------------------------------------
| BIND Type Checks |
| @optname | @address contains |
|----------------------------|-----------------------------------|
| SCTP_SOCKOPT_BINDX_ADD | One or more ipv4 / ipv6 addresses |
| SCTP_PRIMARY_ADDR | Single ipv4 or ipv6 address |
| SCTP_SET_PEER_PRIMARY_ADDR | Single ipv4 or ipv6 address |
------------------------------------------------------------------
------------------------------------------------------------------
| CONNECT Type Checks |
| @optname | @address contains |
|----------------------------|-----------------------------------|
| SCTP_SOCKOPT_CONNECTX | One or more ipv4 / ipv6 addresses |
| SCTP_PARAM_ADD_IP | One or more ipv4 / ipv6 addresses |
| SCTP_SENDMSG_CONNECT | Single ipv4 or ipv6 address |
| SCTP_PARAM_SET_PRIMARY | Single ipv4 or ipv6 address |
------------------------------------------------------------------
A summary of the ``@optname`` entries is as follows::
SCTP_SOCKOPT_BINDX_ADD - Allows additional bind addresses to be
associated after (optionally) calling
bind(3).
sctp_bindx(3) adds a set of bind
addresses on a socket.
SCTP_SOCKOPT_CONNECTX - Allows the allocation of multiple
addresses for reaching a peer
(multi-homed).
sctp_connectx(3) initiates a connection
on an SCTP socket using multiple
destination addresses.
SCTP_SENDMSG_CONNECT - Initiate a connection that is generated by a
sendmsg(2) or sctp_sendmsg(3) on a new asociation.
SCTP_PRIMARY_ADDR - Set local primary address.
SCTP_SET_PEER_PRIMARY_ADDR - Request peer sets address as
association primary.
SCTP_PARAM_ADD_IP - These are used when Dynamic Address
SCTP_PARAM_SET_PRIMARY - Reconfiguration is enabled as explained below.
To support Dynamic Address Reconfiguration the following parameters must be
enabled on both endpoints (or use the appropriate **setsockopt**\(2))::
/proc/sys/net/sctp/addip_enable
/proc/sys/net/sctp/addip_noauth_enable
then the following *_PARAM_*'s are sent to the peer in an
ASCONF chunk when the corresponding ``@optname``'s are present::
@optname ASCONF Parameter
---------- ------------------
SCTP_SOCKOPT_BINDX_ADD -> SCTP_PARAM_ADD_IP
SCTP_SET_PEER_PRIMARY_ADDR -> SCTP_PARAM_SET_PRIMARY
security_sctp_sk_clone()
-------------------------
Called whenever a new socket is created by **accept**\(2)
(i.e. a TCP style socket) or when a socket is 'peeled off' e.g userspace
calls **sctp_peeloff**\(3).
::
@ep - pointer to current sctp endpoint structure.
@sk - pointer to current sock structure.
@sk - pointer to new sock structure.
security_inet_conn_established()
---------------------------------
Called when a COOKIE ACK is received::
@sk - pointer to sock structure.
@skb - pointer to skbuff of the COOKIE ACK packet.
Security Hooks used for Association Establishment
=================================================
The following diagram shows the use of ``security_sctp_bind_connect()``,
``security_sctp_assoc_request()``, ``security_inet_conn_established()`` when
establishing an association.
::
SCTP endpoint "A" SCTP endpoint "Z"
================= =================
sctp_sf_do_prm_asoc()
Association setup can be initiated
by a connect(2), sctp_connectx(3),
sendmsg(2) or sctp_sendmsg(3).
These will result in a call to
security_sctp_bind_connect() to
initiate an association to
SCTP peer endpoint "Z".
INIT --------------------------------------------->
sctp_sf_do_5_1B_init()
Respond to an INIT chunk.
SCTP peer endpoint "A" is
asking for an association. Call
security_sctp_assoc_request()
to set the peer label if first
association.
If not first association, check
whether allowed, IF so send:
<----------------------------------------------- INIT ACK
| ELSE audit event and silently
| discard the packet.
|
COOKIE ECHO ------------------------------------------>
|
|
|
<------------------------------------------- COOKIE ACK
| |
sctp_sf_do_5_1E_ca |
Call security_inet_conn_established() |
to set the peer label. |
| |
| If SCTP_SOCKET_TCP or peeled off
| socket security_sctp_sk_clone() is
| called to clone the new socket.
| |
ESTABLISHED ESTABLISHED
| |
------------------------------------------------------------------
| Association Established |
------------------------------------------------------------------

View file

@ -0,0 +1,158 @@
SCTP SELinux Support
=====================
Security Hooks
===============
``Documentation/security/LSM-sctp.rst`` describes the following SCTP security
hooks with the SELinux specifics expanded below::
security_sctp_assoc_request()
security_sctp_bind_connect()
security_sctp_sk_clone()
security_inet_conn_established()
security_sctp_assoc_request()
-----------------------------
Passes the ``@ep`` and ``@chunk->skb`` of the association INIT packet to the
security module. Returns 0 on success, error on failure.
::
@ep - pointer to sctp endpoint structure.
@skb - pointer to skbuff of association packet.
The security module performs the following operations:
IF this is the first association on ``@ep->base.sk``, then set the peer
sid to that in ``@skb``. This will ensure there is only one peer sid
assigned to ``@ep->base.sk`` that may support multiple associations.
ELSE validate the ``@ep->base.sk peer_sid`` against the ``@skb peer sid``
to determine whether the association should be allowed or denied.
Set the sctp ``@ep sid`` to socket's sid (from ``ep->base.sk``) with
MLS portion taken from ``@skb peer sid``. This will be used by SCTP
TCP style sockets and peeled off connections as they cause a new socket
to be generated.
If IP security options are configured (CIPSO/CALIPSO), then the ip
options are set on the socket.
security_sctp_bind_connect()
-----------------------------
Checks permissions required for ipv4/ipv6 addresses based on the ``@optname``
as follows::
------------------------------------------------------------------
| BIND Permission Checks |
| @optname | @address contains |
|----------------------------|-----------------------------------|
| SCTP_SOCKOPT_BINDX_ADD | One or more ipv4 / ipv6 addresses |
| SCTP_PRIMARY_ADDR | Single ipv4 or ipv6 address |
| SCTP_SET_PEER_PRIMARY_ADDR | Single ipv4 or ipv6 address |
------------------------------------------------------------------
------------------------------------------------------------------
| CONNECT Permission Checks |
| @optname | @address contains |
|----------------------------|-----------------------------------|
| SCTP_SOCKOPT_CONNECTX | One or more ipv4 / ipv6 addresses |
| SCTP_PARAM_ADD_IP | One or more ipv4 / ipv6 addresses |
| SCTP_SENDMSG_CONNECT | Single ipv4 or ipv6 address |
| SCTP_PARAM_SET_PRIMARY | Single ipv4 or ipv6 address |
------------------------------------------------------------------
``Documentation/security/LSM-sctp.rst`` gives a summary of the ``@optname``
entries and also describes ASCONF chunk processing when Dynamic Address
Reconfiguration is enabled.
security_sctp_sk_clone()
-------------------------
Called whenever a new socket is created by **accept**\(2) (i.e. a TCP style
socket) or when a socket is 'peeled off' e.g userspace calls
**sctp_peeloff**\(3). ``security_sctp_sk_clone()`` will set the new
sockets sid and peer sid to that contained in the ``@ep sid`` and
``@ep peer sid`` respectively.
::
@ep - pointer to current sctp endpoint structure.
@sk - pointer to current sock structure.
@sk - pointer to new sock structure.
security_inet_conn_established()
---------------------------------
Called when a COOKIE ACK is received where it sets the connection's peer sid
to that in ``@skb``::
@sk - pointer to sock structure.
@skb - pointer to skbuff of the COOKIE ACK packet.
Policy Statements
==================
The following class and permissions to support SCTP are available within the
kernel::
class sctp_socket inherits socket { node_bind }
whenever the following policy capability is enabled::
policycap extended_socket_class;
SELinux SCTP support adds the ``name_connect`` permission for connecting
to a specific port type and the ``association`` permission that is explained
in the section below.
If userspace tools have been updated, SCTP will support the ``portcon``
statement as shown in the following example::
portcon sctp 1024-1036 system_u:object_r:sctp_ports_t:s0
SCTP Peer Labeling
===================
An SCTP socket will only have one peer label assigned to it. This will be
assigned during the establishment of the first association. Any further
associations on this socket will have their packet peer label compared to
the sockets peer label, and only if they are different will the
``association`` permission be validated. This is validated by checking the
socket peer sid against the received packets peer sid to determine whether
the association should be allowed or denied.
NOTES:
1) If peer labeling is not enabled, then the peer context will always be
``SECINITSID_UNLABELED`` (``unlabeled_t`` in Reference Policy).
2) As SCTP can support more than one transport address per endpoint
(multi-homing) on a single socket, it is possible to configure policy
and NetLabel to provide different peer labels for each of these. As the
socket peer label is determined by the first associations transport
address, it is recommended that all peer labels are consistent.
3) **getpeercon**\(3) may be used by userspace to retrieve the sockets peer
context.
4) While not SCTP specific, be aware when using NetLabel that if a label
is assigned to a specific interface, and that interface 'goes down',
then the NetLabel service will remove the entry. Therefore ensure that
the network startup scripts call **netlabelctl**\(8) to set the required
label (see **netlabel-config**\(8) helper script for details).
5) The NetLabel SCTP peer labeling rules apply as discussed in the following
set of posts tagged "netlabel" at: http://www.paul-moore.com/blog/t.
6) CIPSO is only supported for IPv4 addressing: ``socket(AF_INET, ...)``
CALIPSO is only supported for IPv6 addressing: ``socket(AF_INET6, ...)``
Note the following when testing CIPSO/CALIPSO:
a) CIPSO will send an ICMP packet if an SCTP packet cannot be
delivered because of an invalid label.
b) CALIPSO does not send an ICMP packet, just silently discards it.
7) IPSEC is not supported as RFC 3554 - sctp/ipsec support has not been
implemented in userspace (**racoon**\(8) or **ipsec_pluto**\(8)),
although the kernel supports SCTP/IPSEC.

View file

@ -906,6 +906,33 @@
* associated with the TUN device's security structure.
* @security pointer to the TUN devices's security structure.
*
* Security hooks for SCTP
*
* @sctp_assoc_request:
* Passes the @ep and @chunk->skb of the association INIT packet to
* the security module.
* @ep pointer to sctp endpoint structure.
* @skb pointer to skbuff of association packet.
* Return 0 on success, error on failure.
* @sctp_bind_connect:
* Validiate permissions required for each address associated with sock
* @sk. Depending on @optname, the addresses will be treated as either
* for a connect or bind service. The @addrlen is calculated on each
* ipv4 and ipv6 address using sizeof(struct sockaddr_in) or
* sizeof(struct sockaddr_in6).
* @sk pointer to sock structure.
* @optname name of the option to validate.
* @address list containing one or more ipv4/ipv6 addresses.
* @addrlen total length of address(s).
* Return 0 on success, error on failure.
* @sctp_sk_clone:
* Called whenever a new socket is created by accept(2) (i.e. a TCP
* style socket) or when a socket is 'peeled off' e.g userspace
* calls sctp_peeloff(3).
* @ep pointer to current sctp endpoint structure.
* @sk pointer to current sock structure.
* @sk pointer to new sock structure.
*
* Security hooks for Infiniband
*
* @ib_pkey_access:
@ -1665,6 +1692,12 @@ union security_list_options {
int (*tun_dev_attach_queue)(void *security);
int (*tun_dev_attach)(struct sock *sk, void *security);
int (*tun_dev_open)(void *security);
int (*sctp_assoc_request)(struct sctp_endpoint *ep,
struct sk_buff *skb);
int (*sctp_bind_connect)(struct sock *sk, int optname,
struct sockaddr *address, int addrlen);
void (*sctp_sk_clone)(struct sctp_endpoint *ep, struct sock *sk,
struct sock *newsk);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
@ -1914,6 +1947,9 @@ struct security_hook_heads {
struct list_head tun_dev_attach_queue;
struct list_head tun_dev_attach;
struct list_head tun_dev_open;
struct list_head sctp_assoc_request;
struct list_head sctp_bind_connect;
struct list_head sctp_sk_clone;
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
struct list_head ib_pkey_access;

View file

@ -112,6 +112,7 @@ struct xfrm_policy;
struct xfrm_state;
struct xfrm_user_sec_ctx;
struct seq_file;
struct sctp_endpoint;
#ifdef CONFIG_MMU
extern unsigned long mmap_min_addr;
@ -1226,6 +1227,11 @@ int security_tun_dev_create(void);
int security_tun_dev_attach_queue(void *security);
int security_tun_dev_attach(struct sock *sk, void *security);
int security_tun_dev_open(void *security);
int security_sctp_assoc_request(struct sctp_endpoint *ep, struct sk_buff *skb);
int security_sctp_bind_connect(struct sock *sk, int optname,
struct sockaddr *address, int addrlen);
void security_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk,
struct sock *newsk);
#else /* CONFIG_SECURITY_NETWORK */
static inline int security_unix_stream_connect(struct sock *sock,
@ -1418,6 +1424,25 @@ static inline int security_tun_dev_open(void *security)
{
return 0;
}
static inline int security_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb)
{
return 0;
}
static inline int security_sctp_bind_connect(struct sock *sk, int optname,
struct sockaddr *address,
int addrlen)
{
return 0;
}
static inline void security_sctp_sk_clone(struct sctp_endpoint *ep,
struct sock *sk,
struct sock *newsk)
{
}
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND

View file

@ -432,9 +432,11 @@ static inline int sctp_list_single_entry(struct list_head *head)
static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
{
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
struct sctp_af *af = sp->pf->af;
int frag = pmtu;
frag -= sp->pf->af->net_header_len;
frag -= af->ip_options_len(asoc->base.sk);
frag -= af->net_header_len;
frag -= sizeof(struct sctphdr) + sctp_datachk_len(&asoc->stream);
if (asoc->user_frag)

View file

@ -491,6 +491,7 @@ struct sctp_af {
void (*ecn_capable)(struct sock *sk);
__u16 net_header_len;
int sockaddr_len;
int (*ip_options_len)(struct sock *sk);
sa_family_t sa_family;
struct list_head list;
};
@ -515,6 +516,7 @@ struct sctp_pf {
int (*addr_to_user)(struct sctp_sock *sk, union sctp_addr *addr);
void (*to_sk_saddr)(union sctp_addr *, struct sock *sk);
void (*to_sk_daddr)(union sctp_addr *, struct sock *sk);
void (*copy_ip_options)(struct sock *sk, struct sock *newsk);
struct sctp_af *af;
};
@ -1320,6 +1322,16 @@ struct sctp_endpoint {
reconf_enable:1;
__u8 strreset_enable;
/* Security identifiers from incoming (INIT). These are set by
* security_sctp_assoc_request(). These will only be used by
* SCTP TCP type sockets and peeled off connections as they
* cause a new socket to be generated. security_sctp_sk_clone()
* will then plug these into the new socket.
*/
u32 secid;
u32 peer_secid;
};
/* Recover the outter endpoint structure. */

View file

@ -127,6 +127,7 @@ typedef __s32 sctp_assoc_t;
#define SCTP_STREAM_SCHEDULER 123
#define SCTP_STREAM_SCHEDULER_VALUE 124
#define SCTP_INTERLEAVING_SUPPORTED 125
#define SCTP_SENDMSG_CONNECT 126
/* PR-SCTP policies */
#define SCTP_PR_SCTP_NONE 0x0000

View file

@ -1472,6 +1472,16 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb,
iface = rcu_dereference(netlbl_unlhsh_def);
if (iface == NULL || !iface->valid)
goto unlabel_getattr_nolabel;
#if IS_ENABLED(CONFIG_IPV6)
/* When resolving a fallback label, check the sk_buff version as
* it is possible (e.g. SCTP) to have family = PF_INET6 while
* receiving ip_hdr(skb)->version = 4.
*/
if (family == PF_INET6 && ip_hdr(skb)->version == 4)
family = PF_INET;
#endif /* IPv6 */
switch (family) {
case PF_INET: {
struct iphdr *hdr4;

View file

@ -172,6 +172,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
struct list_head *pos, *temp;
struct sctp_chunk *chunk;
struct sctp_datamsg *msg;
struct sctp_sock *sp;
struct sctp_af *af;
int err;
msg = sctp_datamsg_new(GFP_KERNEL);
@ -190,9 +192,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
/* This is the biggest possible DATA chunk that can fit into
* the packet
*/
max_data = asoc->pathmtu -
sctp_sk(asoc->base.sk)->pf->af->net_header_len -
sizeof(struct sctphdr) - sctp_datachk_len(&asoc->stream);
sp = sctp_sk(asoc->base.sk);
af = sp->pf->af;
max_data = asoc->pathmtu - af->net_header_len -
sizeof(struct sctphdr) - sctp_datachk_len(&asoc->stream) -
af->ip_options_len(asoc->base.sk);
max_data = SCTP_TRUNC4(max_data);
/* If the the peer requested that we authenticate DATA chunks

View file

@ -427,6 +427,41 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist,
rcu_read_unlock();
}
/* Copy over any ip options */
static void sctp_v6_copy_ip_options(struct sock *sk, struct sock *newsk)
{
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
struct ipv6_txoptions *opt;
newnp = inet6_sk(newsk);
rcu_read_lock();
opt = rcu_dereference(np->opt);
if (opt) {
opt = ipv6_dup_options(newsk, opt);
if (!opt)
pr_err("%s: Failed to copy ip options\n", __func__);
}
RCU_INIT_POINTER(newnp->opt, opt);
rcu_read_unlock();
}
/* Account for the IP options */
static int sctp_v6_ip_options_len(struct sock *sk)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_txoptions *opt;
int len = 0;
rcu_read_lock();
opt = rcu_dereference(np->opt);
if (opt)
len = opt->opt_flen + opt->opt_nflen;
rcu_read_unlock();
return len;
}
/* Initialize a sockaddr_storage from in incoming skb. */
static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff *skb,
int is_saddr)
@ -666,7 +701,6 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
struct sock *newsk;
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
struct sctp6_sock *newsctp6sk;
struct ipv6_txoptions *opt;
newsk = sk_alloc(sock_net(sk), PF_INET6, GFP_KERNEL, sk->sk_prot, kern);
if (!newsk)
@ -689,12 +723,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
newnp->ipv6_ac_list = NULL;
newnp->ipv6_fl_list = NULL;
rcu_read_lock();
opt = rcu_dereference(np->opt);
if (opt)
opt = ipv6_dup_options(newsk, opt);
RCU_INIT_POINTER(newnp->opt, opt);
rcu_read_unlock();
sctp_v6_copy_ip_options(sk, newsk);
/* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname()
* and getpeername().
@ -1041,6 +1070,7 @@ static struct sctp_af sctp_af_inet6 = {
.ecn_capable = sctp_v6_ecn_capable,
.net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6),
.ip_options_len = sctp_v6_ip_options_len,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_ipv6_setsockopt,
.compat_getsockopt = compat_ipv6_getsockopt,
@ -1059,6 +1089,7 @@ static struct sctp_pf sctp_pf_inet6 = {
.addr_to_user = sctp_v6_addr_to_user,
.to_sk_saddr = sctp_v6_to_sk_saddr,
.to_sk_daddr = sctp_v6_to_sk_daddr,
.copy_ip_options = sctp_v6_copy_ip_options,
.af = &sctp_af_inet6,
};

View file

@ -69,7 +69,11 @@ static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet,
static void sctp_packet_reset(struct sctp_packet *packet)
{
/* sctp_packet_transmit() relies on this to reset size to the
* current overhead after sending packets.
*/
packet->size = packet->overhead;
packet->has_cookie_echo = 0;
packet->has_sack = 0;
packet->has_data = 0;
@ -87,6 +91,7 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
struct sctp_transport *tp = packet->transport;
struct sctp_association *asoc = tp->asoc;
struct sock *sk;
size_t overhead = sizeof(struct ipv6hdr) + sizeof(struct sctphdr);
pr_debug("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag);
packet->vtag = vtag;
@ -95,10 +100,22 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
if (!sctp_packet_empty(packet))
return;
/* set packet max_size with pathmtu */
/* set packet max_size with pathmtu, then calculate overhead */
packet->max_size = tp->pathmtu;
if (!asoc)
if (asoc) {
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
struct sctp_af *af = sp->pf->af;
overhead = af->net_header_len +
af->ip_options_len(asoc->base.sk);
overhead += sizeof(struct sctphdr);
packet->overhead = overhead;
packet->size = overhead;
} else {
packet->overhead = overhead;
packet->size = overhead;
return;
}
/* update dst or transport pathmtu if in need */
sk = asoc->base.sk;
@ -140,23 +157,14 @@ void sctp_packet_init(struct sctp_packet *packet,
struct sctp_transport *transport,
__u16 sport, __u16 dport)
{
struct sctp_association *asoc = transport->asoc;
size_t overhead;
pr_debug("%s: packet:%p transport:%p\n", __func__, packet, transport);
packet->transport = transport;
packet->source_port = sport;
packet->destination_port = dport;
INIT_LIST_HEAD(&packet->chunk_list);
if (asoc) {
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
overhead = sp->pf->af->net_header_len;
} else {
overhead = sizeof(struct ipv6hdr);
}
overhead += sizeof(struct sctphdr);
packet->overhead = overhead;
/* The overhead will be calculated by sctp_packet_config() */
packet->overhead = 0;
sctp_packet_reset(packet);
packet->vtag = 0;
}

View file

@ -187,6 +187,45 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp,
return error;
}
/* Copy over any ip options */
static void sctp_v4_copy_ip_options(struct sock *sk, struct sock *newsk)
{
struct inet_sock *newinet, *inet = inet_sk(sk);
struct ip_options_rcu *inet_opt, *newopt = NULL;
newinet = inet_sk(newsk);
rcu_read_lock();
inet_opt = rcu_dereference(inet->inet_opt);
if (inet_opt) {
newopt = sock_kmalloc(newsk, sizeof(*inet_opt) +
inet_opt->opt.optlen, GFP_ATOMIC);
if (newopt)
memcpy(newopt, inet_opt, sizeof(*inet_opt) +
inet_opt->opt.optlen);
else
pr_err("%s: Failed to copy ip options\n", __func__);
}
RCU_INIT_POINTER(newinet->inet_opt, newopt);
rcu_read_unlock();
}
/* Account for the IP options */
static int sctp_v4_ip_options_len(struct sock *sk)
{
struct inet_sock *inet = inet_sk(sk);
struct ip_options_rcu *inet_opt;
int len = 0;
rcu_read_lock();
inet_opt = rcu_dereference(inet->inet_opt);
if (inet_opt)
len = inet_opt->opt.optlen;
rcu_read_unlock();
return len;
}
/* Initialize a sctp_addr from in incoming skb. */
static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
int is_saddr)
@ -538,6 +577,8 @@ static struct sock *sctp_v4_create_accept_sk(struct sock *sk,
sctp_copy_sock(newsk, sk, asoc);
sock_reset_flag(newsk, SOCK_ZAPPED);
sctp_v4_copy_ip_options(sk, newsk);
newinet = inet_sk(newsk);
newinet->inet_daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
@ -956,6 +997,7 @@ static struct sctp_pf sctp_pf_inet = {
.addr_to_user = sctp_v4_addr_to_user,
.to_sk_saddr = sctp_v4_to_sk_saddr,
.to_sk_daddr = sctp_v4_to_sk_daddr,
.copy_ip_options = sctp_v4_copy_ip_options,
.af = &sctp_af_inet
};
@ -1040,6 +1082,7 @@ static struct sctp_af sctp_af_inet = {
.ecn_capable = sctp_v4_ecn_capable,
.net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in),
.ip_options_len = sctp_v4_ip_options_len,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_ip_setsockopt,
.compat_getsockopt = compat_ip_getsockopt,

View file

@ -3098,6 +3098,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
if (af->is_any(&addr))
memcpy(&addr, &asconf->source, sizeof(addr));
if (security_sctp_bind_connect(asoc->ep->base.sk,
SCTP_PARAM_ADD_IP,
(struct sockaddr *)&addr,
af->sockaddr_len))
return SCTP_ERROR_REQ_REFUSED;
/* ADDIP 4.3 D9) If an endpoint receives an ADD IP address
* request and does not have the local resources to add this
* new address to the association, it MUST return an Error
@ -3164,6 +3170,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
if (af->is_any(&addr))
memcpy(&addr.v4, sctp_source(asconf), sizeof(addr));
if (security_sctp_bind_connect(asoc->ep->base.sk,
SCTP_PARAM_SET_PRIMARY,
(struct sockaddr *)&addr,
af->sockaddr_len))
return SCTP_ERROR_REQ_REFUSED;
peer = sctp_assoc_lookup_paddr(asoc, &addr);
if (!peer)
return SCTP_ERROR_DNS_FAILED;

View file

@ -321,6 +321,11 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net,
struct sctp_packet *packet;
int len;
/* Update socket peer label if first association. */
if (security_sctp_assoc_request((struct sctp_endpoint *)ep,
chunk->skb))
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
/* 6.10 Bundling
* An endpoint MUST NOT bundle INIT, INIT ACK or
* SHUTDOWN COMPLETE with any other chunks.
@ -922,6 +927,9 @@ enum sctp_disposition sctp_sf_do_5_1E_ca(struct net *net,
*/
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL());
/* Set peer label for connection. */
security_inet_conn_established(ep->base.sk, chunk->skb);
/* RFC 2960 5.1 Normal Establishment of an Association
*
* E) Upon reception of the COOKIE ACK, endpoint "A" will move
@ -1459,6 +1467,11 @@ static enum sctp_disposition sctp_sf_do_unexpected_init(
struct sctp_packet *packet;
int len;
/* Update socket peer label if first association. */
if (security_sctp_assoc_request((struct sctp_endpoint *)ep,
chunk->skb))
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
/* 6.10 Bundling
* An endpoint MUST NOT bundle INIT, INIT ACK or
* SHUTDOWN COMPLETE with any other chunks.
@ -2145,6 +2158,11 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook(
}
}
/* Update socket peer label if first association. */
if (security_sctp_assoc_request((struct sctp_endpoint *)ep,
chunk->skb))
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
/* Set temp so that it won't be added into hashtable */
new_asoc->temp = 1;

View file

@ -1046,6 +1046,12 @@ static int sctp_setsockopt_bindx(struct sock *sk,
/* Do the work. */
switch (op) {
case SCTP_BINDX_ADD_ADDR:
/* Allow security module to validate bindx addresses. */
err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD,
(struct sockaddr *)kaddrs,
addrs_size);
if (err)
goto out;
err = sctp_bindx_add(sk, kaddrs, addrcnt);
if (err)
goto out;
@ -1255,6 +1261,7 @@ static int __sctp_connect(struct sock *sk,
if (assoc_id)
*assoc_id = asoc->assoc_id;
err = sctp_wait_for_connect(asoc, &timeo);
/* Note: the asoc may be freed after the return of
* sctp_wait_for_connect.
@ -1350,7 +1357,16 @@ static int __sctp_setsockopt_connectx(struct sock *sk,
if (unlikely(IS_ERR(kaddrs)))
return PTR_ERR(kaddrs);
/* Allow security module to validate connectx addresses. */
err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_CONNECTX,
(struct sockaddr *)kaddrs,
addrs_size);
if (err)
goto out_free;
err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id);
out_free:
kvfree(kaddrs);
return err;
@ -1680,6 +1696,7 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
struct sctp_association *asoc;
enum sctp_scope scope;
struct cmsghdr *cmsg;
struct sctp_af *af;
int err;
*tp = NULL;
@ -1705,6 +1722,21 @@ static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
scope = sctp_scope(daddr);
/* Label connection socket for first association 1-to-many
* style for client sequence socket()->sendmsg(). This
* needs to be done before sctp_assoc_add_peer() as that will
* set up the initial packet that needs to account for any
* security ip options (CIPSO/CALIPSO) added to the packet.
*/
af = sctp_get_af_specific(daddr->sa.sa_family);
if (!af)
return -EINVAL;
err = security_sctp_bind_connect(sk, SCTP_SENDMSG_CONNECT,
(struct sockaddr *)daddr,
af->sockaddr_len);
if (err < 0)
return err;
asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!asoc)
return -ENOMEM;
@ -2932,6 +2964,8 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
{
struct sctp_prim prim;
struct sctp_transport *trans;
struct sctp_af *af;
int err;
if (optlen != sizeof(struct sctp_prim))
return -EINVAL;
@ -2939,6 +2973,17 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
return -EFAULT;
/* Allow security module to validate address but need address len. */
af = sctp_get_af_specific(prim.ssp_addr.ss_family);
if (!af)
return -EINVAL;
err = security_sctp_bind_connect(sk, SCTP_PRIMARY_ADDR,
(struct sockaddr *)&prim.ssp_addr,
af->sockaddr_len);
if (err)
return err;
trans = sctp_addr_id2transport(sk, &prim.ssp_addr, prim.ssp_assoc_id);
if (!trans)
return -EINVAL;
@ -3161,6 +3206,7 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsign
static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
{
struct sctp_sock *sp = sctp_sk(sk);
struct sctp_af *af = sp->pf->af;
struct sctp_assoc_value params;
struct sctp_association *asoc;
int val;
@ -3185,7 +3231,8 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
if (val) {
int min_len, max_len;
min_len = SCTP_DEFAULT_MINSEGMENT - sp->pf->af->net_header_len;
min_len = SCTP_DEFAULT_MINSEGMENT - af->net_header_len;
min_len -= af->ip_options_len(sk);
min_len -= sizeof(struct sctphdr) +
sizeof(struct sctp_data_chunk);
@ -3198,7 +3245,8 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
asoc = sctp_id2assoc(sk, params.assoc_id);
if (asoc) {
if (val == 0) {
val = asoc->pathmtu - sp->pf->af->net_header_len;
val = asoc->pathmtu - af->net_header_len;
val -= af->ip_options_len(sk);
val -= sizeof(struct sctphdr) +
sctp_datachk_len(&asoc->stream);
}
@ -3267,6 +3315,13 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva
if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim.sspp_addr))
return -EADDRNOTAVAIL;
/* Allow security module to validate address. */
err = security_sctp_bind_connect(sk, SCTP_SET_PEER_PRIMARY_ADDR,
(struct sockaddr *)&prim.sspp_addr,
af->sockaddr_len);
if (err)
return err;
/* Create an ASCONF chunk with SET_PRIMARY parameter */
chunk = sctp_make_asconf_set_prim(asoc,
(union sctp_addr *)&prim.sspp_addr);
@ -5140,9 +5195,11 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
sctp_copy_sock(sock->sk, sk, asoc);
/* Make peeled-off sockets more like 1-1 accepted sockets.
* Set the daddr and initialize id to something more random
* Set the daddr and initialize id to something more random and also
* copy over any ip options.
*/
sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk);
sp->pf->copy_ip_options(sk, sock->sk);
/* Populate the fields of the newsk from the oldsk and migrate the
* asoc to the newsk.
@ -8465,6 +8522,8 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
{
struct inet_sock *inet = inet_sk(sk);
struct inet_sock *newinet;
struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
newsk->sk_type = sk->sk_type;
newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
@ -8507,7 +8566,10 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
if (newsk->sk_flags & SK_FLAGS_TIMESTAMP)
net_enable_timestamp();
security_sk_clone(sk, newsk);
/* Set newsk security attributes from orginal sk and connection
* security attribute from ep.
*/
security_sctp_sk_clone(ep, sk, newsk);
}
static inline void sctp_copy_descendant(struct sock *sk_to,

View file

@ -1473,6 +1473,7 @@ void security_inet_conn_established(struct sock *sk,
{
call_void_hook(inet_conn_established, sk, skb);
}
EXPORT_SYMBOL(security_inet_conn_established);
int security_secmark_relabel_packet(u32 secid)
{
@ -1528,6 +1529,27 @@ int security_tun_dev_open(void *security)
}
EXPORT_SYMBOL(security_tun_dev_open);
int security_sctp_assoc_request(struct sctp_endpoint *ep, struct sk_buff *skb)
{
return call_int_hook(sctp_assoc_request, 0, ep, skb);
}
EXPORT_SYMBOL(security_sctp_assoc_request);
int security_sctp_bind_connect(struct sock *sk, int optname,
struct sockaddr *address, int addrlen)
{
return call_int_hook(sctp_bind_connect, 0, sk, optname,
address, addrlen);
}
EXPORT_SYMBOL(security_sctp_bind_connect);
void security_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk,
struct sock *newsk)
{
call_void_hook(sctp_sk_clone, ep, sk, newsk);
}
EXPORT_SYMBOL(security_sctp_sk_clone);
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND

View file

@ -82,14 +82,42 @@ struct avc_callback_node {
struct avc_callback_node *next;
};
/* Exported via selinufs */
unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
#endif
static struct avc_cache avc_cache;
struct selinux_avc {
unsigned int avc_cache_threshold;
struct avc_cache avc_cache;
};
static struct selinux_avc selinux_avc;
void selinux_avc_init(struct selinux_avc **avc)
{
int i;
selinux_avc.avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
INIT_HLIST_HEAD(&selinux_avc.avc_cache.slots[i]);
spin_lock_init(&selinux_avc.avc_cache.slots_lock[i]);
}
atomic_set(&selinux_avc.avc_cache.active_nodes, 0);
atomic_set(&selinux_avc.avc_cache.lru_hint, 0);
*avc = &selinux_avc;
}
unsigned int avc_get_cache_threshold(struct selinux_avc *avc)
{
return avc->avc_cache_threshold;
}
void avc_set_cache_threshold(struct selinux_avc *avc,
unsigned int cache_threshold)
{
avc->avc_cache_threshold = cache_threshold;
}
static struct avc_callback_node *avc_callbacks;
static struct kmem_cache *avc_node_cachep;
static struct kmem_cache *avc_xperms_data_cachep;
@ -143,13 +171,14 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
* @tsid: target security identifier
* @tclass: target security class
*/
static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass)
static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass)
{
int rc;
char *scontext;
u32 scontext_len;
rc = security_sid_to_context(ssid, &scontext, &scontext_len);
rc = security_sid_to_context(state, ssid, &scontext, &scontext_len);
if (rc)
audit_log_format(ab, "ssid=%d", ssid);
else {
@ -157,7 +186,7 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
kfree(scontext);
}
rc = security_sid_to_context(tsid, &scontext, &scontext_len);
rc = security_sid_to_context(state, tsid, &scontext, &scontext_len);
if (rc)
audit_log_format(ab, " tsid=%d", tsid);
else {
@ -176,15 +205,6 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
*/
void __init avc_init(void)
{
int i;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
INIT_HLIST_HEAD(&avc_cache.slots[i]);
spin_lock_init(&avc_cache.slots_lock[i]);
}
atomic_set(&avc_cache.active_nodes, 0);
atomic_set(&avc_cache.lru_hint, 0);
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
0, SLAB_PANIC, NULL);
avc_xperms_cachep = kmem_cache_create("avc_xperms_node",
@ -199,7 +219,7 @@ void __init avc_init(void)
0, SLAB_PANIC, NULL);
}
int avc_get_hash_stats(char *page)
int avc_get_hash_stats(struct selinux_avc *avc, char *page)
{
int i, chain_len, max_chain_len, slots_used;
struct avc_node *node;
@ -210,7 +230,7 @@ int avc_get_hash_stats(char *page)
slots_used = 0;
max_chain_len = 0;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
head = &avc_cache.slots[i];
head = &avc->avc_cache.slots[i];
if (!hlist_empty(head)) {
slots_used++;
chain_len = 0;
@ -225,7 +245,7 @@ int avc_get_hash_stats(char *page)
return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
"longest chain: %d\n",
atomic_read(&avc_cache.active_nodes),
atomic_read(&avc->avc_cache.active_nodes),
slots_used, AVC_CACHE_SLOTS, max_chain_len);
}
@ -462,11 +482,12 @@ static inline u32 avc_xperms_audit_required(u32 requested,
return audited;
}
static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct av_decision *avd,
struct extended_perms_decision *xpd,
u8 perm, int result,
struct common_audit_data *ad)
static inline int avc_xperms_audit(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct av_decision *avd,
struct extended_perms_decision *xpd,
u8 perm, int result,
struct common_audit_data *ad)
{
u32 audited, denied;
@ -474,7 +495,7 @@ static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass,
requested, avd, xpd, perm, result, &denied);
if (likely(!audited))
return 0;
return slow_avc_audit(ssid, tsid, tclass, requested,
return slow_avc_audit(state, ssid, tsid, tclass, requested,
audited, denied, result, ad, 0);
}
@ -486,29 +507,30 @@ static void avc_node_free(struct rcu_head *rhead)
avc_cache_stats_incr(frees);
}
static void avc_node_delete(struct avc_node *node)
static void avc_node_delete(struct selinux_avc *avc, struct avc_node *node)
{
hlist_del_rcu(&node->list);
call_rcu(&node->rhead, avc_node_free);
atomic_dec(&avc_cache.active_nodes);
atomic_dec(&avc->avc_cache.active_nodes);
}
static void avc_node_kill(struct avc_node *node)
static void avc_node_kill(struct selinux_avc *avc, struct avc_node *node)
{
avc_xperms_free(node->ae.xp_node);
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
atomic_dec(&avc_cache.active_nodes);
atomic_dec(&avc->avc_cache.active_nodes);
}
static void avc_node_replace(struct avc_node *new, struct avc_node *old)
static void avc_node_replace(struct selinux_avc *avc,
struct avc_node *new, struct avc_node *old)
{
hlist_replace_rcu(&old->list, &new->list);
call_rcu(&old->rhead, avc_node_free);
atomic_dec(&avc_cache.active_nodes);
atomic_dec(&avc->avc_cache.active_nodes);
}
static inline int avc_reclaim_node(void)
static inline int avc_reclaim_node(struct selinux_avc *avc)
{
struct avc_node *node;
int hvalue, try, ecx;
@ -517,16 +539,17 @@ static inline int avc_reclaim_node(void)
spinlock_t *lock;
for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
head = &avc_cache.slots[hvalue];
lock = &avc_cache.slots_lock[hvalue];
hvalue = atomic_inc_return(&avc->avc_cache.lru_hint) &
(AVC_CACHE_SLOTS - 1);
head = &avc->avc_cache.slots[hvalue];
lock = &avc->avc_cache.slots_lock[hvalue];
if (!spin_trylock_irqsave(lock, flags))
continue;
rcu_read_lock();
hlist_for_each_entry(node, head, list) {
avc_node_delete(node);
avc_node_delete(avc, node);
avc_cache_stats_incr(reclaims);
ecx++;
if (ecx >= AVC_CACHE_RECLAIM) {
@ -542,7 +565,7 @@ out:
return ecx;
}
static struct avc_node *avc_alloc_node(void)
static struct avc_node *avc_alloc_node(struct selinux_avc *avc)
{
struct avc_node *node;
@ -553,8 +576,9 @@ static struct avc_node *avc_alloc_node(void)
INIT_HLIST_NODE(&node->list);
avc_cache_stats_incr(allocations);
if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
avc_reclaim_node();
if (atomic_inc_return(&avc->avc_cache.active_nodes) >
avc->avc_cache_threshold)
avc_reclaim_node(avc);
out:
return node;
@ -568,14 +592,15 @@ static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tcl
memcpy(&node->ae.avd, avd, sizeof(node->ae.avd));
}
static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
static inline struct avc_node *avc_search_node(struct selinux_avc *avc,
u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node, *ret = NULL;
int hvalue;
struct hlist_head *head;
hvalue = avc_hash(ssid, tsid, tclass);
head = &avc_cache.slots[hvalue];
head = &avc->avc_cache.slots[hvalue];
hlist_for_each_entry_rcu(node, head, list) {
if (ssid == node->ae.ssid &&
tclass == node->ae.tclass &&
@ -600,12 +625,13 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
* then this function returns the avc_node.
* Otherwise, this function returns NULL.
*/
static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
static struct avc_node *avc_lookup(struct selinux_avc *avc,
u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node;
avc_cache_stats_incr(lookups);
node = avc_search_node(ssid, tsid, tclass);
node = avc_search_node(avc, ssid, tsid, tclass);
if (node)
return node;
@ -614,7 +640,8 @@ static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
return NULL;
}
static int avc_latest_notif_update(int seqno, int is_insert)
static int avc_latest_notif_update(struct selinux_avc *avc,
int seqno, int is_insert)
{
int ret = 0;
static DEFINE_SPINLOCK(notif_lock);
@ -622,14 +649,14 @@ static int avc_latest_notif_update(int seqno, int is_insert)
spin_lock_irqsave(&notif_lock, flag);
if (is_insert) {
if (seqno < avc_cache.latest_notif) {
if (seqno < avc->avc_cache.latest_notif) {
printk(KERN_WARNING "SELinux: avc: seqno %d < latest_notif %d\n",
seqno, avc_cache.latest_notif);
seqno, avc->avc_cache.latest_notif);
ret = -EAGAIN;
}
} else {
if (seqno > avc_cache.latest_notif)
avc_cache.latest_notif = seqno;
if (seqno > avc->avc_cache.latest_notif)
avc->avc_cache.latest_notif = seqno;
}
spin_unlock_irqrestore(&notif_lock, flag);
@ -654,18 +681,19 @@ static int avc_latest_notif_update(int seqno, int is_insert)
* the access vectors into a cache entry, returns
* avc_node inserted. Otherwise, this function returns NULL.
*/
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
struct av_decision *avd,
struct avc_xperms_node *xp_node)
static struct avc_node *avc_insert(struct selinux_avc *avc,
u32 ssid, u32 tsid, u16 tclass,
struct av_decision *avd,
struct avc_xperms_node *xp_node)
{
struct avc_node *pos, *node = NULL;
int hvalue;
unsigned long flag;
if (avc_latest_notif_update(avd->seqno, 1))
if (avc_latest_notif_update(avc, avd->seqno, 1))
goto out;
node = avc_alloc_node();
node = avc_alloc_node(avc);
if (node) {
struct hlist_head *head;
spinlock_t *lock;
@ -678,15 +706,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
kmem_cache_free(avc_node_cachep, node);
return NULL;
}
head = &avc_cache.slots[hvalue];
lock = &avc_cache.slots_lock[hvalue];
head = &avc->avc_cache.slots[hvalue];
lock = &avc->avc_cache.slots_lock[hvalue];
spin_lock_irqsave(lock, flag);
hlist_for_each_entry(pos, head, list) {
if (pos->ae.ssid == ssid &&
pos->ae.tsid == tsid &&
pos->ae.tclass == tclass) {
avc_node_replace(node, pos);
avc_node_replace(avc, node, pos);
goto found;
}
}
@ -724,9 +752,10 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
audit_log_format(ab, " ");
avc_dump_query(ab, ad->selinux_audit_data->ssid,
ad->selinux_audit_data->tsid,
ad->selinux_audit_data->tclass);
avc_dump_query(ab, ad->selinux_audit_data->state,
ad->selinux_audit_data->ssid,
ad->selinux_audit_data->tsid,
ad->selinux_audit_data->tclass);
if (ad->selinux_audit_data->denied) {
audit_log_format(ab, " permissive=%u",
ad->selinux_audit_data->result ? 0 : 1);
@ -734,10 +763,11 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
}
/* This is the slow part of avc audit with big stack footprint */
noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
u32 requested, u32 audited, u32 denied, int result,
struct common_audit_data *a,
unsigned flags)
noinline int slow_avc_audit(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass,
u32 requested, u32 audited, u32 denied, int result,
struct common_audit_data *a,
unsigned int flags)
{
struct common_audit_data stack_data;
struct selinux_audit_data sad;
@ -765,6 +795,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
sad.audited = audited;
sad.denied = denied;
sad.result = result;
sad.state = state;
a->selinux_audit_data = &sad;
@ -813,10 +844,11 @@ out:
* otherwise, this function updates the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
u32 tsid, u16 tclass, u32 seqno,
struct extended_perms_decision *xpd,
u32 flags)
static int avc_update_node(struct selinux_avc *avc,
u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
u32 tsid, u16 tclass, u32 seqno,
struct extended_perms_decision *xpd,
u32 flags)
{
int hvalue, rc = 0;
unsigned long flag;
@ -824,7 +856,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
struct hlist_head *head;
spinlock_t *lock;
node = avc_alloc_node();
node = avc_alloc_node(avc);
if (!node) {
rc = -ENOMEM;
goto out;
@ -833,8 +865,8 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
/* Lock the target slot */
hvalue = avc_hash(ssid, tsid, tclass);
head = &avc_cache.slots[hvalue];
lock = &avc_cache.slots_lock[hvalue];
head = &avc->avc_cache.slots[hvalue];
lock = &avc->avc_cache.slots_lock[hvalue];
spin_lock_irqsave(lock, flag);
@ -850,7 +882,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
if (!orig) {
rc = -ENOENT;
avc_node_kill(node);
avc_node_kill(avc, node);
goto out_unlock;
}
@ -894,7 +926,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
avc_add_xperms_decision(node, xpd);
break;
}
avc_node_replace(node, orig);
avc_node_replace(avc, node, orig);
out_unlock:
spin_unlock_irqrestore(lock, flag);
out:
@ -904,7 +936,7 @@ out:
/**
* avc_flush - Flush the cache
*/
static void avc_flush(void)
static void avc_flush(struct selinux_avc *avc)
{
struct hlist_head *head;
struct avc_node *node;
@ -913,8 +945,8 @@ static void avc_flush(void)
int i;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
head = &avc_cache.slots[i];
lock = &avc_cache.slots_lock[i];
head = &avc->avc_cache.slots[i];
lock = &avc->avc_cache.slots_lock[i];
spin_lock_irqsave(lock, flag);
/*
@ -923,7 +955,7 @@ static void avc_flush(void)
*/
rcu_read_lock();
hlist_for_each_entry(node, head, list)
avc_node_delete(node);
avc_node_delete(avc, node);
rcu_read_unlock();
spin_unlock_irqrestore(lock, flag);
}
@ -933,12 +965,12 @@ static void avc_flush(void)
* avc_ss_reset - Flush the cache and revalidate migrated permissions.
* @seqno: policy sequence number
*/
int avc_ss_reset(u32 seqno)
int avc_ss_reset(struct selinux_avc *avc, u32 seqno)
{
struct avc_callback_node *c;
int rc = 0, tmprc;
avc_flush();
avc_flush(avc);
for (c = avc_callbacks; c; c = c->next) {
if (c->events & AVC_CALLBACK_RESET) {
@ -950,7 +982,7 @@ int avc_ss_reset(u32 seqno)
}
}
avc_latest_notif_update(seqno, 0);
avc_latest_notif_update(avc, seqno, 0);
return rc;
}
@ -963,30 +995,34 @@ int avc_ss_reset(u32 seqno)
* Don't inline this, since it's the slow-path and just
* results in a bigger stack frame.
*/
static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd,
struct avc_xperms_node *xp_node)
static noinline
struct avc_node *avc_compute_av(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd,
struct avc_xperms_node *xp_node)
{
rcu_read_unlock();
INIT_LIST_HEAD(&xp_node->xpd_head);
security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp);
security_compute_av(state, ssid, tsid, tclass, avd, &xp_node->xp);
rcu_read_lock();
return avc_insert(ssid, tsid, tclass, avd, xp_node);
return avc_insert(state->avc, ssid, tsid, tclass, avd, xp_node);
}
static noinline int avc_denied(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
u8 driver, u8 xperm, unsigned flags,
struct av_decision *avd)
static noinline int avc_denied(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
u8 driver, u8 xperm, unsigned int flags,
struct av_decision *avd)
{
if (flags & AVC_STRICT)
return -EACCES;
if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
if (enforcing_enabled(state) &&
!(avd->flags & AVD_FLAGS_PERMISSIVE))
return -EACCES;
avc_update_node(AVC_CALLBACK_GRANT, requested, driver, xperm, ssid,
tsid, tclass, avd->seqno, NULL, flags);
avc_update_node(state->avc, AVC_CALLBACK_GRANT, requested, driver,
xperm, ssid, tsid, tclass, avd->seqno, NULL, flags);
return 0;
}
@ -997,8 +1033,9 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
* as-is the case with ioctls, then multiple may be chained together and the
* driver field is used to specify which set contains the permission.
*/
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
u8 driver, u8 xperm, struct common_audit_data *ad)
int avc_has_extended_perms(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass, u32 requested,
u8 driver, u8 xperm, struct common_audit_data *ad)
{
struct avc_node *node;
struct av_decision avd;
@ -1017,9 +1054,9 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
rcu_read_lock();
node = avc_lookup(ssid, tsid, tclass);
node = avc_lookup(state->avc, ssid, tsid, tclass);
if (unlikely(!node)) {
node = avc_compute_av(ssid, tsid, tclass, &avd, xp_node);
node = avc_compute_av(state, ssid, tsid, tclass, &avd, xp_node);
} else {
memcpy(&avd, &node->ae.avd, sizeof(avd));
xp_node = node->ae.xp_node;
@ -1043,11 +1080,12 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
goto decision;
}
rcu_read_unlock();
security_compute_xperms_decision(ssid, tsid, tclass, driver,
&local_xpd);
security_compute_xperms_decision(state, ssid, tsid, tclass,
driver, &local_xpd);
rcu_read_lock();
avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver, xperm,
ssid, tsid, tclass, avd.seqno, &local_xpd, 0);
avc_update_node(state->avc, AVC_CALLBACK_ADD_XPERMS, requested,
driver, xperm, ssid, tsid, tclass, avd.seqno,
&local_xpd, 0);
} else {
avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd);
}
@ -1059,12 +1097,12 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
decision:
denied = requested & ~(avd.allowed);
if (unlikely(denied))
rc = avc_denied(ssid, tsid, tclass, requested, driver, xperm,
AVC_EXTENDED_PERMS, &avd);
rc = avc_denied(state, ssid, tsid, tclass, requested,
driver, xperm, AVC_EXTENDED_PERMS, &avd);
rcu_read_unlock();
rc2 = avc_xperms_audit(ssid, tsid, tclass, requested,
rc2 = avc_xperms_audit(state, ssid, tsid, tclass, requested,
&avd, xpd, xperm, rc, ad);
if (rc2)
return rc2;
@ -1091,10 +1129,11 @@ decision:
* auditing, e.g. in cases where a lock must be held for the check but
* should be released for the auditing.
*/
inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd)
inline int avc_has_perm_noaudit(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned int flags,
struct av_decision *avd)
{
struct avc_node *node;
struct avc_xperms_node xp_node;
@ -1105,15 +1144,16 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
rcu_read_lock();
node = avc_lookup(ssid, tsid, tclass);
node = avc_lookup(state->avc, ssid, tsid, tclass);
if (unlikely(!node))
node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
node = avc_compute_av(state, ssid, tsid, tclass, avd, &xp_node);
else
memcpy(avd, &node->ae.avd, sizeof(*avd));
denied = requested & ~(avd->allowed);
if (unlikely(denied))
rc = avc_denied(ssid, tsid, tclass, requested, 0, 0, flags, avd);
rc = avc_denied(state, ssid, tsid, tclass, requested, 0, 0,
flags, avd);
rcu_read_unlock();
return rc;
@ -1135,39 +1175,43 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
* permissions are granted, -%EACCES if any permissions are denied, or
* another -errno upon other errors.
*/
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
int avc_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct common_audit_data *auditdata)
{
struct av_decision avd;
int rc, rc2;
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0,
&avd);
rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, 0);
rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc,
auditdata, 0);
if (rc2)
return rc2;
return rc;
}
int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct common_audit_data *auditdata,
int avc_has_perm_flags(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass, u32 requested,
struct common_audit_data *auditdata,
int flags)
{
struct av_decision avd;
int rc, rc2;
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0,
&avd);
rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc,
rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc,
auditdata, flags);
if (rc2)
return rc2;
return rc;
}
u32 avc_policy_seqno(void)
u32 avc_policy_seqno(struct selinux_state *state)
{
return avc_cache.latest_notif;
return state->avc->avc_cache.latest_notif;
}
void avc_disable(void)
@ -1184,7 +1228,7 @@ void avc_disable(void)
* the cache and get that memory back.
*/
if (avc_node_cachep) {
avc_flush();
avc_flush(selinux_state.avc);
/* kmem_cache_destroy(avc_node_cachep); */
}
}

File diff suppressed because it is too large Load diff

View file

@ -152,7 +152,8 @@ static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
return 0;
}
ret = security_ib_pkey_sid(subnet_prefix, pkey_num, sid);
ret = security_ib_pkey_sid(&selinux_state, subnet_prefix, pkey_num,
sid);
if (ret)
goto out;

View file

@ -20,12 +20,6 @@
#include "av_permissions.h"
#include "security.h"
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
extern int selinux_enforcing;
#else
#define selinux_enforcing 1
#endif
/*
* An entry in the AVC.
*/
@ -58,6 +52,7 @@ struct selinux_audit_data {
u32 audited;
u32 denied;
int result;
struct selinux_state *state;
};
/*
@ -102,7 +97,8 @@ static inline u32 avc_audit_required(u32 requested,
return audited;
}
int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
int slow_avc_audit(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass,
u32 requested, u32 audited, u32 denied, int result,
struct common_audit_data *a,
unsigned flags);
@ -127,7 +123,8 @@ int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
* be performed under a lock, to allow the lock to be released
* before calling the auditing code.
*/
static inline int avc_audit(u32 ssid, u32 tsid,
static inline int avc_audit(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd,
int result,
@ -138,31 +135,35 @@ static inline int avc_audit(u32 ssid, u32 tsid,
audited = avc_audit_required(requested, avd, result, 0, &denied);
if (likely(!audited))
return 0;
return slow_avc_audit(ssid, tsid, tclass,
return slow_avc_audit(state, ssid, tsid, tclass,
requested, audited, denied, result,
a, flags);
}
#define AVC_STRICT 1 /* Ignore permissive mode. */
#define AVC_EXTENDED_PERMS 2 /* update extended permissions */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
int avc_has_perm_noaudit(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd);
int avc_has_perm(u32 ssid, u32 tsid,
int avc_has_perm(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct common_audit_data *auditdata);
int avc_has_perm_flags(u32 ssid, u32 tsid,
int avc_has_perm_flags(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct common_audit_data *auditdata,
int flags);
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
u8 driver, u8 perm, struct common_audit_data *ad);
int avc_has_extended_perms(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass, u32 requested,
u8 driver, u8 perm, struct common_audit_data *ad);
u32 avc_policy_seqno(void);
u32 avc_policy_seqno(struct selinux_state *state);
#define AVC_CALLBACK_GRANT 1
#define AVC_CALLBACK_TRY_REVOKE 2
@ -177,8 +178,11 @@ u32 avc_policy_seqno(void);
int avc_add_callback(int (*callback)(u32 event), u32 events);
/* Exported to selinuxfs */
int avc_get_hash_stats(char *page);
extern unsigned int avc_cache_threshold;
struct selinux_avc;
int avc_get_hash_stats(struct selinux_avc *avc, char *page);
unsigned int avc_get_cache_threshold(struct selinux_avc *avc);
void avc_set_cache_threshold(struct selinux_avc *avc,
unsigned int cache_threshold);
/* Attempt to free avc node cache */
void avc_disable(void);

View file

@ -9,7 +9,8 @@
#include "flask.h"
int avc_ss_reset(u32 seqno);
struct selinux_avc;
int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
/* Class/perm mapping support */
struct security_class_mapping {
@ -19,11 +20,5 @@ struct security_class_mapping {
extern struct security_class_mapping secclass_map[];
/*
* The security server must be initialized before
* any labeling or access decisions can be provided.
*/
extern int ss_initialized;
#endif /* _SELINUX_AVC_SS_H_ */

View file

@ -176,7 +176,7 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_CAP2_PERMS, NULL } },
{ "sctp_socket",
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },
"node_bind", "name_connect", "association", NULL } },
{ "icmp_socket",
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },

View file

@ -13,10 +13,15 @@
#ifndef _SELINUX_CONDITIONAL_H_
#define _SELINUX_CONDITIONAL_H_
int security_get_bools(int *len, char ***names, int **values);
#include "security.h"
int security_set_bools(int len, int *values);
int security_get_bools(struct selinux_state *state,
int *len, char ***names, int **values);
int security_get_bool_value(int index);
int security_set_bools(struct selinux_state *state,
int len, int *values);
int security_get_bool_value(struct selinux_state *state,
int index);
#endif

View file

@ -32,6 +32,7 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/request_sock.h>
#include <net/sctp/structs.h>
#include "avc.h"
#include "objsec.h"
@ -52,9 +53,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
u16 family,
u32 sid);
int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb);
int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family);
void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family);
void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk);
int selinux_netlbl_socket_post_create(struct sock *sk, u16 family);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
@ -64,6 +67,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
int selinux_netlbl_socket_connect_locked(struct sock *sk,
struct sockaddr *addr);
#else
static inline void selinux_netlbl_cache_invalidate(void)
@ -113,6 +118,11 @@ static inline int selinux_netlbl_conn_setsid(struct sock *sk,
return 0;
}
static inline int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb)
{
return 0;
}
static inline int selinux_netlbl_inet_conn_request(struct request_sock *req,
u16 family)
{
@ -122,6 +132,11 @@ static inline void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
{
return;
}
static inline void selinux_netlbl_sctp_sk_clone(struct sock *sk,
struct sock *newsk)
{
return;
}
static inline int selinux_netlbl_socket_post_create(struct sock *sk,
u16 family)
{
@ -145,6 +160,11 @@ static inline int selinux_netlbl_socket_connect(struct sock *sk,
{
return 0;
}
static inline int selinux_netlbl_socket_connect_locked(struct sock *sk,
struct sockaddr *addr)
{
return 0;
}
#endif /* CONFIG_NETLABEL */
#endif

View file

@ -130,6 +130,10 @@ struct sk_security_struct {
u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
u16 sclass; /* sock security class */
enum { /* SCTP association state */
SCTP_ASSOC_UNSET = 0,
SCTP_ASSOC_SET,
} sctp_assoc_state;
};
struct tun_security_struct {
@ -154,6 +158,4 @@ struct bpf_security_struct {
u32 sid; /*SID of bpf obj creater*/
};
extern unsigned int selinux_checkreqprot;
#endif /* _SELINUX_OBJSEC_H_ */

View file

@ -13,6 +13,8 @@
#include <linux/dcache.h>
#include <linux/magic.h>
#include <linux/types.h>
#include <linux/refcount.h>
#include <linux/workqueue.h>
#include "flask.h"
#define SECSID_NULL 0x00000000 /* unspecified SID */
@ -81,13 +83,6 @@ enum {
extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
extern int selinux_policycap_netpeer;
extern int selinux_policycap_openperm;
extern int selinux_policycap_extsockclass;
extern int selinux_policycap_alwaysnetwork;
extern int selinux_policycap_cgroupseclabel;
extern int selinux_policycap_nnp_nosuid_transition;
/*
* type_datum properties
* available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY
@ -98,13 +93,98 @@ extern int selinux_policycap_nnp_nosuid_transition;
/* limitation of boundary depth */
#define POLICYDB_BOUNDS_MAXDEPTH 4
int security_mls_enabled(void);
struct selinux_avc;
struct selinux_ss;
int security_load_policy(void *data, size_t len);
int security_read_policy(void **data, size_t *len);
size_t security_policydb_len(void);
struct selinux_state {
bool disabled;
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
bool enforcing;
#endif
bool checkreqprot;
bool initialized;
bool policycap[__POLICYDB_CAPABILITY_MAX];
struct selinux_avc *avc;
struct selinux_ss *ss;
};
int security_policycap_supported(unsigned int req_cap);
void selinux_ss_init(struct selinux_ss **ss);
void selinux_avc_init(struct selinux_avc **avc);
extern struct selinux_state selinux_state;
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
static inline bool enforcing_enabled(struct selinux_state *state)
{
return state->enforcing;
}
static inline void enforcing_set(struct selinux_state *state, bool value)
{
state->enforcing = value;
}
#else
static inline bool enforcing_enabled(struct selinux_state *state)
{
return true;
}
static inline void enforcing_set(struct selinux_state *state, bool value)
{
}
#endif
static inline bool selinux_policycap_netpeer(void)
{
struct selinux_state *state = &selinux_state;
return state->policycap[POLICYDB_CAPABILITY_NETPEER];
}
static inline bool selinux_policycap_openperm(void)
{
struct selinux_state *state = &selinux_state;
return state->policycap[POLICYDB_CAPABILITY_OPENPERM];
}
static inline bool selinux_policycap_extsockclass(void)
{
struct selinux_state *state = &selinux_state;
return state->policycap[POLICYDB_CAPABILITY_EXTSOCKCLASS];
}
static inline bool selinux_policycap_alwaysnetwork(void)
{
struct selinux_state *state = &selinux_state;
return state->policycap[POLICYDB_CAPABILITY_ALWAYSNETWORK];
}
static inline bool selinux_policycap_cgroupseclabel(void)
{
struct selinux_state *state = &selinux_state;
return state->policycap[POLICYDB_CAPABILITY_CGROUPSECLABEL];
}
static inline bool selinux_policycap_nnp_nosuid_transition(void)
{
struct selinux_state *state = &selinux_state;
return state->policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION];
}
int security_mls_enabled(struct selinux_state *state);
int security_load_policy(struct selinux_state *state,
void *data, size_t len);
int security_read_policy(struct selinux_state *state,
void **data, size_t *len);
size_t security_policydb_len(struct selinux_state *state);
int security_policycap_supported(struct selinux_state *state,
unsigned int req_cap);
#define SEL_VEC_MAX 32
struct av_decision {
@ -141,76 +221,100 @@ struct extended_perms {
/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE 0x0001
void security_compute_av(u32 ssid, u32 tsid,
void security_compute_av(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd,
struct extended_perms *xperms);
void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass,
u8 driver, struct extended_perms_decision *xpermd);
void security_compute_xperms_decision(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass,
u8 driver,
struct extended_perms_decision *xpermd);
void security_compute_av_user(u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd);
void security_compute_av_user(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd);
int security_transition_sid(u32 ssid, u32 tsid, u16 tclass,
int security_transition_sid(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass,
const struct qstr *qstr, u32 *out_sid);
int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass,
int security_transition_sid_user(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass,
const char *objname, u32 *out_sid);
int security_member_sid(u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
int security_member_sid(struct selinux_state *state, u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
int security_change_sid(u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
int security_change_sid(struct selinux_state *state, u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
int security_sid_to_context(u32 sid, char **scontext,
u32 *scontext_len);
int security_sid_to_context(struct selinux_state *state, u32 sid,
char **scontext, u32 *scontext_len);
int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);
int security_sid_to_context_force(struct selinux_state *state,
u32 sid, char **scontext, u32 *scontext_len);
int security_context_to_sid(const char *scontext, u32 scontext_len,
int security_context_to_sid(struct selinux_state *state,
const char *scontext, u32 scontext_len,
u32 *out_sid, gfp_t gfp);
int security_context_str_to_sid(const char *scontext, u32 *out_sid, gfp_t gfp);
int security_context_str_to_sid(struct selinux_state *state,
const char *scontext, u32 *out_sid, gfp_t gfp);
int security_context_to_sid_default(const char *scontext, u32 scontext_len,
int security_context_to_sid_default(struct selinux_state *state,
const char *scontext, u32 scontext_len,
u32 *out_sid, u32 def_sid, gfp_t gfp_flags);
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
int security_context_to_sid_force(struct selinux_state *state,
const char *scontext, u32 scontext_len,
u32 *sid);
int security_get_user_sids(u32 callsid, char *username,
int security_get_user_sids(struct selinux_state *state,
u32 callsid, char *username,
u32 **sids, u32 *nel);
int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
int security_port_sid(struct selinux_state *state,
u8 protocol, u16 port, u32 *out_sid);
int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
int security_ib_pkey_sid(struct selinux_state *state,
u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid);
int security_ib_endport_sid(struct selinux_state *state,
const char *dev_name, u8 port_num, u32 *out_sid);
int security_netif_sid(char *name, u32 *if_sid);
int security_netif_sid(struct selinux_state *state,
char *name, u32 *if_sid);
int security_node_sid(u16 domain, void *addr, u32 addrlen,
u32 *out_sid);
int security_node_sid(struct selinux_state *state,
u16 domain, void *addr, u32 addrlen,
u32 *out_sid);
int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
int security_validate_transition(struct selinux_state *state,
u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
int security_validate_transition_user(struct selinux_state *state,
u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
int security_bounded_transition(u32 oldsid, u32 newsid);
int security_bounded_transition(struct selinux_state *state,
u32 oldsid, u32 newsid);
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
int security_sid_mls_copy(struct selinux_state *state,
u32 sid, u32 mls_sid, u32 *new_sid);
int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
int security_net_peersid_resolve(struct selinux_state *state,
u32 nlbl_sid, u32 nlbl_type,
u32 xfrm_sid,
u32 *peer_sid);
int security_get_classes(char ***classes, int *nclasses);
int security_get_permissions(char *class, char ***perms, int *nperms);
int security_get_reject_unknown(void);
int security_get_allow_unknown(void);
int security_get_classes(struct selinux_state *state,
char ***classes, int *nclasses);
int security_get_permissions(struct selinux_state *state,
char *class, char ***perms, int *nperms);
int security_get_reject_unknown(struct selinux_state *state);
int security_get_allow_unknown(struct selinux_state *state);
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
@ -221,27 +325,31 @@ int security_get_allow_unknown(void);
#define SECURITY_FS_USE_NATIVE 7 /* use native label support */
#define SECURITY_FS_USE_MAX 7 /* Highest SECURITY_FS_USE_XXX */
int security_fs_use(struct super_block *sb);
int security_fs_use(struct selinux_state *state, struct super_block *sb);
int security_genfs_sid(const char *fstype, char *name, u16 sclass,
u32 *sid);
int security_genfs_sid(struct selinux_state *state,
const char *fstype, char *name, u16 sclass,
u32 *sid);
#ifdef CONFIG_NETLABEL
int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
int security_netlbl_secattr_to_sid(struct selinux_state *state,
struct netlbl_lsm_secattr *secattr,
u32 *sid);
int security_netlbl_sid_to_secattr(u32 sid,
int security_netlbl_sid_to_secattr(struct selinux_state *state,
u32 sid,
struct netlbl_lsm_secattr *secattr);
#else
static inline int security_netlbl_secattr_to_sid(
static inline int security_netlbl_secattr_to_sid(struct selinux_state *state,
struct netlbl_lsm_secattr *secattr,
u32 *sid)
{
return -EIDRM;
}
static inline int security_netlbl_sid_to_secattr(u32 sid,
struct netlbl_lsm_secattr *secattr)
static inline int security_netlbl_sid_to_secattr(struct selinux_state *state,
u32 sid,
struct netlbl_lsm_secattr *secattr)
{
return -ENOENT;
}
@ -252,7 +360,7 @@ const char *security_get_initial_sid_context(u32 sid);
/*
* status notifier using mmap interface
*/
extern struct page *selinux_kernel_status_page(void);
extern struct page *selinux_kernel_status_page(struct selinux_state *state);
#define SELINUX_KERNEL_STATUS_VERSION 1
struct selinux_kernel_status {
@ -266,10 +374,12 @@ struct selinux_kernel_status {
*/
} __packed;
extern void selinux_status_update_setenforce(int enforcing);
extern void selinux_status_update_policyload(int seqno);
extern void selinux_status_update_setenforce(struct selinux_state *state,
int enforcing);
extern void selinux_status_update_policyload(struct selinux_state *state,
int seqno);
extern void selinux_complete_init(void);
extern int selinux_disable(void);
extern int selinux_disable(struct selinux_state *state);
extern void exit_sel_fs(void);
extern struct path selinux_null;
extern struct vfsmount *selinuxfs_mount;
@ -277,5 +387,8 @@ extern void selnl_notify_setenforce(int val);
extern void selnl_notify_policyload(u32 seqno);
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
#endif /* _SELINUX_SECURITY_H_ */
extern void avtab_cache_init(void);
extern void ebitmap_cache_init(void);
extern void hashtab_cache_init(void);
#endif /* _SELINUX_SECURITY_H_ */

View file

@ -163,7 +163,7 @@ static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
ret = -ENOMEM;
goto out;
}
ret = security_netif_sid(dev->name, &new->nsec.sid);
ret = security_netif_sid(&selinux_state, dev->name, &new->nsec.sid);
if (ret != 0)
goto out;
new->nsec.ns = ns;

View file

@ -59,7 +59,7 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
{
int rc;
rc = security_netlbl_secattr_to_sid(secattr, sid);
rc = security_netlbl_secattr_to_sid(&selinux_state, secattr, sid);
if (rc == 0 &&
(secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
(secattr->flags & NETLBL_SECATTR_CACHE))
@ -90,7 +90,8 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
secattr = netlbl_secattr_alloc(GFP_ATOMIC);
if (secattr == NULL)
return NULL;
rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
rc = security_netlbl_sid_to_secattr(&selinux_state, sksec->sid,
secattr);
if (rc != 0) {
netlbl_secattr_free(secattr);
return NULL;
@ -249,6 +250,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
sk = skb_to_full_sk(skb);
if (sk != NULL) {
struct sk_security_struct *sksec = sk->sk_security;
if (sksec->nlbl_state != NLBL_REQSKB)
return 0;
secattr = selinux_netlbl_sock_getattr(sk, sid);
@ -256,7 +258,8 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
if (secattr == NULL) {
secattr = &secattr_storage;
netlbl_secattr_init(secattr);
rc = security_netlbl_sid_to_secattr(sid, secattr);
rc = security_netlbl_sid_to_secattr(&selinux_state, sid,
secattr);
if (rc != 0)
goto skbuff_setsid_return;
}
@ -269,6 +272,62 @@ skbuff_setsid_return:
return rc;
}
/**
* selinux_netlbl_sctp_assoc_request - Label an incoming sctp association.
* @ep: incoming association endpoint.
* @skb: the packet.
*
* Description:
* A new incoming connection is represented by @ep, ......
* Returns zero on success, negative values on failure.
*
*/
int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
struct sk_buff *skb)
{
int rc;
struct netlbl_lsm_secattr secattr;
struct sk_security_struct *sksec = ep->base.sk->sk_security;
struct sockaddr *addr;
struct sockaddr_in addr4;
#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 addr6;
#endif
if (ep->base.sk->sk_family != PF_INET &&
ep->base.sk->sk_family != PF_INET6)
return 0;
netlbl_secattr_init(&secattr);
rc = security_netlbl_sid_to_secattr(&selinux_state,
ep->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;
addr = (struct sockaddr *)&addr4;
#if IS_ENABLED(CONFIG_IPV6)
} else {
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = ipv6_hdr(skb)->saddr;
addr = (struct sockaddr *)&addr6;
#endif
}
rc = netlbl_conn_setattr(ep->base.sk, addr, &secattr);
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
@ -289,7 +348,8 @@ int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
return 0;
netlbl_secattr_init(&secattr);
rc = security_netlbl_sid_to_secattr(req->secid, &secattr);
rc = security_netlbl_sid_to_secattr(&selinux_state, req->secid,
&secattr);
if (rc != 0)
goto inet_conn_request_return;
rc = netlbl_req_setattr(req, &secattr);
@ -318,6 +378,22 @@ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
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 = sk->sk_security;
struct sk_security_struct *newsksec = newsk->sk_security;
newsksec->nlbl_state = sksec->nlbl_state;
}
/**
* selinux_netlbl_socket_post_create - Label a socket using NetLabel
* @sock: the socket to label
@ -402,7 +478,8 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
perm = RAWIP_SOCKET__RECVFROM;
}
rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
rc = avc_has_perm(&selinux_state,
sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
if (rc == 0)
return 0;
@ -468,6 +545,69 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
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 = sk->sk_security;
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 (secattr == NULL) {
rc = -ENOMEM;
return rc;
}
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 = sk->sk_security;
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
@ -481,34 +621,10 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr *secattr;
if (sksec->nlbl_state != NLBL_REQSKB &&
sksec->nlbl_state != NLBL_CONNLABELED)
return 0;
lock_sock(sk);
/* 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;
goto socket_connect_return;
}
secattr = selinux_netlbl_sock_genattr(sk);
if (secattr == NULL) {
rc = -ENOMEM;
goto socket_connect_return;
}
rc = netlbl_conn_setattr(sk, addr, secattr);
if (rc == 0)
sksec->nlbl_state = NLBL_CONNLABELED;
socket_connect_return:
rc = selinux_netlbl_socket_connect_locked(sk, addr);
release_sock(sk);
return rc;
}

View file

@ -215,12 +215,12 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
goto out;
switch (family) {
case PF_INET:
ret = security_node_sid(PF_INET,
ret = security_node_sid(&selinux_state, PF_INET,
addr, sizeof(struct in_addr), sid);
new->nsec.addr.ipv4 = *(__be32 *)addr;
break;
case PF_INET6:
ret = security_node_sid(PF_INET6,
ret = security_node_sid(&selinux_state, PF_INET6,
addr, sizeof(struct in6_addr), sid);
new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
break;

View file

@ -161,7 +161,7 @@ static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL)
goto out;
ret = security_port_sid(protocol, pnum, sid);
ret = security_port_sid(&selinux_state, protocol, pnum, sid);
if (ret != 0)
goto out;

File diff suppressed because it is too large Load diff

View file

@ -655,7 +655,8 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp)
return rc;
}
void avtab_cache_init(void)
void __init avtab_cache_init(void)
{
avtab_node_cachep = kmem_cache_create("avtab_node",
sizeof(struct avtab_node),
@ -664,9 +665,3 @@ void avtab_cache_init(void)
sizeof(struct avtab_extended_perms),
0, SLAB_PANIC, NULL);
}
void avtab_cache_destroy(void)
{
kmem_cache_destroy(avtab_node_cachep);
kmem_cache_destroy(avtab_xperms_cachep);
}

View file

@ -114,9 +114,6 @@ struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key);
struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
void avtab_cache_init(void);
void avtab_cache_destroy(void);
#define MAX_AVTAB_HASH_BITS 16
#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)

View file

@ -523,14 +523,9 @@ int ebitmap_write(struct ebitmap *e, void *fp)
return 0;
}
void ebitmap_cache_init(void)
void __init ebitmap_cache_init(void)
{
ebitmap_node_cachep = kmem_cache_create("ebitmap_node",
sizeof(struct ebitmap_node),
0, SLAB_PANIC, NULL);
}
void ebitmap_cache_destroy(void)
{
kmem_cache_destroy(ebitmap_node_cachep);
}

View file

@ -131,9 +131,6 @@ void ebitmap_destroy(struct ebitmap *e);
int ebitmap_read(struct ebitmap *e, void *fp);
int ebitmap_write(struct ebitmap *e, void *fp);
void ebitmap_cache_init(void);
void ebitmap_cache_destroy(void);
#ifdef CONFIG_NETLABEL
int ebitmap_netlbl_export(struct ebitmap *ebmap,
struct netlbl_lsm_catmap **catmap);

View file

@ -169,14 +169,10 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
info->slots_used = slots_used;
info->max_chain_len = max_chain_len;
}
void hashtab_cache_init(void)
void __init hashtab_cache_init(void)
{
hashtab_node_cachep = kmem_cache_create("hashtab_node",
sizeof(struct hashtab_node),
0, SLAB_PANIC, NULL);
}
void hashtab_cache_destroy(void)
{
kmem_cache_destroy(hashtab_node_cachep);
}

View file

@ -85,8 +85,4 @@ int hashtab_map(struct hashtab *h,
/* Fill info with some hash table statistics */
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
/* Use kmem_cache for hashtab_node */
void hashtab_cache_init(void);
void hashtab_cache_destroy(void);
#endif /* _SS_HASHTAB_H */

View file

@ -33,20 +33,20 @@
* Return the length in bytes for the MLS fields of the
* security context string representation of `context'.
*/
int mls_compute_context_len(struct context *context)
int mls_compute_context_len(struct policydb *p, struct context *context)
{
int i, l, len, head, prev;
char *nm;
struct ebitmap *e;
struct ebitmap_node *node;
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return 0;
len = 1; /* for the beginning ":" */
for (l = 0; l < 2; l++) {
int index_sens = context->range.level[l].sens;
len += strlen(sym_name(&policydb, SYM_LEVELS, index_sens - 1));
len += strlen(sym_name(p, SYM_LEVELS, index_sens - 1));
/* categories */
head = -2;
@ -56,17 +56,17 @@ int mls_compute_context_len(struct context *context)
if (i - prev > 1) {
/* one or more negative bits are skipped */
if (head != prev) {
nm = sym_name(&policydb, SYM_CATS, prev);
nm = sym_name(p, SYM_CATS, prev);
len += strlen(nm) + 1;
}
nm = sym_name(&policydb, SYM_CATS, i);
nm = sym_name(p, SYM_CATS, i);
len += strlen(nm) + 1;
head = i;
}
prev = i;
}
if (prev != head) {
nm = sym_name(&policydb, SYM_CATS, prev);
nm = sym_name(p, SYM_CATS, prev);
len += strlen(nm) + 1;
}
if (l == 0) {
@ -86,7 +86,8 @@ int mls_compute_context_len(struct context *context)
* the MLS fields of `context' into the string `*scontext'.
* Update `*scontext' to point to the end of the MLS fields.
*/
void mls_sid_to_context(struct context *context,
void mls_sid_to_context(struct policydb *p,
struct context *context,
char **scontext)
{
char *scontextp, *nm;
@ -94,7 +95,7 @@ void mls_sid_to_context(struct context *context,
struct ebitmap *e;
struct ebitmap_node *node;
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return;
scontextp = *scontext;
@ -103,7 +104,7 @@ void mls_sid_to_context(struct context *context,
scontextp++;
for (l = 0; l < 2; l++) {
strcpy(scontextp, sym_name(&policydb, SYM_LEVELS,
strcpy(scontextp, sym_name(p, SYM_LEVELS,
context->range.level[l].sens - 1));
scontextp += strlen(scontextp);
@ -119,7 +120,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = '.';
else
*scontextp++ = ',';
nm = sym_name(&policydb, SYM_CATS, prev);
nm = sym_name(p, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
@ -127,7 +128,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = ':';
else
*scontextp++ = ',';
nm = sym_name(&policydb, SYM_CATS, i);
nm = sym_name(p, SYM_CATS, i);
strcpy(scontextp, nm);
scontextp += strlen(nm);
head = i;
@ -140,7 +141,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = '.';
else
*scontextp++ = ',';
nm = sym_name(&policydb, SYM_CATS, prev);
nm = sym_name(p, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
@ -375,12 +376,13 @@ out:
* the string `str'. This function will allocate temporary memory with the
* given constraints of gfp_mask.
*/
int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
int mls_from_string(struct policydb *p, char *str, struct context *context,
gfp_t gfp_mask)
{
char *tmpstr, *freestr;
int rc;
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return -EINVAL;
/* we need freestr because mls_context_to_sid will change
@ -389,7 +391,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
if (!tmpstr) {
rc = -ENOMEM;
} else {
rc = mls_context_to_sid(&policydb, ':', &tmpstr, context,
rc = mls_context_to_sid(p, ':', &tmpstr, context,
NULL, SECSID_NULL);
kfree(freestr);
}
@ -417,10 +419,11 @@ int mls_range_set(struct context *context,
return rc;
}
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
int mls_setup_user_range(struct policydb *p,
struct context *fromcon, struct user_datum *user,
struct context *usercon)
{
if (policydb.mls_enabled) {
if (p->mls_enabled) {
struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
struct mls_level *user_low = &(user->range.level[0]);
@ -470,7 +473,7 @@ int mls_convert_context(struct policydb *oldp,
struct ebitmap_node *node;
int l, i;
if (!policydb.mls_enabled)
if (!oldp->mls_enabled || !newp->mls_enabled)
return 0;
for (l = 0; l < 2; l++) {
@ -503,7 +506,8 @@ int mls_convert_context(struct policydb *oldp,
return 0;
}
int mls_compute_sid(struct context *scontext,
int mls_compute_sid(struct policydb *p,
struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 specified,
@ -515,7 +519,7 @@ int mls_compute_sid(struct context *scontext,
struct class_datum *cladatum;
int default_range = 0;
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return 0;
switch (specified) {
@ -524,12 +528,12 @@ int mls_compute_sid(struct context *scontext,
rtr.source_type = scontext->type;
rtr.target_type = tcontext->type;
rtr.target_class = tclass;
r = hashtab_search(policydb.range_tr, &rtr);
r = hashtab_search(p->range_tr, &rtr);
if (r)
return mls_range_set(newcontext, r);
if (tclass && tclass <= policydb.p_classes.nprim) {
cladatum = policydb.class_val_to_struct[tclass - 1];
if (tclass && tclass <= p->p_classes.nprim) {
cladatum = p->class_val_to_struct[tclass - 1];
if (cladatum)
default_range = cladatum->default_range;
}
@ -551,7 +555,7 @@ int mls_compute_sid(struct context *scontext,
/* Fallthrough */
case AVTAB_CHANGE:
if ((tclass == policydb.process_class) || (sock == true))
if ((tclass == p->process_class) || (sock == true))
/* Use the process MLS attributes. */
return mls_context_cpy(newcontext, scontext);
else
@ -577,10 +581,11 @@ int mls_compute_sid(struct context *scontext,
* NetLabel MLS sensitivity level field.
*
*/
void mls_export_netlbl_lvl(struct context *context,
void mls_export_netlbl_lvl(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return;
secattr->attr.mls.lvl = context->range.level[0].sens - 1;
@ -597,10 +602,11 @@ void mls_export_netlbl_lvl(struct context *context,
* NetLabel MLS sensitivity level into the context.
*
*/
void mls_import_netlbl_lvl(struct context *context,
void mls_import_netlbl_lvl(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return;
context->range.level[0].sens = secattr->attr.mls.lvl + 1;
@ -617,12 +623,13 @@ void mls_import_netlbl_lvl(struct context *context,
* MLS category field. Returns zero on success, negative values on failure.
*
*/
int mls_export_netlbl_cat(struct context *context,
int mls_export_netlbl_cat(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return 0;
rc = ebitmap_netlbl_export(&context->range.level[0].cat,
@ -645,12 +652,13 @@ int mls_export_netlbl_cat(struct context *context,
* negative values on failure.
*
*/
int mls_import_netlbl_cat(struct context *context,
int mls_import_netlbl_cat(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
if (!policydb.mls_enabled)
if (!p->mls_enabled)
return 0;
rc = ebitmap_netlbl_import(&context->range.level[0].cat,

View file

@ -25,8 +25,9 @@
#include "context.h"
#include "policydb.h"
int mls_compute_context_len(struct context *context);
void mls_sid_to_context(struct context *context, char **scontext);
int mls_compute_context_len(struct policydb *p, struct context *context);
void mls_sid_to_context(struct policydb *p, struct context *context,
char **scontext);
int mls_context_isvalid(struct policydb *p, struct context *c);
int mls_range_isvalid(struct policydb *p, struct mls_range *r);
int mls_level_isvalid(struct policydb *p, struct mls_level *l);
@ -38,7 +39,8 @@ int mls_context_to_sid(struct policydb *p,
struct sidtab *s,
u32 def_sid);
int mls_from_string(char *str, struct context *context, gfp_t gfp_mask);
int mls_from_string(struct policydb *p, char *str, struct context *context,
gfp_t gfp_mask);
int mls_range_set(struct context *context, struct mls_range *range);
@ -46,42 +48,52 @@ int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
struct context *context);
int mls_compute_sid(struct context *scontext,
int mls_compute_sid(struct policydb *p,
struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 specified,
struct context *newcontext,
bool sock);
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
int mls_setup_user_range(struct policydb *p,
struct context *fromcon, struct user_datum *user,
struct context *usercon);
#ifdef CONFIG_NETLABEL
void mls_export_netlbl_lvl(struct context *context,
void mls_export_netlbl_lvl(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr);
void mls_import_netlbl_lvl(struct context *context,
void mls_import_netlbl_lvl(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr);
int mls_export_netlbl_cat(struct context *context,
int mls_export_netlbl_cat(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr);
int mls_import_netlbl_cat(struct context *context,
int mls_import_netlbl_cat(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr);
#else
static inline void mls_export_netlbl_lvl(struct context *context,
static inline void mls_export_netlbl_lvl(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return;
}
static inline void mls_import_netlbl_lvl(struct context *context,
static inline void mls_import_netlbl_lvl(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return;
}
static inline int mls_export_netlbl_cat(struct context *context,
static inline int mls_export_netlbl_cat(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return -ENOMEM;
}
static inline int mls_import_netlbl_cat(struct context *context,
static inline int mls_import_netlbl_cat(struct policydb *p,
struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return -ENOMEM;

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,28 @@
#include "policydb.h"
#include "sidtab.h"
extern struct policydb policydb;
/* Mapping for a single class */
struct selinux_mapping {
u16 value; /* policy value for class */
unsigned int num_perms; /* number of permissions in class */
u32 perms[sizeof(u32) * 8]; /* policy values for permissions */
};
/* Map for all of the classes, with array size */
struct selinux_map {
struct selinux_mapping *mapping; /* indexed by class */
u16 size; /* array size of mapping */
};
struct selinux_ss {
struct sidtab sidtab;
struct policydb policydb;
rwlock_t policy_rwlock;
u32 latest_granting;
struct selinux_map map;
struct page *status_page;
struct mutex status_lock;
};
void services_compute_xperms_drivers(struct extended_perms *xperms,
struct avtab_node *node);
@ -19,4 +40,3 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
struct avtab_node *node);
#endif /* _SS_SERVICES_H_ */

View file

@ -35,8 +35,6 @@
* In most cases, application shall confirm the kernel status is not
* changed without any system call invocations.
*/
static struct page *selinux_status_page;
static DEFINE_MUTEX(selinux_status_lock);
/*
* selinux_kernel_status_page
@ -44,21 +42,21 @@ static DEFINE_MUTEX(selinux_status_lock);
* It returns a reference to selinux_status_page. If the status page is
* not allocated yet, it also tries to allocate it at the first time.
*/
struct page *selinux_kernel_status_page(void)
struct page *selinux_kernel_status_page(struct selinux_state *state)
{
struct selinux_kernel_status *status;
struct page *result = NULL;
mutex_lock(&selinux_status_lock);
if (!selinux_status_page) {
selinux_status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
mutex_lock(&state->ss->status_lock);
if (!state->ss->status_page) {
state->ss->status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
if (selinux_status_page) {
status = page_address(selinux_status_page);
if (state->ss->status_page) {
status = page_address(state->ss->status_page);
status->version = SELINUX_KERNEL_STATUS_VERSION;
status->sequence = 0;
status->enforcing = selinux_enforcing;
status->enforcing = enforcing_enabled(state);
/*
* NOTE: the next policyload event shall set
* a positive value on the status->policyload,
@ -66,11 +64,12 @@ struct page *selinux_kernel_status_page(void)
* So, application can know it was updated.
*/
status->policyload = 0;
status->deny_unknown = !security_get_allow_unknown();
status->deny_unknown =
!security_get_allow_unknown(state);
}
}
result = selinux_status_page;
mutex_unlock(&selinux_status_lock);
result = state->ss->status_page;
mutex_unlock(&state->ss->status_lock);
return result;
}
@ -80,13 +79,14 @@ struct page *selinux_kernel_status_page(void)
*
* It updates status of the current enforcing/permissive mode.
*/
void selinux_status_update_setenforce(int enforcing)
void selinux_status_update_setenforce(struct selinux_state *state,
int enforcing)
{
struct selinux_kernel_status *status;
mutex_lock(&selinux_status_lock);
if (selinux_status_page) {
status = page_address(selinux_status_page);
mutex_lock(&state->ss->status_lock);
if (state->ss->status_page) {
status = page_address(state->ss->status_page);
status->sequence++;
smp_wmb();
@ -96,7 +96,7 @@ void selinux_status_update_setenforce(int enforcing)
smp_wmb();
status->sequence++;
}
mutex_unlock(&selinux_status_lock);
mutex_unlock(&state->ss->status_lock);
}
/*
@ -105,22 +105,23 @@ void selinux_status_update_setenforce(int enforcing)
* It updates status of the times of policy reloaded, and current
* setting of deny_unknown.
*/
void selinux_status_update_policyload(int seqno)
void selinux_status_update_policyload(struct selinux_state *state,
int seqno)
{
struct selinux_kernel_status *status;
mutex_lock(&selinux_status_lock);
if (selinux_status_page) {
status = page_address(selinux_status_page);
mutex_lock(&state->ss->status_lock);
if (state->ss->status_page) {
status = page_address(state->ss->status_page);
status->sequence++;
smp_wmb();
status->policyload = seqno;
status->deny_unknown = !security_get_allow_unknown();
status->deny_unknown = !security_get_allow_unknown(state);
smp_wmb();
status->sequence++;
}
mutex_unlock(&selinux_status_lock);
mutex_unlock(&state->ss->status_lock);
}

View file

@ -101,11 +101,13 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
ctx->ctx_len = str_len;
memcpy(ctx->ctx_str, &uctx[1], str_len);
ctx->ctx_str[str_len] = '\0';
rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid, gfp);
rc = security_context_to_sid(&selinux_state, ctx->ctx_str, str_len,
&ctx->ctx_sid, gfp);
if (rc)
goto err;
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
rc = avc_has_perm(&selinux_state,
tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL);
if (rc)
goto err;
@ -141,7 +143,8 @@ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
if (!ctx)
return 0;
return avc_has_perm(tsec->sid, ctx->ctx_sid,
return avc_has_perm(&selinux_state,
tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
NULL);
}
@ -163,7 +166,8 @@ int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
if (!selinux_authorizable_ctx(ctx))
return -EINVAL;
rc = avc_has_perm(fl_secid, ctx->ctx_sid,
rc = avc_has_perm(&selinux_state,
fl_secid, ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL);
return (rc == -EACCES ? -ESRCH : rc);
}
@ -202,7 +206,8 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
/* We don't need a separate SA Vs. policy polmatch check since the SA
* is now of the same label as the flow and a flow Vs. policy polmatch
* check had already happened in selinux_xfrm_policy_lookup() above. */
return (avc_has_perm(fl->flowi_secid, state_sid,
return (avc_has_perm(&selinux_state,
fl->flowi_secid, state_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO,
NULL) ? 0 : 1);
}
@ -352,7 +357,8 @@ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
if (secid == 0)
return -EINVAL;
rc = security_sid_to_context(secid, &ctx_str, &str_len);
rc = security_sid_to_context(&selinux_state, secid, &ctx_str,
&str_len);
if (rc)
return rc;
@ -420,7 +426,8 @@ int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
/* This check even when there's no association involved is intended,
* according to Trent Jaeger, to make sure a process can't engage in
* non-IPsec communication unless explicitly allowed by policy. */
return avc_has_perm(sk_sid, peer_sid,
return avc_has_perm(&selinux_state,
sk_sid, peer_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);
}
@ -463,6 +470,6 @@ int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
/* This check even when there's no association involved is intended,
* according to Trent Jaeger, to make sure a process can't engage in
* non-IPsec communication unless explicitly allowed by policy. */
return avc_has_perm(sk_sid, SECINITSID_UNLABELED,
return avc_has_perm(&selinux_state, sk_sid, SECINITSID_UNLABELED,
SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);
}