mirror of
https://github.com/a52xq-dev/kernel.git
synced 2026-10-18 21:58:11 +07:00
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:
commit
9eda2d2dca
43 changed files with 2936 additions and 1252 deletions
175
Documentation/security/LSM-sctp.rst
Normal file
175
Documentation/security/LSM-sctp.rst
Normal 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 |
|
||||
------------------------------------------------------------------
|
||||
|
||||
|
||||
158
Documentation/security/SELinux-sctp.rst
Normal file
158
Documentation/security/SELinux-sctp.rst
Normal 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.
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(¬if_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(¬if_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
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue