diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-06 15:39:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-06 15:39:26 -0700 |
commit | 9eda2d2dca830f0f8923b1f377d0fb70f576af1d (patch) | |
tree | 5bdc06d14182e67ebdf20e5543de2a407563662f | |
parent | 6ad11bdd57ad31182850a54235f0e947cbfd5ad0 (diff) | |
parent | 6b6bc6205d98796361962ee282a063f18ba8dc57 (diff) | |
download | linux-9eda2d2dca830f0f8923b1f377d0fb70f576af1d.tar.bz2 |
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
43 files changed, 2936 insertions, 1252 deletions
diff --git a/Documentation/security/LSM-sctp.rst b/Documentation/security/LSM-sctp.rst new file mode 100644 index 000000000000..6e5a3925a860 --- /dev/null +++ b/Documentation/security/LSM-sctp.rst @@ -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 | + ------------------------------------------------------------------ + + diff --git a/Documentation/security/SELinux-sctp.rst b/Documentation/security/SELinux-sctp.rst new file mode 100644 index 000000000000..a332cb1c5334 --- /dev/null +++ b/Documentation/security/SELinux-sctp.rst @@ -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. diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index bde167fa2c51..6e6951b42be5 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -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; diff --git a/include/linux/security.h b/include/linux/security.h index 128e1e4a5346..17ffd1ef6074 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -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 diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 72c5b8fc3232..28b996d63490 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -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) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c63249ea34c3..a0ec462bc1a9 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -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. */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index afd4346386e0..b64d583bf053 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -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 diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 22dc1b9d6362..c070dfc0190a 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -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; diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index f889a84f264d..be296d633e95 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -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 diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 0d873c58e516..6dd976c8ab61 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -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, }; diff --git a/net/sctp/output.c b/net/sctp/output.c index d6e1c90cc09a..690d8557bb7b 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -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; } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index a24cde236330..d685f8456762 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -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, diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index cc20bc39ee7c..5a4fb1dc8400 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -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; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index cc56a67dbb4d..dd0594a10961 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -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; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 7a10ae3c3d82..2a2e094560de 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -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, diff --git a/security/security.c b/security/security.c index 02d734e69955..af53d2834e20 100644 --- a/security/security.c +++ b/security/security.c @@ -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 diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 2380b8d72cec..f3aedf077509 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -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); */ } } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 925e546b5a87..0314fc766134 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -67,6 +67,8 @@ #include <linux/tcp.h> #include <linux/udp.h> #include <linux/dccp.h> +#include <linux/sctp.h> +#include <net/sctp/structs.h> #include <linux/quota.h> #include <linux/un.h> /* for Unix socket types */ #include <net/af_unix.h> /* for Unix socket types */ @@ -98,20 +100,24 @@ #include "audit.h" #include "avc_ss.h" +struct selinux_state selinux_state; + /* SECMARK reference count */ static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); #ifdef CONFIG_SECURITY_SELINUX_DEVELOP -int selinux_enforcing; +static int selinux_enforcing_boot; static int __init enforcing_setup(char *str) { unsigned long enforcing; if (!kstrtoul(str, 0, &enforcing)) - selinux_enforcing = enforcing ? 1 : 0; + selinux_enforcing_boot = enforcing ? 1 : 0; return 1; } __setup("enforcing=", enforcing_setup); +#else +#define selinux_enforcing_boot 1 #endif #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM @@ -129,6 +135,19 @@ __setup("selinux=", selinux_enabled_setup); int selinux_enabled = 1; #endif +static unsigned int selinux_checkreqprot_boot = + CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; + +static int __init checkreqprot_setup(char *str) +{ + unsigned long checkreqprot; + + if (!kstrtoul(str, 0, &checkreqprot)) + selinux_checkreqprot_boot = checkreqprot ? 1 : 0; + return 1; +} +__setup("checkreqprot=", checkreqprot_setup); + static struct kmem_cache *sel_inode_cache; static struct kmem_cache *file_security_cache; @@ -145,7 +164,8 @@ static struct kmem_cache *file_security_cache; */ static int selinux_secmark_enabled(void) { - return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount)); + return (selinux_policycap_alwaysnetwork() || + atomic_read(&selinux_secmark_refcount)); } /** @@ -160,7 +180,8 @@ static int selinux_secmark_enabled(void) */ static int selinux_peerlbl_enabled(void) { - return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled()); + return (selinux_policycap_alwaysnetwork() || + netlbl_enabled() || selinux_xfrm_enabled()); } static int selinux_netcache_avc_callback(u32 event) @@ -264,7 +285,8 @@ static int __inode_security_revalidate(struct inode *inode, might_sleep_if(may_sleep); - if (ss_initialized && isec->initialized != LABEL_INITIALIZED) { + if (selinux_state.initialized && + isec->initialized != LABEL_INITIALIZED) { if (!may_sleep) return -ECHILD; @@ -446,12 +468,14 @@ static int may_context_mount_sb_relabel(u32 sid, const struct task_security_struct *tsec = cred->security; int rc; - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, NULL); if (rc) return rc; - rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + tsec->sid, sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, NULL); return rc; } @@ -462,12 +486,14 @@ static int may_context_mount_inode_relabel(u32 sid, { const struct task_security_struct *tsec = cred->security; int rc; - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, NULL); if (rc) return rc; - rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, NULL); return rc; } @@ -486,7 +512,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) !strcmp(sb->s_type->name, "debugfs") || !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "rootfs") || - (selinux_policycap_cgroupseclabel && + (selinux_policycap_cgroupseclabel() && (!strcmp(sb->s_type->name, "cgroup") || !strcmp(sb->s_type->name, "cgroup2"))); } @@ -586,7 +612,7 @@ static int selinux_get_mnt_opts(const struct super_block *sb, if (!(sbsec->flags & SE_SBINITIALIZED)) return -EINVAL; - if (!ss_initialized) + if (!selinux_state.initialized) return -EINVAL; /* make sure we always check enough bits to cover the mask */ @@ -617,21 +643,25 @@ static int selinux_get_mnt_opts(const struct super_block *sb, i = 0; if (sbsec->flags & FSCONTEXT_MNT) { - rc = security_sid_to_context(sbsec->sid, &context, &len); + rc = security_sid_to_context(&selinux_state, sbsec->sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; opts->mnt_opts_flags[i++] = FSCONTEXT_MNT; } if (sbsec->flags & CONTEXT_MNT) { - rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len); + rc = security_sid_to_context(&selinux_state, + sbsec->mntpoint_sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; opts->mnt_opts_flags[i++] = CONTEXT_MNT; } if (sbsec->flags & DEFCONTEXT_MNT) { - rc = security_sid_to_context(sbsec->def_sid, &context, &len); + rc = security_sid_to_context(&selinux_state, sbsec->def_sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; @@ -641,7 +671,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb, struct dentry *root = sbsec->sb->s_root; struct inode_security_struct *isec = backing_inode_security(root); - rc = security_sid_to_context(isec->sid, &context, &len); + rc = security_sid_to_context(&selinux_state, isec->sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; @@ -704,7 +735,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, mutex_lock(&sbsec->lock); - if (!ss_initialized) { + if (!selinux_state.initialized) { if (!num_opts) { /* Defer initialization until selinux_complete_init, after the initial policy is loaded and the security @@ -750,7 +781,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (flags[i] == SBLABEL_MNT) continue; - rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL); + rc = security_context_str_to_sid(&selinux_state, + mount_options[i], &sid, + GFP_KERNEL); if (rc) { printk(KERN_WARNING "SELinux: security_context_str_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -826,7 +859,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, * Determine the labeling behavior to use for this * filesystem type. */ - rc = security_fs_use(sb); + rc = security_fs_use(&selinux_state, sb); if (rc) { printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", @@ -851,7 +884,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, } if (sbsec->behavior == SECURITY_FS_USE_XATTR) { sbsec->behavior = SECURITY_FS_USE_MNTPOINT; - rc = security_transition_sid(current_sid(), current_sid(), + rc = security_transition_sid(&selinux_state, + current_sid(), + current_sid(), SECCLASS_FILE, NULL, &sbsec->mntpoint_sid); if (rc) @@ -987,7 +1022,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, * if the parent was able to be mounted it clearly had no special lsm * mount options. thus we can safely deal with this superblock later */ - if (!ss_initialized) + if (!selinux_state.initialized) return 0; /* @@ -1014,7 +1049,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, if (newsbsec->behavior == SECURITY_FS_USE_NATIVE && !(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) { - rc = security_fs_use(newsb); + rc = security_fs_use(&selinux_state, newsb); if (rc) goto out; } @@ -1297,7 +1332,7 @@ static inline int default_protocol_dgram(int protocol) static inline u16 socket_type_to_security_class(int family, int type, int protocol) { - int extsockclass = selinux_policycap_extsockclass; + int extsockclass = selinux_policycap_extsockclass(); switch (family) { case PF_UNIX: @@ -1471,7 +1506,8 @@ static int selinux_genfs_get_sid(struct dentry *dentry, path++; } } - rc = security_genfs_sid(sb->s_type->name, path, tclass, sid); + rc = security_genfs_sid(&selinux_state, sb->s_type->name, + path, tclass, sid); } free_page((unsigned long)buffer); return rc; @@ -1589,7 +1625,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent sid = sbsec->def_sid; rc = 0; } else { - rc = security_context_to_sid_default(context, rc, &sid, + rc = security_context_to_sid_default(&selinux_state, + context, rc, &sid, sbsec->def_sid, GFP_NOFS); if (rc) { @@ -1622,7 +1659,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent sid = sbsec->sid; /* Try to obtain a transition SID. */ - rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid); + rc = security_transition_sid(&selinux_state, task_sid, sid, + sclass, NULL, &sid); if (rc) goto out; break; @@ -1740,9 +1778,11 @@ static int cred_has_capability(const struct cred *cred, return -EINVAL; } - rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); + rc = avc_has_perm_noaudit(&selinux_state, + sid, sid, sclass, av, 0, &avd); if (audit == SECURITY_CAP_AUDIT) { - int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0); + int rc2 = avc_audit(&selinux_state, + sid, sid, sclass, av, &avd, rc, &ad, 0); if (rc2) return rc2; } @@ -1768,7 +1808,8 @@ static int inode_has_perm(const struct cred *cred, sid = cred_sid(cred); isec = inode->i_security; - return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp); + return avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, perms, adp); } /* Same as inode_has_perm, but pass explicit audit data containing @@ -1841,7 +1882,8 @@ static int file_has_perm(const struct cred *cred, ad.u.file = file; if (sid != fsec->sid) { - rc = avc_has_perm(sid, fsec->sid, + rc = avc_has_perm(&selinux_state, + sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); @@ -1883,7 +1925,8 @@ selinux_determine_inode_label(const struct task_security_struct *tsec, *_new_isid = tsec->create_sid; } else { const struct inode_security_struct *dsec = inode_security(dir); - return security_transition_sid(tsec->sid, dsec->sid, tclass, + return security_transition_sid(&selinux_state, tsec->sid, + dsec->sid, tclass, name, _new_isid); } @@ -1910,7 +1953,8 @@ static int may_create(struct inode *dir, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; - rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, + rc = avc_has_perm(&selinux_state, + sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, &ad); if (rc) @@ -1921,11 +1965,13 @@ static int may_create(struct inode *dir, if (rc) return rc; - rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad); + rc = avc_has_perm(&selinux_state, + sid, newsid, tclass, FILE__CREATE, &ad); if (rc) return rc; - return avc_has_perm(newsid, sbsec->sid, + return avc_has_perm(&selinux_state, + newsid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, &ad); } @@ -1954,7 +2000,8 @@ static int may_link(struct inode *dir, av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); - rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad); + rc = avc_has_perm(&selinux_state, + sid, dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; @@ -1974,7 +2021,8 @@ static int may_link(struct inode *dir, return 0; } - rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad); + rc = avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, av, &ad); return rc; } @@ -1998,16 +2046,19 @@ static inline int may_rename(struct inode *old_dir, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = old_dentry; - rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, + rc = avc_has_perm(&selinux_state, + sid, old_dsec->sid, SECCLASS_DIR, DIR__REMOVE_NAME | DIR__SEARCH, &ad); if (rc) return rc; - rc = avc_has_perm(sid, old_isec->sid, + rc = avc_has_perm(&selinux_state, + sid, old_isec->sid, old_isec->sclass, FILE__RENAME, &ad); if (rc) return rc; if (old_is_dir && new_dir != old_dir) { - rc = avc_has_perm(sid, old_isec->sid, + rc = avc_has_perm(&selinux_state, + sid, old_isec->sid, old_isec->sclass, DIR__REPARENT, &ad); if (rc) return rc; @@ -2017,13 +2068,15 @@ static inline int may_rename(struct inode *old_dir, av = DIR__ADD_NAME | DIR__SEARCH; if (d_is_positive(new_dentry)) av |= DIR__REMOVE_NAME; - rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad); + rc = avc_has_perm(&selinux_state, + sid, new_dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; if (d_is_positive(new_dentry)) { new_isec = backing_inode_security(new_dentry); new_is_dir = d_is_dir(new_dentry); - rc = avc_has_perm(sid, new_isec->sid, + rc = avc_has_perm(&selinux_state, + sid, new_isec->sid, new_isec->sclass, (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad); if (rc) @@ -2043,7 +2096,8 @@ static int superblock_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); sbsec = sb->s_security; - return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); + return avc_has_perm(&selinux_state, + sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); } /* Convert a Linux mode and permission mask to an access vector. */ @@ -2106,7 +2160,8 @@ static inline u32 open_file_to_av(struct file *file) u32 av = file_to_av(file); struct inode *inode = file_inode(file); - if (selinux_policycap_openperm && inode->i_sb->s_magic != SOCKFS_MAGIC) + if (selinux_policycap_openperm() && + inode->i_sb->s_magic != SOCKFS_MAGIC) av |= FILE__OPEN; return av; @@ -2119,7 +2174,8 @@ static int selinux_binder_set_context_mgr(struct task_struct *mgr) u32 mysid = current_sid(); u32 mgrsid = task_sid(mgr); - return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER, + return avc_has_perm(&selinux_state, + mysid, mgrsid, SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); } @@ -2132,13 +2188,15 @@ static int selinux_binder_transaction(struct task_struct *from, int rc; if (mysid != fromsid) { - rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, + rc = avc_has_perm(&selinux_state, + mysid, fromsid, SECCLASS_BINDER, BINDER__IMPERSONATE, NULL); if (rc) return rc; } - return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, + return avc_has_perm(&selinux_state, + fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, NULL); } @@ -2148,7 +2206,8 @@ static int selinux_binder_transfer_binder(struct task_struct *from, u32 fromsid = task_sid(from); u32 tosid = task_sid(to); - return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, + return avc_has_perm(&selinux_state, + fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, NULL); } @@ -2167,7 +2226,8 @@ static int selinux_binder_transfer_file(struct task_struct *from, ad.u.path = file->f_path; if (sid != fsec->sid) { - rc = avc_has_perm(sid, fsec->sid, + rc = avc_has_perm(&selinux_state, + sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); @@ -2185,7 +2245,8 @@ static int selinux_binder_transfer_file(struct task_struct *from, return 0; isec = backing_inode_security(dentry); - return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), + return avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, file_to_av(file), &ad); } @@ -2196,21 +2257,25 @@ static int selinux_ptrace_access_check(struct task_struct *child, u32 csid = task_sid(child); if (mode & PTRACE_MODE_READ) - return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); + return avc_has_perm(&selinux_state, + sid, csid, SECCLASS_FILE, FILE__READ, NULL); - return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); + return avc_has_perm(&selinux_state, + sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); } static int selinux_ptrace_traceme(struct task_struct *parent) { - return avc_has_perm(task_sid(parent), current_sid(), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + task_sid(parent), current_sid(), SECCLASS_PROCESS, PROCESS__PTRACE, NULL); } static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { - return avc_has_perm(current_sid(), task_sid(target), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(target), SECCLASS_PROCESS, PROCESS__GETCAP, NULL); } @@ -2219,7 +2284,8 @@ static int selinux_capset(struct cred *new, const struct cred *old, const kernel_cap_t *inheritable, const kernel_cap_t *permitted) { - return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + cred_sid(old), cred_sid(new), SECCLASS_PROCESS, PROCESS__SETCAP, NULL); } @@ -2279,18 +2345,21 @@ static int selinux_syslog(int type) switch (type) { case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL); case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ /* Set level of messages printed to console */ case SYSLOG_ACTION_CONSOLE_LEVEL: - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, NULL); } /* All other syslog types */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL); } @@ -2351,13 +2420,14 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, * policy allows the corresponding permission between * the old and new contexts. */ - if (selinux_policycap_nnp_nosuid_transition) { + if (selinux_policycap_nnp_nosuid_transition()) { av = 0; if (nnp) av |= PROCESS2__NNP_TRANSITION; if (nosuid) av |= PROCESS2__NOSUID_TRANSITION; - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS2, av, NULL); if (!rc) return 0; @@ -2368,7 +2438,8 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, * i.e. SIDs that are guaranteed to only be allowed a subset * of the permissions of the current SID. */ - rc = security_bounded_transition(old_tsec->sid, new_tsec->sid); + rc = security_bounded_transition(&selinux_state, old_tsec->sid, + new_tsec->sid); if (!rc) return 0; @@ -2420,8 +2491,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) return rc; } else { /* Check for a default transition on this program. */ - rc = security_transition_sid(old_tsec->sid, isec->sid, - SECCLASS_PROCESS, NULL, + rc = security_transition_sid(&selinux_state, old_tsec->sid, + isec->sid, SECCLASS_PROCESS, NULL, &new_tsec->sid); if (rc) return rc; @@ -2439,25 +2510,29 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) ad.u.file = bprm->file; if (new_tsec->sid == old_tsec->sid) { - rc = avc_has_perm(old_tsec->sid, isec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, isec->sid, SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); if (rc) return rc; } else { /* Check permissions for the transition. */ - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__TRANSITION, &ad); if (rc) return rc; - rc = avc_has_perm(new_tsec->sid, isec->sid, + rc = avc_has_perm(&selinux_state, + new_tsec->sid, isec->sid, SECCLASS_FILE, FILE__ENTRYPOINT, &ad); if (rc) return rc; /* Check for shared state */ if (bprm->unsafe & LSM_UNSAFE_SHARE) { - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__SHARE, NULL); if (rc) @@ -2469,7 +2544,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) if (bprm->unsafe & LSM_UNSAFE_PTRACE) { u32 ptsid = ptrace_parent_sid(); if (ptsid != 0) { - rc = avc_has_perm(ptsid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + ptsid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); if (rc) @@ -2483,7 +2559,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) /* Enable secure mode for SIDs transitions unless the noatsecure permission is granted between the two SIDs, i.e. ahp returns 0. */ - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__NOATSECURE, NULL); bprm->secureexec |= !!rc; @@ -2575,7 +2652,8 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm) * higher than the default soft limit for cases where the default is * lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK. */ - rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__RLIMITINH, NULL); if (rc) { /* protect against do_prlimit() */ @@ -2615,7 +2693,8 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) * This must occur _after_ the task SID has been updated so that any * kill done after the flush will be checked against the new SID. */ - rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); + rc = avc_has_perm(&selinux_state, + osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); if (rc) { if (IS_ENABLED(CONFIG_POSIX_TIMERS)) { memset(&itimer, 0, sizeof itimer); @@ -2779,7 +2858,9 @@ static int selinux_sb_remount(struct super_block *sb, void *data) if (flags[i] == SBLABEL_MNT) continue; - rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL); + rc = security_context_str_to_sid(&selinux_state, + mount_options[i], &sid, + GFP_KERNEL); if (rc) { printk(KERN_WARNING "SELinux: security_context_str_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -2904,7 +2985,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, if (rc) return rc; - return security_sid_to_context(newsid, (char **)ctx, ctxlen); + return security_sid_to_context(&selinux_state, newsid, (char **)ctx, + ctxlen); } static int selinux_dentry_create_files_as(struct dentry *dentry, int mode, @@ -2958,14 +3040,15 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, isec->initialized = LABEL_INITIALIZED; } - if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT)) + if (!selinux_state.initialized || !(sbsec->flags & SBLABEL_MNT)) return -EOPNOTSUPP; if (name) *name = XATTR_SELINUX_SUFFIX; if (value && len) { - rc = security_sid_to_context_force(newsid, &context, &clen); + rc = security_sid_to_context_force(&selinux_state, newsid, + &context, &clen); if (rc) return rc; *value = context; @@ -3040,7 +3123,8 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, if (IS_ERR(isec)) return PTR_ERR(isec); - return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, + return avc_has_perm_flags(&selinux_state, + sid, isec->sid, isec->sclass, FILE__READ, &ad, rcu ? MAY_NOT_BLOCK : 0); } @@ -3056,7 +3140,8 @@ static noinline int audit_inode_permission(struct inode *inode, ad.type = LSM_AUDIT_DATA_INODE; ad.u.inode = inode; - rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, + rc = slow_avc_audit(&selinux_state, + current_sid(), isec->sid, isec->sclass, perms, audited, denied, result, &ad, flags); if (rc) return rc; @@ -3094,7 +3179,8 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (IS_ERR(isec)) return PTR_ERR(isec); - rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); + rc = avc_has_perm_noaudit(&selinux_state, + sid, isec->sid, isec->sclass, perms, 0, &avd); audited = avc_audit_required(perms, &avd, rc, from_access ? FILE__AUDIT_ACCESS : 0, &denied); @@ -3126,7 +3212,7 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) return dentry_has_perm(cred, dentry, FILE__SETATTR); - if (selinux_policycap_openperm && + if (selinux_policycap_openperm() && inode->i_sb->s_magic != SOCKFS_MAGIC && (ia_valid & ATTR_SIZE) && !(ia_valid & ATTR_FILE)) @@ -3183,12 +3269,14 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, ad.u.dentry = dentry; isec = backing_inode_security(dentry); - rc = avc_has_perm(sid, isec->sid, isec->sclass, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, FILE__RELABELFROM, &ad); if (rc) return rc; - rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL); + rc = security_context_to_sid(&selinux_state, value, size, &newsid, + GFP_KERNEL); if (rc == -EINVAL) { if (!has_cap_mac_admin(true)) { struct audit_buffer *ab; @@ -3213,22 +3301,25 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, return rc; } - rc = security_context_to_sid_force(value, size, &newsid); + rc = security_context_to_sid_force(&selinux_state, value, + size, &newsid); } if (rc) return rc; - rc = avc_has_perm(sid, newsid, isec->sclass, + rc = avc_has_perm(&selinux_state, + sid, newsid, isec->sclass, FILE__RELABELTO, &ad); if (rc) return rc; - rc = security_validate_transition(isec->sid, newsid, sid, - isec->sclass); + rc = security_validate_transition(&selinux_state, isec->sid, newsid, + sid, isec->sclass); if (rc) return rc; - return avc_has_perm(newsid, + return avc_has_perm(&selinux_state, + newsid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, @@ -3249,7 +3340,8 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, return; } - rc = security_context_to_sid_force(value, size, &newsid); + rc = security_context_to_sid_force(&selinux_state, value, size, + &newsid); if (rc) { printk(KERN_ERR "SELinux: unable to map context to SID" "for (%s, %lu), rc=%d\n", @@ -3324,10 +3416,12 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void */ isec = inode_security(inode); if (has_cap_mac_admin(false)) - error = security_sid_to_context_force(isec->sid, &context, + error = security_sid_to_context_force(&selinux_state, + isec->sid, &context, &size); else - error = security_sid_to_context(isec->sid, &context, &size); + error = security_sid_to_context(&selinux_state, isec->sid, + &context, &size); if (error) return error; error = size; @@ -3353,7 +3447,8 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, if (!value || !size) return -EACCES; - rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL); + rc = security_context_to_sid(&selinux_state, value, size, &newsid, + GFP_KERNEL); if (rc) return rc; @@ -3442,7 +3537,7 @@ static int selinux_file_permission(struct file *file, int mask) isec = inode_security(inode); if (sid == fsec->sid && fsec->isid == isec->sid && - fsec->pseqno == avc_policy_seqno()) + fsec->pseqno == avc_policy_seqno(&selinux_state)) /* No change since file_open check. */ return 0; @@ -3482,7 +3577,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, ad.u.op->path = file->f_path; if (ssid != fsec->sid) { - rc = avc_has_perm(ssid, fsec->sid, + rc = avc_has_perm(&selinux_state, + ssid, fsec->sid, SECCLASS_FD, FD__USE, &ad); @@ -3494,8 +3590,9 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, return 0; isec = inode_security(inode); - rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, - requested, driver, xperm, &ad); + rc = avc_has_extended_perms(&selinux_state, + ssid, isec->sid, isec->sclass, + requested, driver, xperm, &ad); out: return rc; } @@ -3563,7 +3660,8 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared * private file mapping that will also be writable. * This has an additional check. */ - rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM, NULL); if (rc) goto error; @@ -3593,7 +3691,8 @@ static int selinux_mmap_addr(unsigned long addr) if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { u32 sid = current_sid(); - rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, NULL); } @@ -3615,7 +3714,7 @@ static int selinux_mmap_file(struct file *file, unsigned long reqprot, return rc; } - if (selinux_checkreqprot) + if (selinux_state.checkreqprot) prot = reqprot; return file_map_prot_check(file, prot, @@ -3629,7 +3728,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, const struct cred *cred = current_cred(); u32 sid = cred_sid(cred); - if (selinux_checkreqprot) + if (selinux_state.checkreqprot) prot = reqprot; if (default_noexec && @@ -3637,13 +3736,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, int rc = 0; if (vma->vm_start >= vma->vm_mm->start_brk && vma->vm_end <= vma->vm_mm->brk) { - rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__EXECHEAP, NULL); } else if (!vma->vm_file && ((vma->vm_start <= vma->vm_mm->start_stack && vma->vm_end >= vma->vm_mm->start_stack) || vma_is_stack_for_current(vma))) { - rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__EXECSTACK, NULL); } else if (vma->vm_file && vma->anon_vma) { /* @@ -3735,7 +3836,8 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, else perm = signal_to_av(signum); - return avc_has_perm(fsec->fown_sid, sid, + return avc_has_perm(&selinux_state, + fsec->fown_sid, sid, SECCLASS_PROCESS, perm, NULL); } @@ -3761,7 +3863,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * struct as its SID. */ fsec->isid = isec->sid; - fsec->pseqno = avc_policy_seqno(); + fsec->pseqno = avc_policy_seqno(&selinux_state); /* * Since the inode label or policy seqno may have changed * between the selinux_inode_permission check and the saving @@ -3780,7 +3882,8 @@ static int selinux_task_alloc(struct task_struct *task, { u32 sid = current_sid(); - return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); + return avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); } /* @@ -3854,7 +3957,8 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid) u32 sid = current_sid(); int ret; - ret = avc_has_perm(sid, secid, + ret = avc_has_perm(&selinux_state, + sid, secid, SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__USE_AS_OVERRIDE, NULL); @@ -3878,7 +3982,8 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) u32 sid = current_sid(); int ret; - ret = avc_has_perm(sid, isec->sid, + ret = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__CREATE_FILES_AS, NULL); @@ -3895,7 +4000,8 @@ static int selinux_kernel_module_request(char *kmod_name) ad.type = LSM_AUDIT_DATA_KMOD; ad.u.kmod_name = kmod_name; - return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__MODULE_REQUEST, &ad); } @@ -3909,7 +4015,8 @@ static int selinux_kernel_module_from_file(struct file *file) /* init_module */ if (file == NULL) - return avc_has_perm(sid, sid, SECCLASS_SYSTEM, + return avc_has_perm(&selinux_state, + sid, sid, SECCLASS_SYSTEM, SYSTEM__MODULE_LOAD, NULL); /* finit_module */ @@ -3919,13 +4026,15 @@ static int selinux_kernel_module_from_file(struct file *file) fsec = file->f_security; if (sid != fsec->sid) { - rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); + rc = avc_has_perm(&selinux_state, + sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); if (rc) return rc; } isec = inode_security(file_inode(file)); - return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SYSTEM, SYSTEM__MODULE_LOAD, &ad); } @@ -3947,19 +4056,22 @@ static int selinux_kernel_read_file(struct file *file, static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETPGID, NULL); } static int selinux_task_getpgid(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETPGID, NULL); } static int selinux_task_getsid(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETSESSION, NULL); } @@ -3970,19 +4082,22 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid) static int selinux_task_setnice(struct task_struct *p, int nice) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_setioprio(struct task_struct *p, int ioprio) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getioprio(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } @@ -3997,7 +4112,8 @@ static int selinux_task_prlimit(const struct cred *cred, const struct cred *tcre av |= PROCESS__SETRLIMIT; if (flags & LSM_PRLIMIT_READ) av |= PROCESS__GETRLIMIT; - return avc_has_perm(cred_sid(cred), cred_sid(tcred), + return avc_has_perm(&selinux_state, + cred_sid(cred), cred_sid(tcred), SECCLASS_PROCESS, av, NULL); } @@ -4011,7 +4127,8 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, later be used as a safe reset point for the soft limit upon context transitions. See selinux_bprm_committing_creds. */ if (old_rlim->rlim_max != new_rlim->rlim_max) - return avc_has_perm(current_sid(), task_sid(p), + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL); return 0; @@ -4019,19 +4136,22 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, static int selinux_task_setscheduler(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getscheduler(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } static int selinux_task_movememory(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } @@ -4046,7 +4166,8 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, perm = signal_to_av(sig); if (!secid) secid = current_sid(); - return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); + return avc_has_perm(&selinux_state, + secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); } static void selinux_task_to_inode(struct task_struct *p, @@ -4134,6 +4255,23 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, break; } +#if IS_ENABLED(CONFIG_IP_SCTP) + case IPPROTO_SCTP: { + struct sctphdr _sctph, *sh; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + offset += ihlen; + sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph); + if (sh == NULL) + break; + + ad->u.net->sport = sh->source; + ad->u.net->dport = sh->dest; + break; + } +#endif default: break; } @@ -4207,6 +4345,19 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, break; } +#if IS_ENABLED(CONFIG_IP_SCTP) + case IPPROTO_SCTP: { + struct sctphdr _sctph, *sh; + + sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph); + if (sh == NULL) + break; + + ad->u.net->sport = sh->source; + ad->u.net->dport = sh->dest; + break; + } +#endif /* includes fragments */ default: break; @@ -4287,7 +4438,8 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) if (unlikely(err)) return -EACCES; - err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid); + err = security_net_peersid_resolve(&selinux_state, nlbl_sid, + nlbl_type, xfrm_sid, sid); if (unlikely(err)) { printk(KERN_WARNING "SELinux: failure in selinux_skb_peerlbl_sid()," @@ -4315,7 +4467,8 @@ static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) int err = 0; if (skb_sid != SECSID_NULL) - err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); + err = security_sid_mls_copy(&selinux_state, sk_sid, skb_sid, + conn_sid); else *conn_sid = sk_sid; @@ -4332,8 +4485,8 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec, return 0; } - return security_transition_sid(tsec->sid, tsec->sid, secclass, NULL, - socksid); + return security_transition_sid(&selinux_state, tsec->sid, tsec->sid, + secclass, NULL, socksid); } static int sock_has_perm(struct sock *sk, u32 perms) @@ -4349,7 +4502,8 @@ static int sock_has_perm(struct sock *sk, u32 perms) ad.u.net = &net; ad.u.net->sk = sk; - return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms, + return avc_has_perm(&selinux_state, + current_sid(), sksec->sid, sksec->sclass, perms, &ad); } @@ -4369,7 +4523,8 @@ static int selinux_socket_create(int family, int type, if (rc) return rc; - return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); + return avc_has_perm(&selinux_state, + tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); } static int selinux_socket_post_create(struct socket *sock, int family, @@ -4396,6 +4551,10 @@ static int selinux_socket_post_create(struct socket *sock, int family, sksec = sock->sk->sk_security; sksec->sclass = sclass; sksec->sid = sid; + /* Allows detection of the first association on this socket */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + sksec->sctp_assoc_state = SCTP_ASSOC_UNSET; + err = selinux_netlbl_socket_post_create(sock->sk, family); } @@ -4416,11 +4575,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; - /* - * If PF_INET or PF_INET6, check name_bind permission for the port. - * Multiple address binding for SCTP is not supported yet: we just - * check the first address now. - */ + /* If PF_INET or PF_INET6, check name_bind permission for the port. */ family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { char *addrp; @@ -4432,22 +4587,35 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in unsigned short snum; u32 sid, node_perm; - if (family == PF_INET) { - if (addrlen < sizeof(struct sockaddr_in)) { - err = -EINVAL; - goto out; - } + /* + * sctp_bindx(3) calls via selinux_sctp_bind_connect() + * that validates multiple binding addresses. Because of this + * need to check address->sa_family as it is possible to have + * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. + */ + switch (address->sa_family) { + case AF_INET: + if (addrlen < sizeof(struct sockaddr_in)) + return -EINVAL; addr4 = (struct sockaddr_in *)address; snum = ntohs(addr4->sin_port); addrp = (char *)&addr4->sin_addr.s_addr; - } else { - if (addrlen < SIN6_LEN_RFC2133) { - err = -EINVAL; - goto out; - } + break; + case AF_INET6: + if (addrlen < SIN6_LEN_RFC2133) + return -EINVAL; addr6 = (struct sockaddr_in6 *)address; snum = ntohs(addr6->sin6_port); addrp = (char *)&addr6->sin6_addr.s6_addr; + break; + default: + /* Note that SCTP services expect -EINVAL, whereas + * others expect -EAFNOSUPPORT. + */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + return -EINVAL; + else + return -EAFNOSUPPORT; } if (snum) { @@ -4465,7 +4633,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ad.u.net = &net; ad.u.net->sport = htons(snum); ad.u.net->family = family; - err = avc_has_perm(sksec->sid, sid, + err = avc_has_perm(&selinux_state, + sksec->sid, sid, sksec->sclass, SOCKET__NAME_BIND, &ad); if (err) @@ -4486,6 +4655,10 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in node_perm = DCCP_SOCKET__NODE_BIND; break; + case SECCLASS_SCTP_SOCKET: + node_perm = SCTP_SOCKET__NODE_BIND; + break; + default: node_perm = RAWIP_SOCKET__NODE_BIND; break; @@ -4500,12 +4673,13 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ad.u.net->sport = htons(snum); ad.u.net->family = family; - if (family == PF_INET) + if (address->sa_family == AF_INET) ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; else ad.u.net->v6info.saddr = addr6->sin6_addr; - err = avc_has_perm(sksec->sid, sid, + err = avc_has_perm(&selinux_state, + sksec->sid, sid, sksec->sclass, node_perm, &ad); if (err) goto out; @@ -4514,7 +4688,11 @@ out: return err; } -static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) +/* This supports connect(2) and SCTP connect services such as sctp_connectx(3) + * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.txt + */ +static int selinux_socket_connect_helper(struct socket *sock, + struct sockaddr *address, int addrlen) { struct sock *sk = sock->sk; struct sk_security_struct *sksec = sk->sk_security; @@ -4525,10 +4703,12 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, return err; /* - * If a TCP or DCCP socket, check name_connect permission for the port. + * If a TCP, DCCP or SCTP socket, check name_connect permission + * for the port. */ if (sksec->sclass == SECCLASS_TCP_SOCKET || - sksec->sclass == SECCLASS_DCCP_SOCKET) { + sksec->sclass == SECCLASS_DCCP_SOCKET || + sksec->sclass == SECCLASS_SCTP_SOCKET) { struct common_audit_data ad; struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; @@ -4536,38 +4716,75 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, unsigned short snum; u32 sid, perm; - if (sk->sk_family == PF_INET) { + /* sctp_connectx(3) calls via selinux_sctp_bind_connect() + * that validates multiple connect addresses. Because of this + * need to check address->sa_family as it is possible to have + * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. + */ + switch (address->sa_family) { + case AF_INET: addr4 = (struct sockaddr_in *)address; if (addrlen < sizeof(struct sockaddr_in)) return -EINVAL; snum = ntohs(addr4->sin_port); - } else { + break; + case AF_INET6: addr6 = (struct sockaddr_in6 *)address; if (addrlen < SIN6_LEN_RFC2133) return -EINVAL; snum = ntohs(addr6->sin6_port); + break; + default: + /* Note that SCTP services expect -EINVAL, whereas + * others expect -EAFNOSUPPORT. + */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + return -EINVAL; + else + return -EAFNOSUPPORT; } err = sel_netport_sid(sk->sk_protocol, snum, &sid); if (err) - goto out; + return err; - perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? - TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; + switch (sksec->sclass) { + case SECCLASS_TCP_SOCKET: + perm = TCP_SOCKET__NAME_CONNECT; + break; + case SECCLASS_DCCP_SOCKET: + perm = DCCP_SOCKET__NAME_CONNECT; + break; + case SECCLASS_SCTP_SOCKET: + perm = SCTP_SOCKET__NAME_CONNECT; + break; + } ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; ad.u.net->dport = htons(snum); ad.u.net->family = sk->sk_family; - err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); + err = avc_has_perm(&selinux_state, + sksec->sid, sid, sksec->sclass, perm, &ad); if (err) - goto out; + return err; } - err = selinux_netlbl_socket_connect(sk, address); + return 0; +} -out: - return err; +/* Supports connect(2), see comments in selinux_socket_connect_helper() */ +static int selinux_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + int err; + struct sock *sk = sock->sk; + + err = selinux_socket_connect_helper(sock, address, addrlen); + if (err) + return err; + + return selinux_netlbl_socket_connect(sk, address); } static int selinux_socket_listen(struct socket *sock, int backlog) @@ -4660,7 +4877,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, ad.u.net = &net; ad.u.net->sk = other; - err = avc_has_perm(sksec_sock->sid, sksec_other->sid, + err = avc_has_perm(&selinux_state, + sksec_sock->sid, sksec_other->sid, sksec_other->sclass, UNIX_STREAM_SOCKET__CONNECTTO, &ad); if (err) @@ -4668,8 +4886,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, /* server child socket */ sksec_new->peer_sid = sksec_sock->sid; - err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid, - &sksec_new->sid); + err = security_sid_mls_copy(&selinux_state, sksec_other->sid, + sksec_sock->sid, &sksec_new->sid); if (err) return err; @@ -4691,7 +4909,8 @@ static int selinux_socket_unix_may_send(struct socket *sock, ad.u.net = &net; ad.u.net->sk = other->sk; - return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, + return avc_has_perm(&selinux_state, + ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, &ad); } @@ -4706,7 +4925,8 @@ static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, err = sel_netif_sid(ns, ifindex, &if_sid); if (err) return err; - err = avc_has_perm(peer_sid, if_sid, + err = avc_has_perm(&selinux_state, + peer_sid, if_sid, SECCLASS_NETIF, NETIF__INGRESS, ad); if (err) return err; @@ -4714,7 +4934,8 @@ static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, err = sel_netnode_sid(addrp, family, &node_sid); if (err) return err; - return avc_has_perm(peer_sid, node_sid, + return avc_has_perm(&selinux_state, + peer_sid, node_sid, SECCLASS_NODE, NODE__RECVFROM, ad); } @@ -4737,7 +4958,8 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, return err; if (selinux_secmark_enabled()) { - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + err = avc_has_perm(&selinux_state, + sk_sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); if (err) return err; @@ -4774,7 +4996,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * to the selinux_sock_rcv_skb_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ - if (!selinux_policycap_netpeer) + if (!selinux_policycap_netpeer()) return selinux_sock_rcv_skb_compat(sk, skb, family); secmark_active = selinux_secmark_enabled(); @@ -4802,7 +5024,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) selinux_netlbl_err(skb, family, err, 0); return err; } - err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, + err = avc_has_perm(&selinux_state, + sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); if (err) { selinux_netlbl_err(skb, family, err, 0); @@ -4811,7 +5034,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } if (secmark_active) { - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + err = avc_has_perm(&selinux_state, + sk_sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); if (err) return err; @@ -4830,12 +5054,14 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op u32 peer_sid = SECSID_NULL; if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || - sksec->sclass == SECCLASS_TCP_SOCKET) + sksec->sclass == SECCLASS_TCP_SOCKET || + sksec->sclass == SECCLASS_SCTP_SOCKET) peer_sid = sksec->peer_sid; if (peer_sid == SECSID_NULL) return -ENOPROTOOPT; - err = security_sid_to_context(peer_sid, &scontext, &scontext_len); + err = security_sid_to_context(&selinux_state, peer_sid, &scontext, + &scontext_len); if (err) return err; @@ -4943,6 +5169,172 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) sksec->sclass = isec->sclass; } +/* Called whenever SCTP receives an INIT chunk. This happens when an incoming + * connect(2), sctp_connectx(3) or sctp_sendmsg(3) (with no association + * already present). + */ +static int selinux_sctp_assoc_request(struct sctp_endpoint *ep, + struct sk_buff *skb) +{ + struct sk_security_struct *sksec = ep->base.sk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + u8 peerlbl_active; + u32 peer_sid = SECINITSID_UNLABELED; + u32 conn_sid; + int err = 0; + + if (!selinux_policycap_extsockclass()) + return 0; + + peerlbl_active = selinux_peerlbl_enabled(); + + if (peerlbl_active) { + /* This will return peer_sid = SECSID_NULL if there are + * no peer labels, see security_net_peersid_resolve(). + */ + err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family, + &peer_sid); + if (err) + return err; + + if (peer_sid == SECSID_NULL) + peer_sid = SECINITSID_UNLABELED; + } + + if (sksec->sctp_assoc_state == SCTP_ASSOC_UNSET) { + sksec->sctp_assoc_state = SCTP_ASSOC_SET; + + /* Here as first association on socket. As the peer SID + * was allowed by peer recv (and the netif/node checks), + * then it is approved by policy and used as the primary + * peer SID for getpeercon(3). + */ + sksec->peer_sid = peer_sid; + } else if (sksec->peer_sid != peer_sid) { + /* Other association peer SIDs are checked to enforce + * consistency among the peer SIDs. + */ + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sk = ep->base.sk; + err = avc_has_perm(&selinux_state, + sksec->peer_sid, peer_sid, sksec->sclass, + SCTP_SOCKET__ASSOCIATION, &ad); + if (err) + return err; + } + + /* Compute the MLS component for the connection and store + * the information in ep. This will be used by SCTP TCP type + * sockets and peeled off connections as they cause a new + * socket to be generated. selinux_sctp_sk_clone() will then + * plug this into the new socket. + */ + err = selinux_conn_sid(sksec->sid, peer_sid, &conn_sid); + if (err) + return err; + + ep->secid = conn_sid; + ep->peer_secid = peer_sid; + + /* Set any NetLabel labels including CIPSO/CALIPSO options. */ + return selinux_netlbl_sctp_assoc_request(ep, skb); +} + +/* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting + * based on their @optname. + */ +static int selinux_sctp_bind_connect(struct sock *sk, int optname, + struct sockaddr *address, + int addrlen) +{ + int len, err = 0, walk_size = 0; + void *addr_buf; + struct sockaddr *addr; + struct socket *sock; + + if (!selinux_policycap_extsockclass()) + return 0; + + /* Process one or more addresses that may be IPv4 or IPv6 */ + sock = sk->sk_socket; + addr_buf = address; + + while (walk_size < addrlen) { + addr = addr_buf; + switch (addr->sa_family) { + case AF_INET: + len = sizeof(struct sockaddr_in); + break; + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; + default: + return -EAFNOSUPPORT; + } + + err = -EINVAL; + switch (optname) { + /* Bind checks */ + case SCTP_PRIMARY_ADDR: + case SCTP_SET_PEER_PRIMARY_ADDR: + case SCTP_SOCKOPT_BINDX_ADD: + err = selinux_socket_bind(sock, addr, len); + break; + /* Connect checks */ + case SCTP_SOCKOPT_CONNECTX: + case SCTP_PARAM_SET_PRIMARY: + case SCTP_PARAM_ADD_IP: + case SCTP_SENDMSG_CONNECT: + err = selinux_socket_connect_helper(sock, addr, len); + if (err) + return err; + + /* As selinux_sctp_bind_connect() is called by the + * SCTP protocol layer, the socket is already locked, + * therefore selinux_netlbl_socket_connect_locked() is + * is called here. The situations handled are: + * sctp_connectx(3), sctp_sendmsg(3), sendmsg(2), + * whenever a new IP address is added or when a new + * primary address is selected. + * Note that an SCTP connect(2) call happens before + * the SCTP protocol layer and is handled via + * selinux_socket_connect(). + */ + err = selinux_netlbl_socket_connect_locked(sk, addr); + break; + } + + if (err) + return err; + + addr_buf += len; + walk_size += len; + } + + return 0; +} + +/* Called whenever a new socket is created by accept(2) or sctp_peeloff(3). */ +static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk, + struct sock *newsk) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct sk_security_struct *newsksec = newsk->sk_security; + + /* If policy does not support SECCLASS_SCTP_SOCKET then call + * the non-sctp clone version. + */ + if (!selinux_policycap_extsockclass()) + return selinux_sk_clone_security(sk, newsk); + + newsksec->sid = ep->secid; + newsksec->peer_sid = ep->peer_secid; + newsksec->sclass = sksec->sclass; + selinux_netlbl_sctp_sk_clone(sk, newsk); +} + static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { @@ -5001,7 +5393,9 @@ static int selinux_secmark_relabel_packet(u32 sid) __tsec = current_security(); tsid = __tsec->sid; - return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, NULL); + return avc_has_perm(&selinux_state, + tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, + NULL); } static void selinux_secmark_refcount_inc(void) @@ -5049,7 +5443,8 @@ static int selinux_tun_dev_create(void) * connections unlike traditional sockets - check the TUN driver to * get a better understanding of why this socket is special */ - return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE, + return avc_has_perm(&selinux_state, + sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE, NULL); } @@ -5057,7 +5452,8 @@ static int selinux_tun_dev_attach_queue(void *security) { struct tun_security_struct *tunsec = security; - return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET, + return avc_has_perm(&selinux_state, + current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__ATTACH_QUEUE, NULL); } @@ -5085,11 +5481,13 @@ static int selinux_tun_dev_open(void *security) u32 sid = current_sid(); int err; - err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET, + err = avc_has_perm(&selinux_state, + sid, tunsec->sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__RELABELFROM, NULL); if (err) return err; - err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, + err = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__RELABELTO, NULL); if (err) return err; @@ -5120,7 +5518,8 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) sk->sk_protocol, nlh->nlmsg_type, secclass_map[sksec->sclass - 1].name, task_pid_nr(current), current->comm); - if (!selinux_enforcing || security_get_allow_unknown()) + if (!enforcing_enabled(&selinux_state) || + security_get_allow_unknown(&selinux_state)) err = 0; } @@ -5150,7 +5549,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, u8 netlbl_active; u8 peerlbl_active; - if (!selinux_policycap_netpeer) + if (!selinux_policycap_netpeer()) return NF_ACCEPT; secmark_active = selinux_secmark_enabled(); @@ -5179,7 +5578,8 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, } if (secmark_active) - if (avc_has_perm(peer_sid, skb->secmark, + if (avc_has_perm(&selinux_state, + peer_sid, skb->secmark, SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) return NF_DROP; @@ -5291,7 +5691,8 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_DROP; if (selinux_secmark_enabled()) - if (avc_has_perm(sksec->sid, skb->secmark, + if (avc_has_perm(&selinux_state, + sksec->sid, skb->secmark, SECCLASS_PACKET, PACKET__SEND, &ad)) return NF_DROP_ERR(-ECONNREFUSED); @@ -5319,7 +5720,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, * to the selinux_ip_postroute_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ - if (!selinux_policycap_netpeer) + if (!selinux_policycap_netpeer()) return selinux_ip_postroute_compat(skb, ifindex, family); secmark_active = selinux_secmark_enabled(); @@ -5414,7 +5815,8 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, return NF_DROP; if (secmark_active) - if (avc_has_perm(peer_sid, skb->secmark, + if (avc_has_perm(&selinux_state, + peer_sid, skb->secmark, SECCLASS_PACKET, secmark_perm, &ad)) return NF_DROP_ERR(-ECONNREFUSED); @@ -5424,13 +5826,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid)) return NF_DROP; - if (avc_has_perm(peer_sid, if_sid, + if (avc_has_perm(&selinux_state, + peer_sid, if_sid, SECCLASS_NETIF, NETIF__EGRESS, &ad)) return NF_DROP_ERR(-ECONNREFUSED); if (sel_netnode_sid(addrp, family, &node_sid)) return NF_DROP; - if (avc_has_perm(peer_sid, node_sid, + if (avc_has_perm(&selinux_state, + peer_sid, node_sid, SECCLASS_NODE, NODE__SENDTO, &ad)) return NF_DROP_ERR(-ECONNREFUSED); } @@ -5518,7 +5922,8 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = ipc_perms->key; - return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); + return avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, perms, &ad); } static int selinux_msg_msg_alloc_security(struct msg_msg *msg) @@ -5548,7 +5953,8 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; - rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__CREATE, &ad); if (rc) { ipc_free_security(msq); @@ -5573,7 +5979,8 @@ static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; - return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__ASSOCIATE, &ad); } @@ -5586,7 +5993,8 @@ static int selinux_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd) case IPC_INFO: case MSG_INFO: /* No specific object, just general system-wide information. */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case MSG_STAT: @@ -5625,8 +6033,8 @@ static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *m * Compute new sid based on current process and * message queue this message will be stored in */ - rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG, - NULL, &msec->sid); + rc = security_transition_sid(&selinux_state, sid, isec->sid, + SECCLASS_MSG, NULL, &msec->sid); if (rc) return rc; } @@ -5635,15 +6043,18 @@ static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *m ad.u.ipc_id = msq->key; /* Can this process write to the queue? */ - rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__WRITE, &ad); if (!rc) /* Can this process send the message */ - rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG, + rc = avc_has_perm(&selinux_state, + sid, msec->sid, SECCLASS_MSG, MSG__SEND, &ad); if (!rc) /* Can the message be put in the queue? */ - rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ, + rc = avc_has_perm(&selinux_state, + msec->sid, isec->sid, SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad); return rc; @@ -5665,10 +6076,12 @@ static int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *m ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; - rc = avc_has_perm(sid, isec->sid, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__READ, &ad); if (!rc) - rc = avc_has_perm(sid, msec->sid, + rc = avc_has_perm(&selinux_state, + sid, msec->sid, SECCLASS_MSG, MSG__RECEIVE, &ad); return rc; } @@ -5690,7 +6103,8 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->key; - rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SHM, SHM__CREATE, &ad); if (rc) { ipc_free_security(shp); @@ -5715,7 +6129,8 @@ static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->key; - return avc_has_perm(sid, isec->sid, SECCLASS_SHM, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SHM, SHM__ASSOCIATE, &ad); } @@ -5729,7 +6144,8 @@ static int selinux_shm_shmctl(struct kern_ipc_perm *shp, int cmd) case IPC_INFO: case SHM_INFO: /* No specific object, just general system-wide information. */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case SHM_STAT: @@ -5783,7 +6199,8 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->key; - rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SEM, SEM__CREATE, &ad); if (rc) { ipc_free_security(sma); @@ -5808,7 +6225,8 @@ static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->key; - return avc_has_perm(sid, isec->sid, SECCLASS_SEM, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SEM, SEM__ASSOCIATE, &ad); } @@ -5822,7 +6240,8 @@ static int selinux_sem_semctl(struct kern_ipc_perm *sma, int cmd) case IPC_INFO: case SEM_INFO: /* No specific object, just general system-wide information. */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case GETPID: case GETNCNT: @@ -5908,7 +6327,8 @@ static int selinux_getprocattr(struct task_struct *p, __tsec = __task_cred(p)->security; if (current != p) { - error = avc_has_perm(current_sid(), __tsec->sid, + error = avc_has_perm(&selinux_state, + current_sid(), __tsec->sid, SECCLASS_PROCESS, PROCESS__GETATTR, NULL); if (error) goto bad; @@ -5935,7 +6355,7 @@ static int selinux_getprocattr(struct task_struct *p, if (!sid) return 0; - error = security_sid_to_context(sid, value, &len); + error = security_sid_to_context(&selinux_state, sid, value, &len); if (error) return error; return len; @@ -5957,19 +6377,24 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) * Basic control over ability to set these attributes at all. */ if (!strcmp(name, "exec")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETEXEC, NULL); else if (!strcmp(name, "fscreate")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETFSCREATE, NULL); else if (!strcmp(name, "keycreate")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETKEYCREATE, NULL); else if (!strcmp(name, "sockcreate")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETSOCKCREATE, NULL); else if (!strcmp(name, "current")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETCURRENT, NULL); else error = -EINVAL; @@ -5982,7 +6407,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) str[size-1] = 0; size--; } - error = security_context_to_sid(value, size, &sid, GFP_KERNEL); + error = security_context_to_sid(&selinux_state, value, size, + &sid, GFP_KERNEL); if (error == -EINVAL && !strcmp(name, "fscreate")) { if (!has_cap_mac_admin(true)) { struct audit_buffer *ab; @@ -6001,8 +6427,9 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) return error; } - error = security_context_to_sid_force(value, size, - &sid); + error = security_context_to_sid_force( + &selinux_state, + value, size, &sid); } if (error) return error; @@ -6024,7 +6451,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) } else if (!strcmp(name, "fscreate")) { tsec->create_sid = sid; } else if (!strcmp(name, "keycreate")) { - error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE, + error = avc_has_perm(&selinux_state, + mysid, sid, SECCLASS_KEY, KEY__CREATE, NULL); if (error) goto abort_change; @@ -6039,13 +6467,15 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) /* Only allow single threaded processes to change context */ error = -EPERM; if (!current_is_single_threaded()) { - error = security_bounded_transition(tsec->sid, sid); + error = security_bounded_transition(&selinux_state, + tsec->sid, sid); if (error) goto abort_change; } /* Check permissions for the transition. */ - error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + tsec->sid, sid, SECCLASS_PROCESS, PROCESS__DYNTRANSITION, NULL); if (error) goto abort_change; @@ -6054,7 +6484,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) Otherwise, leave SID unchanged and fail. */ ptsid = ptrace_parent_sid(); if (ptsid != 0) { - error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + ptsid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); if (error) goto abort_change; @@ -6081,12 +6512,14 @@ static int selinux_ismaclabel(const char *name) static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { - return security_sid_to_context(secid, secdata, seclen); + return security_sid_to_context(&selinux_state, secid, + secdata, seclen); } static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) { - return security_context_to_sid(secdata, seclen, secid, GFP_KERNEL); + return security_context_to_sid(&selinux_state, secdata, seclen, + secid, GFP_KERNEL); } static void selinux_release_secctx(char *secdata, u32 seclen) @@ -6178,7 +6611,8 @@ static int selinux_key_permission(key_ref_t key_ref, key = key_ref_to_ptr(key_ref); ksec = key->security; - return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL); + return avc_has_perm(&selinux_state, + sid, ksec->sid, SECCLASS_KEY, perm, NULL); } static int selinux_key_getsecurity(struct key *key, char **_buffer) @@ -6188,7 +6622,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) unsigned len; int rc; - rc = security_sid_to_context(ksec->sid, &context, &len); + rc = security_sid_to_context(&selinux_state, ksec->sid, + &context, &len); if (!rc) rc = len; *_buffer = context; @@ -6213,7 +6648,8 @@ static int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val) ibpkey.subnet_prefix = subnet_prefix; ibpkey.pkey = pkey_val; ad.u.ibpkey = &ibpkey; - return avc_has_perm(sec->sid, sid, + return avc_has_perm(&selinux_state, + sec->sid, sid, SECCLASS_INFINIBAND_PKEY, INFINIBAND_PKEY__ACCESS, &ad); } @@ -6227,7 +6663,8 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, struct ib_security_struct *sec = ib_sec; struct lsm_ibendport_audit ibendport; - err = security_ib_endport_sid(dev_name, port_num, &sid); + err = security_ib_endport_sid(&selinux_state, dev_name, port_num, + &sid); if (err) return err; @@ -6236,7 +6673,8 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, strncpy(ibendport.dev_name, dev_name, sizeof(ibendport.dev_name)); ibendport.port = port_num; ad.u.ibendport = &ibendport; - return avc_has_perm(sec->sid, sid, + return avc_has_perm(&selinux_state, + sec->sid, sid, SECCLASS_INFINIBAND_ENDPORT, INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad); } @@ -6269,11 +6707,13 @@ static int selinux_bpf(int cmd, union bpf_attr *attr, switch (cmd) { case BPF_MAP_CREATE: - ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE, + ret = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL); break; case BPF_PROG_LOAD: - ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__PROG_LOAD, + ret = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL); break; default: @@ -6313,14 +6753,16 @@ static int bpf_fd_pass(struct file *file, u32 sid) if (file->f_op == &bpf_map_fops) { map = file->private_data; bpfsec = map->security; - ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + ret = avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, bpf_map_fmode_to_av(file->f_mode), NULL); if (ret) return ret; } else if (file->f_op == &bpf_prog_fops) { prog = file->private_data; bpfsec = prog->aux->security; - ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + ret = avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_RUN, NULL); if (ret) return ret; @@ -6334,7 +6776,8 @@ static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode) struct bpf_security_struct *bpfsec; bpfsec = map->security; - return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + return avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, bpf_map_fmode_to_av(fmode), NULL); } @@ -6344,7 +6787,8 @@ static int selinux_bpf_prog(struct bpf_prog *prog) struct bpf_security_struct *bpfsec; bpfsec = prog->aux->security; - return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + return avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_RUN, NULL); } @@ -6563,6 +7007,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sk_clone_security, selinux_sk_clone_security), LSM_HOOK_INIT(sk_getsecid, selinux_sk_getsecid), LSM_HOOK_INIT(sock_graft, selinux_sock_graft), + LSM_HOOK_INIT(sctp_assoc_request, selinux_sctp_assoc_request), + LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone), + LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect), LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request), LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone), LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established), @@ -6638,6 +7085,12 @@ static __init int selinux_init(void) printk(KERN_INFO "SELinux: Initializing.\n"); + memset(&selinux_state, 0, sizeof(selinux_state)); + enforcing_set(&selinux_state, selinux_enforcing_boot); + selinux_state.checkreqprot = selinux_checkreqprot_boot; + selinux_ss_init(&selinux_state.ss); + selinux_avc_init(&selinux_state.avc); + /* Set the security state for the initial task. */ cred_init_security(); @@ -6651,6 +7104,12 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); + avtab_cache_init(); + + ebitmap_cache_init(); + + hashtab_cache_init(); + security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) @@ -6659,7 +7118,7 @@ static __init int selinux_init(void) if (avc_add_callback(selinux_lsm_notifier_avc_callback, AVC_CALLBACK_RESET)) panic("SELinux: Unable to register AVC LSM notifier callback\n"); - if (selinux_enforcing) + if (selinux_enforcing_boot) printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n"); else printk(KERN_DEBUG "SELinux: Starting in permissive mode\n"); @@ -6780,23 +7239,22 @@ static void selinux_nf_ip_exit(void) #endif /* CONFIG_NETFILTER */ #ifdef CONFIG_SECURITY_SELINUX_DISABLE -static int selinux_disabled; - -int selinux_disable(void) +int selinux_disable(struct selinux_state *state) { - if (ss_initialized) { + if (state->initialized) { /* Not permitted after initial policy load. */ return -EINVAL; } - if (selinux_disabled) { + if (state->disabled) { /* Only do this once. */ return -EINVAL; } + state->disabled = 1; + printk(KERN_INFO "SELinux: Disabled at runtime.\n"); - selinux_disabled = 1; selinux_enabled = 0; security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c index e3614ee5f1c0..0a4b89d48297 100644 --- a/security/selinux/ibpkey.c +++ b/security/selinux/ibpkey.c @@ -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; diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 57d61cf36500..ef899bcfd2cb 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -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); diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h index 3bcc72769b87..88c384c5c09e 100644 --- a/security/selinux/include/avc_ss.h +++ b/security/selinux/include/avc_ss.h @@ -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_ */ diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index acdee7795297..7f0372426494 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -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 } }, diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h index ff4fddca9050..0e30eca02c48 100644 --- a/security/selinux/include/conditional.h +++ b/security/selinux/include/conditional.h @@ -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 diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index e77a5e307955..8671de09c363 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -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 diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 3d54468ce334..cc5e26b0161b 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -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_ */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 02f0412d42f2..23e762d529fa 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -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; + +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; +}; + +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]; +} -int security_load_policy(void *data, size_t len); -int security_read_policy(void **data, size_t *len); -size_t security_policydb_len(void); +static inline bool selinux_policycap_extsockclass(void) +{ + struct selinux_state *state = &selinux_state; + + return state->policycap[POLICYDB_CAPABILITY_EXTSOCKCLASS]; +} -int security_policycap_supported(unsigned int req_cap); +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_ */ diff --git a/security/selinux/netif.c b/security/selinux/netif.c index e607b4473ef6..ac65f7417413 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -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; diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 2c297b995b16..186e727b737b 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -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; } @@ -270,6 +273,62 @@ skbuff_setsid_return: } /** + * 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); @@ -319,6 +379,22 @@ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family) } /** + * 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 * @family: protocol family @@ -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; @@ -469,7 +546,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, } /** - * selinux_netlbl_socket_connect - Label a client-side socket on connect + * selinux_netlbl_socket_connect_helper - Help label a client-side socket on + * connect * @sk: the socket to label * @addr: the destination address * @@ -478,18 +556,13 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, * Returns zero values on success, negative values on failure. * */ -int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) +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; - 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 */ @@ -497,18 +570,61 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) netlbl_sock_delattr(sk); sksec->nlbl_state = NLBL_REQSKB; rc = 0; - goto socket_connect_return; + return rc; } secattr = selinux_netlbl_sock_genattr(sk); if (secattr == NULL) { rc = -ENOMEM; - goto socket_connect_return; + return rc; } rc = netlbl_conn_setattr(sk, addr, secattr); if (rc == 0) sksec->nlbl_state = NLBL_CONNLABELED; -socket_connect_return: + 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 + * @addr: the destination address + * + * Description: + * Attempt to label a connected socket with NetLabel using the given address. + * Returns zero values on success, negative values on failure. + * + */ +int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) +{ + int rc; + + lock_sock(sk); + rc = selinux_netlbl_socket_connect_locked(sk, addr); release_sock(sk); + return rc; } diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index da923f89d2a9..6dd89b89bc1f 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -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; diff --git a/security/selinux/netport.c b/security/selinux/netport.c index 3311cc393cb4..9ed4c5064a5e 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -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; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 00eed842c491..4be683eeba01 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/fs.h> +#include <linux/mount.h> #include <linux/mutex.h> #include <linux/init.h> #include <linux/string.h> @@ -41,34 +42,6 @@ #include "objsec.h" #include "conditional.h" -unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; - -static int __init checkreqprot_setup(char *str) -{ - unsigned long checkreqprot; - if (!kstrtoul(str, 0, &checkreqprot)) - selinux_checkreqprot = checkreqprot ? 1 : 0; - return 1; -} -__setup("checkreqprot=", checkreqprot_setup); - -static DEFINE_MUTEX(sel_mutex); - -/* global data for booleans */ -static struct dentry *bool_dir; -static int bool_num; -static char **bool_pending_names; -static int *bool_pending_values; - -/* global data for classes */ -static struct dentry *class_dir; -static unsigned long last_class_ino; - -static char policy_opened; - -/* global data for policy capabilities */ -static struct dentry *policycap_dir; - enum sel_inos { SEL_ROOT_INO = 2, SEL_LOAD, /* load policy */ @@ -93,7 +66,51 @@ enum sel_inos { SEL_INO_NEXT, /* The next inode number to use */ }; -static unsigned long sel_last_ino = SEL_INO_NEXT - 1; +struct selinux_fs_info { + struct dentry *bool_dir; + unsigned int bool_num; + char **bool_pending_names; + unsigned int *bool_pending_values; + struct dentry *class_dir; + unsigned long last_class_ino; + bool policy_opened; + struct dentry *policycap_dir; + struct mutex mutex; + unsigned long last_ino; + struct selinux_state *state; + struct super_block *sb; +}; + +static int selinux_fs_info_create(struct super_block *sb) +{ + struct selinux_fs_info *fsi; + + fsi = kzalloc(sizeof(*fsi), GFP_KERNEL); + if (!fsi) + return -ENOMEM; + + mutex_init(&fsi->mutex); + fsi->last_ino = SEL_INO_NEXT - 1; + fsi->state = &selinux_state; + fsi->sb = sb; + sb->s_fs_info = fsi; + return 0; +} + +static void selinux_fs_info_free(struct super_block *sb) +{ + struct selinux_fs_info *fsi = sb->s_fs_info; + int i; + + if (fsi) { + for (i = 0; i < fsi->bool_num; i++) + kfree(fsi->bool_pending_names[i]); + kfree(fsi->bool_pending_names); + kfree(fsi->bool_pending_values); + } + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; +} #define SEL_INITCON_INO_OFFSET 0x01000000 #define SEL_BOOL_INO_OFFSET 0x02000000 @@ -105,10 +122,12 @@ static unsigned long sel_last_ino = SEL_INO_NEXT - 1; static ssize_t sel_read_enforce(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; char tmpbuf[TMPBUFLEN]; ssize_t length; - length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing); + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", + enforcing_enabled(fsi->state)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -117,9 +136,11 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *page = NULL; ssize_t length; - int new_value; + int old_value, new_value; if (count >= PAGE_SIZE) return -ENOMEM; @@ -138,23 +159,25 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, new_value = !!new_value; - if (new_value != selinux_enforcing) { - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + old_value = enforcing_enabled(state); + if (new_value != old_value) { + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETENFORCE, NULL); if (length) goto out; audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, "enforcing=%d old_enforcing=%d auid=%u ses=%u", - new_value, selinux_enforcing, + new_value, old_value, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); - selinux_enforcing = new_value; - if (selinux_enforcing) - avc_ss_reset(0); - selnl_notify_setenforce(selinux_enforcing); - selinux_status_update_setenforce(selinux_enforcing); - if (!selinux_enforcing) + enforcing_set(state, new_value); + if (new_value) + avc_ss_reset(state->avc, 0); + selnl_notify_setenforce(new_value); + selinux_status_update_setenforce(state, new_value); + if (!new_value) call_lsm_notifier(LSM_POLICY_CHANGE, NULL); } length = count; @@ -175,11 +198,14 @@ static const struct file_operations sel_enforce_ops = { static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char tmpbuf[TMPBUFLEN]; ssize_t length; ino_t ino = file_inode(filp)->i_ino; int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ? - security_get_reject_unknown() : !security_get_allow_unknown(); + security_get_reject_unknown(state) : + !security_get_allow_unknown(state); length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); @@ -192,7 +218,8 @@ static const struct file_operations sel_handle_unknown_ops = { static int sel_open_handle_status(struct inode *inode, struct file *filp) { - struct page *status = selinux_kernel_status_page(); + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; + struct page *status = selinux_kernel_status_page(fsi->state); if (!status) return -ENOMEM; @@ -248,6 +275,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; char *page; ssize_t length; int new_value; @@ -268,7 +296,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, goto out; if (new_value) { - length = selinux_disable(); + length = selinux_disable(fsi->state); if (length) goto out; audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, @@ -307,9 +335,9 @@ static const struct file_operations sel_policyvers_ops = { }; /* declaration for sel_write_load */ -static int sel_make_bools(void); -static int sel_make_classes(void); -static int sel_make_policycap(void); +static int sel_make_bools(struct selinux_fs_info *fsi); +static int sel_make_classes(struct selinux_fs_info *fsi); +static int sel_make_policycap(struct selinux_fs_info *fsi); /* declaration for sel_make_class_dirs */ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, @@ -318,11 +346,12 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, static ssize_t sel_read_mls(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; char tmpbuf[TMPBUFLEN]; ssize_t length; length = scnprintf(tmpbuf, TMPBUFLEN, "%d", - security_mls_enabled()); + security_mls_enabled(fsi->state)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -338,20 +367,23 @@ struct policy_load_memory { static int sel_open_policy(struct inode *inode, struct file *filp) { + struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; struct policy_load_memory *plm = NULL; int rc; BUG_ON(filp->private_data); - mutex_lock(&sel_mutex); + mutex_lock(&fsi->mutex); - rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, + rc = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); if (rc) goto err; rc = -EBUSY; - if (policy_opened) + if (fsi->policy_opened) goto err; rc = -ENOMEM; @@ -359,25 +391,25 @@ static int sel_open_policy(struct inode *inode, struct file *filp) if (!plm) goto err; - if (i_size_read(inode) != security_policydb_len()) { + if (i_size_read(inode) != security_policydb_len(state)) { inode_lock(inode); - i_size_write(inode, security_policydb_len()); + i_size_write(inode, security_policydb_len(state)); inode_unlock(inode); } - rc = security_read_policy(&plm->data, &plm->len); + rc = security_read_policy(state, &plm->data, &plm->len); if (rc) goto err; - policy_opened = 1; + fsi->policy_opened = 1; filp->private_data = plm; - mutex_unlock(&sel_mutex); + mutex_unlock(&fsi->mutex); return 0; err: - mutex_unlock(&sel_mutex); + mutex_unlock(&fsi->mutex); if (plm) vfree(plm->data); @@ -387,11 +419,12 @@ err: static int sel_release_policy(struct inode *inode, struct file *filp) { + struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; struct policy_load_memory *plm = filp->private_data; BUG_ON(!plm); - policy_opened = 0; + fsi->policy_opened = 0; vfree(plm->data); kfree(plm); @@ -402,19 +435,21 @@ static int sel_release_policy(struct inode *inode, struct file *filp) static ssize_t sel_read_policy(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; struct policy_load_memory *plm = filp->private_data; int ret; - mutex_lock(&sel_mutex); + mutex_lock(&fsi->mutex); - ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, + ret = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); if (ret) goto out; ret = simple_read_from_buffer(buf, count, ppos, plm->data, plm->len); out: - mutex_unlock(&sel_mutex); + mutex_unlock(&fsi->mutex); return ret; } @@ -468,16 +503,43 @@ static const struct file_operations sel_policy_ops = { .llseek = generic_file_llseek, }; +static int sel_make_policy_nodes(struct selinux_fs_info *fsi) +{ + int ret; + + ret = sel_make_bools(fsi); + if (ret) { + pr_err("SELinux: failed to load policy booleans\n"); + return ret; + } + + ret = sel_make_classes(fsi); + if (ret) { + pr_err("SELinux: failed to load policy classes\n"); + return ret; + } + + ret = sel_make_policycap(fsi); + if (ret) { + pr_err("SELinux: failed to load policy capabilities\n"); + return ret; + } + + return 0; +} + static ssize_t sel_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; ssize_t length; void *data = NULL; - mutex_lock(&sel_mutex); + mutex_lock(&fsi->mutex); - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL); if (length) goto out; @@ -500,29 +562,15 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, if (copy_from_user(data, buf, count) != 0) goto out; - length = security_load_policy(data, count); + length = security_load_policy(fsi->state, data, count); if (length) { pr_warn_ratelimited("SELinux: failed to load policy\n"); goto out; } - length = sel_make_bools(); - if (length) { - pr_err("SELinux: failed to load policy booleans\n"); - goto out1; - } - - length = sel_make_classes(); - if (length) { - pr_err("SELinux: failed to load policy classes\n"); - goto out1; - } - - length = sel_make_policycap(); - if (length) { - pr_err("SELinux: failed to load policy capabilities\n"); + length = sel_make_policy_nodes(fsi); + if (length) goto out1; - } length = count; @@ -532,7 +580,7 @@ out1: from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); out: - mutex_unlock(&sel_mutex); + mutex_unlock(&fsi->mutex); vfree(data); return length; } @@ -544,20 +592,23 @@ static const struct file_operations sel_load_ops = { static ssize_t sel_write_context(struct file *file, char *buf, size_t size) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *canon = NULL; u32 sid, len; ssize_t length; - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL); if (length) goto out; - length = security_context_to_sid(buf, size, &sid, GFP_KERNEL); + length = security_context_to_sid(state, buf, size, &sid, GFP_KERNEL); if (length) goto out; - length = security_sid_to_context(sid, &canon, &len); + length = security_sid_to_context(state, sid, &canon, &len); if (length) goto out; @@ -578,21 +629,24 @@ out: static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; char tmpbuf[TMPBUFLEN]; ssize_t length; - length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot); + length = scnprintf(tmpbuf, TMPBUFLEN, "%u", fsi->state->checkreqprot); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; char *page; ssize_t length; unsigned int new_value; - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, NULL); if (length) @@ -613,7 +667,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, if (sscanf(page, "%u", &new_value) != 1) goto out; - selinux_checkreqprot = new_value ? 1 : 0; + fsi->state->checkreqprot = new_value ? 1 : 0; length = count; out: kfree(page); @@ -629,13 +683,16 @@ static ssize_t sel_write_validatetrans(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *oldcon = NULL, *newcon = NULL, *taskcon = NULL; char *req = NULL; u32 osid, nsid, tsid; u16 tclass; int rc; - rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, + rc = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL); if (rc) goto out; @@ -673,19 +730,19 @@ static ssize_t sel_write_validatetrans(struct file *file, if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4) goto out; - rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL); + rc = security_context_str_to_sid(state, oldcon, &osid, GFP_KERNEL); if (rc) goto out; - rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL); + rc = security_context_str_to_sid(state, newcon, &nsid, GFP_KERNEL); if (rc) goto out; - rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL); + rc = security_context_str_to_sid(state, taskcon, &tsid, GFP_KERNEL); if (rc) goto out; - rc = security_validate_transition_user(osid, nsid, tsid, tclass); + rc = security_validate_transition_user(state, osid, nsid, tsid, tclass); if (!rc) rc = count; out: @@ -755,13 +812,16 @@ static const struct file_operations transaction_ops = { static ssize_t sel_write_access(struct file *file, char *buf, size_t size) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; u32 ssid, tsid; u16 tclass; struct av_decision avd; ssize_t length; - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL); if (length) goto out; @@ -780,15 +840,15 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); if (length) goto out; - security_compute_av_user(ssid, tsid, tclass, &avd); + security_compute_av_user(state, ssid, tsid, tclass, &avd); length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%x %x %x %x %u %x", @@ -803,6 +863,8 @@ out: static ssize_t sel_write_create(struct file *file, char *buf, size_t size) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; char *namebuf = NULL, *objname = NULL; u32 ssid, tsid, newsid; @@ -812,7 +874,8 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) u32 len; int nargs; - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, NULL); if (length) @@ -868,20 +931,20 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) objname = namebuf; } - length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); if (length) goto out; - length = security_transition_sid_user(ssid, tsid, tclass, + length = security_transition_sid_user(state, ssid, tsid, tclass, objname, &newsid); if (length) goto out; - length = security_sid_to_context(newsid, &newcon, &len); + length = security_sid_to_context(state, newsid, &newcon, &len); if (length) goto out; @@ -904,6 +967,8 @@ out: static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; u32 ssid, tsid, newsid; u16 tclass; @@ -911,7 +976,8 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) char *newcon = NULL; u32 len; - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, NULL); if (length) @@ -931,19 +997,19 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); if (length) goto out; - length = security_change_sid(ssid, tsid, tclass, &newsid); + length = security_change_sid(state, ssid, tsid, tclass, &newsid); if (length) goto out; - length = security_sid_to_context(newsid, &newcon, &len); + length = security_sid_to_context(state, newsid, &newcon, &len); if (length) goto out; @@ -962,6 +1028,8 @@ out: static ssize_t sel_write_user(struct file *file, char *buf, size_t size) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *con = NULL, *user = NULL, *ptr; u32 sid, *sids = NULL; ssize_t length; @@ -969,7 +1037,8 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) int i, rc; u32 len, nsids; - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_USER, NULL); if (length) @@ -989,18 +1058,18 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s", con, user) != 2) goto out; - length = security_context_str_to_sid(con, &sid, GFP_KERNEL); + length = security_context_str_to_sid(state, con, &sid, GFP_KERNEL); if (length) goto out; - length = security_get_user_sids(sid, user, &sids, &nsids); + length = security_get_user_sids(state, sid, user, &sids, &nsids); if (length) goto out; length = sprintf(buf, "%u", nsids) + 1; ptr = buf + length; for (i = 0; i < nsids; i++) { - rc = security_sid_to_context(sids[i], &newcon, &len); + rc = security_sid_to_context(state, sids[i], &newcon, &len); if (rc) { length = rc; goto out; @@ -1024,6 +1093,8 @@ out: static ssize_t sel_write_member(struct file *file, char *buf, size_t size) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *scon = NULL, *tcon = NULL; u32 ssid, tsid, newsid; u16 tclass; @@ -1031,7 +1102,8 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) char *newcon = NULL; u32 len; - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, NULL); if (length) @@ -1051,19 +1123,19 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); + length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); + length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); if (length) goto out; - length = security_member_sid(ssid, tsid, tclass, &newsid); + length = security_member_sid(state, ssid, tsid, tclass, &newsid); if (length) goto out; - length = security_sid_to_context(newsid, &newcon, &len); + length = security_sid_to_context(state, newsid, &newcon, &len); if (length) goto out; @@ -1097,6 +1169,7 @@ static struct inode *sel_make_inode(struct super_block *sb, int mode) static ssize_t sel_read_bool(struct file *filep, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; char *page = NULL; ssize_t length; ssize_t ret; @@ -1104,10 +1177,11 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; const char *name = filep->f_path.dentry->d_name.name; - mutex_lock(&sel_mutex); + mutex_lock(&fsi->mutex); ret = -EINVAL; - if (index >= bool_num || strcmp(name, bool_pending_names[index])) + if (index >= fsi->bool_num || strcmp(name, + fsi->bool_pending_names[index])) goto out; ret = -ENOMEM; @@ -1115,16 +1189,16 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf, if (!page) goto out; - cur_enforcing = security_get_bool_value(index); + cur_enforcing = security_get_bool_value(fsi->state, index); if (cur_enforcing < 0) { ret = cur_enforcing; goto out; } length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, - bool_pending_values[index]); + fsi->bool_pending_values[index]); ret = simple_read_from_buffer(buf, count, ppos, page, length); out: - mutex_unlock(&sel_mutex); + mutex_unlock(&fsi->mutex); free_page((unsigned long)page); return ret; } @@ -1132,22 +1206,25 @@ out: static ssize_t sel_write_bool(struct file *filep, const char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; char *page = NULL; ssize_t length; int new_value; unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; const char *name = filep->f_path.dentry->d_name.name; - mutex_lock(&sel_mutex); + mutex_lock(&fsi->mutex); - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETBOOL, NULL); if (length) goto out; length = -EINVAL; - if (index >= bool_num || strcmp(name, bool_pending_names[index])) + if (index >= fsi->bool_num || strcmp(name, + fsi->bool_pending_names[index])) goto out; length = -ENOMEM; @@ -1173,11 +1250,11 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, if (new_value) new_value = 1; - bool_pending_values[index] = new_value; + fsi->bool_pending_values[index] = new_value; length = count; out: - mutex_unlock(&sel_mutex); + mutex_unlock(&fsi->mutex); kfree(page); return length; } @@ -1192,13 +1269,15 @@ static ssize_t sel_commit_bools_write(struct file *filep, const char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; char *page = NULL; ssize_t length; int new_value; - mutex_lock(&sel_mutex); + mutex_lock(&fsi->mutex); - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, + length = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETBOOL, NULL); if (length) @@ -1225,14 +1304,15 @@ static ssize_t sel_commit_bools_write(struct file *filep, goto out; length = 0; - if (new_value && bool_pending_values) - length = security_set_bools(bool_num, bool_pending_values); + if (new_value && fsi->bool_pending_values) + length = security_set_bools(fsi->state, fsi->bool_num, + fsi->bool_pending_values); if (!length) length = count; out: - mutex_unlock(&sel_mutex); + mutex_unlock(&fsi->mutex); kfree(page); return length; } @@ -1250,12 +1330,12 @@ static void sel_remove_entries(struct dentry *de) #define BOOL_DIR_NAME "booleans" -static int sel_make_bools(void) +static int sel_make_bools(struct selinux_fs_info *fsi) { int i, ret; ssize_t len; struct dentry *dentry = NULL; - struct dentry *dir = bool_dir; + struct dentry *dir = fsi->bool_dir; struct inode *inode = NULL; struct inode_security_struct *isec; char **names = NULL, *page; @@ -1264,13 +1344,13 @@ static int sel_make_bools(void) u32 sid; /* remove any existing files */ - for (i = 0; i < bool_num; i++) - kfree(bool_pending_names[i]); - kfree(bool_pending_names); - kfree(bool_pending_values); - bool_num = 0; - bool_pending_names = NULL; - bool_pending_values = NULL; + for (i = 0; i < fsi->bool_num; i++) + kfree(fsi->bool_pending_names[i]); + kfree(fsi->bool_pending_names); + kfree(fsi->bool_pending_values); + fsi->bool_num = 0; + fsi->bool_pending_names = NULL; + fsi->bool_pending_values = NULL; sel_remove_entries(dir); @@ -1279,7 +1359,7 @@ static int sel_make_bools(void) if (!page) goto out; - ret = security_get_bools(&num, &names, &values); + ret = security_get_bools(fsi->state, &num, &names, &values); if (ret) goto out; @@ -1300,7 +1380,8 @@ static int sel_make_bools(void) goto out; isec = (struct inode_security_struct *)inode->i_security; - ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); + ret = security_genfs_sid(fsi->state, "selinuxfs", page, + SECCLASS_FILE, &sid); if (ret) { pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n", page); @@ -1313,9 +1394,9 @@ static int sel_make_bools(void) inode->i_ino = i|SEL_BOOL_INO_OFFSET; d_add(dentry, inode); } - bool_num = num; - bool_pending_names = names; - bool_pending_values = values; + fsi->bool_num = num; + fsi->bool_pending_names = names; + fsi->bool_pending_values = values; free_page((unsigned long)page); return 0; @@ -1333,17 +1414,16 @@ out: return ret; } -#define NULL_FILE_NAME "null" - -struct path selinux_null; - static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char tmpbuf[TMPBUFLEN]; ssize_t length; - length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold); + length = scnprintf(tmpbuf, TMPBUFLEN, "%u", + avc_get_cache_threshold(state->avc)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -1352,11 +1432,14 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *page; ssize_t ret; unsigned int new_value; - ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, + ret = avc_has_perm(&selinux_state, + current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__SETSECPARAM, NULL); if (ret) @@ -1377,7 +1460,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, if (sscanf(page, "%u", &new_value) != 1) goto out; - avc_cache_threshold = new_value; + avc_set_cache_threshold(state->avc, new_value); ret = count; out: @@ -1388,6 +1471,8 @@ out: static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; + struct selinux_state *state = fsi->state; char *page; ssize_t length; @@ -1395,7 +1480,7 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, if (!page) return -ENOMEM; - length = avc_get_hash_stats(page); + length = avc_get_hash_stats(state->avc, page); if (length >= 0) length = simple_read_from_buffer(buf, count, ppos, page, length); free_page((unsigned long)page); @@ -1486,6 +1571,8 @@ static const struct file_operations sel_avc_cache_stats_ops = { static int sel_make_avc_files(struct dentry *dir) { + struct super_block *sb = dir->d_sb; + struct selinux_fs_info *fsi = sb->s_fs_info; int i; static const struct tree_descr files[] = { { "cache_threshold", @@ -1509,7 +1596,7 @@ static int sel_make_avc_files(struct dentry *dir) return -ENOMEM; inode->i_fop = files[i].ops; - inode->i_ino = ++sel_last_ino; + inode->i_ino = ++fsi->last_ino; d_add(dentry, inode); } @@ -1519,12 +1606,13 @@ static int sel_make_avc_files(struct dentry *dir) static ssize_t sel_read_initcon(struct file *file, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; char *con; u32 sid, len; ssize_t ret; sid = file_inode(file)->i_ino&SEL_INO_MASK; - ret = security_sid_to_context(sid, &con, &len); + ret = security_sid_to_context(fsi->state, sid, &con, &len); if (ret) return ret; @@ -1612,12 +1700,13 @@ static const struct file_operations sel_perm_ops = { static ssize_t sel_read_policycap(struct file *file, char __user *buf, size_t count, loff_t *ppos) { + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; int value; char tmpbuf[TMPBUFLEN]; ssize_t length; unsigned long i_ino = file_inode(file)->i_ino; - value = security_policycap_supported(i_ino & SEL_INO_MASK); + value = security_policycap_supported(fsi->state, i_ino & SEL_INO_MASK); length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); @@ -1631,10 +1720,11 @@ static const struct file_operations sel_policycap_ops = { static int sel_make_perm_files(char *objclass, int classvalue, struct dentry *dir) { + struct selinux_fs_info *fsi = dir->d_sb->s_fs_info; int i, rc, nperms; char **perms; - rc = security_get_permissions(objclass, &perms, &nperms); + rc = security_get_permissions(fsi->state, objclass, &perms, &nperms); if (rc) return rc; @@ -1668,6 +1758,8 @@ out: static int sel_make_class_dir_entries(char *classname, int index, struct dentry *dir) { + struct super_block *sb = dir->d_sb; + struct selinux_fs_info *fsi = sb->s_fs_info; struct dentry *dentry = NULL; struct inode *inode = NULL; int rc; @@ -1684,7 +1776,7 @@ static int sel_make_class_dir_entries(char *classname, int index, inode->i_ino = sel_class_to_ino(index); d_add(dentry, inode); - dentry = sel_make_dir(dir, "perms", &last_class_ino); + dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino); if (IS_ERR(dentry)) return PTR_ERR(dentry); @@ -1693,26 +1785,27 @@ static int sel_make_class_dir_entries(char *classname, int index, return rc; } -static int sel_make_classes(void) +static int sel_make_classes(struct selinux_fs_info *fsi) { + int rc, nclasses, i; char **classes; /* delete any existing entries */ - sel_remove_entries(class_dir); + sel_remove_entries(fsi->class_dir); - rc = security_get_classes(&classes, &nclasses); + rc = security_get_classes(fsi->state, &classes, &nclasses); if (rc) return rc; /* +2 since classes are 1-indexed */ - last_class_ino = sel_class_to_ino(nclasses + 2); + fsi->last_class_ino = sel_class_to_ino(nclasses + 2); for (i = 0; i < nclasses; i++) { struct dentry *class_name_dir; - class_name_dir = sel_make_dir(class_dir, classes[i], - &last_class_ino); + class_name_dir = sel_make_dir(fsi->class_dir, classes[i], + &fsi->last_class_ino); if (IS_ERR(class_name_dir)) { rc = PTR_ERR(class_name_dir); goto out; @@ -1732,25 +1825,25 @@ out: return rc; } -static int sel_make_policycap(void) +static int sel_make_policycap(struct selinux_fs_info *fsi) { unsigned int iter; struct dentry *dentry = NULL; struct inode *inode = NULL; - sel_remove_entries(policycap_dir); + sel_remove_entries(fsi->policycap_dir); for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { if (iter < ARRAY_SIZE(selinux_policycap_names)) - dentry = d_alloc_name(policycap_dir, + dentry = d_alloc_name(fsi->policycap_dir, selinux_policycap_names[iter]); else - dentry = d_alloc_name(policycap_dir, "unknown"); + dentry = d_alloc_name(fsi->policycap_dir, "unknown"); if (dentry == NULL) return -ENOMEM; - inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO); + inode = sel_make_inode(fsi->sb, S_IFREG | 0444); if (inode == NULL) return -ENOMEM; @@ -1789,8 +1882,11 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, return dentry; } +#define NULL_FILE_NAME "null" + static int sel_fill_super(struct super_block *sb, void *data, int silent) { + struct selinux_fs_info *fsi; int ret; struct dentry *dentry; struct inode *inode; @@ -1818,14 +1914,20 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) S_IWUGO}, /* last one */ {""} }; + + ret = selinux_fs_info_create(sb); + if (ret) + goto err; + ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); if (ret) goto err; - bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &sel_last_ino); - if (IS_ERR(bool_dir)) { - ret = PTR_ERR(bool_dir); - bool_dir = NULL; + fsi = sb->s_fs_info; + fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino); + if (IS_ERR(fsi->bool_dir)) { + ret = PTR_ERR(fsi->bool_dir); + fsi->bool_dir = NULL; goto err; } @@ -1839,7 +1941,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) if (!inode) goto err; - inode->i_ino = ++sel_last_ino; + inode->i_ino = ++fsi->last_ino; isec = (struct inode_security_struct *)inode->i_security; isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; @@ -1847,9 +1949,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); d_add(dentry, inode); - selinux_null.dentry = dentry; - dentry = sel_make_dir(sb->s_root, "avc", &sel_last_ino); + dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino); if (IS_ERR(dentry)) { ret = PTR_ERR(dentry); goto err; @@ -1859,7 +1960,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) if (ret) goto err; - dentry = sel_make_dir(sb->s_root, "initial_contexts", &sel_last_ino); + dentry = sel_make_dir(sb->s_root, "initial_contexts", &fsi->last_ino); if (IS_ERR(dentry)) { ret = PTR_ERR(dentry); goto err; @@ -1869,23 +1970,31 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) if (ret) goto err; - class_dir = sel_make_dir(sb->s_root, "class", &sel_last_ino); - if (IS_ERR(class_dir)) { - ret = PTR_ERR(class_dir); - class_dir = NULL; + fsi->class_dir = sel_make_dir(sb->s_root, "class", &fsi->last_ino); + if (IS_ERR(fsi->class_dir)) { + ret = PTR_ERR(fsi->class_dir); + fsi->class_dir = NULL; goto err; } - policycap_dir = sel_make_dir(sb->s_root, "policy_capabilities", &sel_last_ino); - if (IS_ERR(policycap_dir)) { - ret = PTR_ERR(policycap_dir); - policycap_dir = NULL; + fsi->policycap_dir = sel_make_dir(sb->s_root, "policy_capabilities", + &fsi->last_ino); + if (IS_ERR(fsi->policycap_dir)) { + ret = PTR_ERR(fsi->policycap_dir); + fsi->policycap_dir = NULL; goto err; } + + ret = sel_make_policy_nodes(fsi); + if (ret) + goto err; return 0; err: printk(KERN_ERR "SELinux: %s: failed while creating inodes\n", __func__); + + selinux_fs_info_free(sb); + return ret; } @@ -1895,16 +2004,25 @@ static struct dentry *sel_mount(struct file_system_type *fs_type, return mount_single(fs_type, flags, data, sel_fill_super); } +static void sel_kill_sb(struct super_block *sb) +{ + selinux_fs_info_free(sb); + kill_litter_super(sb); +} + static struct file_system_type sel_fs_type = { .name = "selinuxfs", .mount = sel_mount, - .kill_sb = kill_litter_super, + .kill_sb = sel_kill_sb, }; struct vfsmount *selinuxfs_mount; +struct path selinux_null; static int __init init_sel_fs(void) { + struct qstr null_name = QSTR_INIT(NULL_FILE_NAME, + sizeof(NULL_FILE_NAME)-1); int err; if (!selinux_enabled) @@ -1926,6 +2044,13 @@ static int __init init_sel_fs(void) err = PTR_ERR(selinuxfs_mount); selinuxfs_mount = NULL; } + selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root, + &null_name); + if (IS_ERR(selinux_null.dentry)) { + pr_err("selinuxfs: could not lookup null!\n"); + err = PTR_ERR(selinux_null.dentry); + selinux_null.dentry = NULL; + } return err; } diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 2c3c7d010d8a..a2c9148b0662 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -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); -} diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 725853cadc42..0d652fad5319 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -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) diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index b6a78b09235c..5ae8c61b75bf 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -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); -} diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index edf4fa39c60a..6aa7cf6a2197 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -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); diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index fe25b3fb2154..ebfdaa31ee32 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -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); -} diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index 6183ee2a2e7a..3e3e42bfd150 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -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 */ diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index ad982ce8bfa4..39475fb455bc 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -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, diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 131d76266ea5..9a3ff7af70ad 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -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; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8900ea5cbabf..8057e19dc15f 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -80,53 +80,32 @@ char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { "nnp_nosuid_transition" }; -int selinux_policycap_netpeer; -int selinux_policycap_openperm; -int selinux_policycap_extsockclass; -int selinux_policycap_alwaysnetwork; -int selinux_policycap_cgroupseclabel; -int selinux_policycap_nnp_nosuid_transition; +static struct selinux_ss selinux_ss; -static DEFINE_RWLOCK(policy_rwlock); - -static struct sidtab sidtab; -struct policydb policydb; -int ss_initialized; - -/* - * The largest sequence number that has been used when - * providing an access decision to the access vector cache. - * The sequence number only changes when a policy change - * occurs. - */ -static u32 latest_granting; +void selinux_ss_init(struct selinux_ss **ss) +{ + rwlock_init(&selinux_ss.policy_rwlock); + mutex_init(&selinux_ss.status_lock); + *ss = &selinux_ss; +} /* Forward declaration. */ -static int context_struct_to_string(struct context *context, char **scontext, +static int context_struct_to_string(struct policydb *policydb, + struct context *context, + char **scontext, u32 *scontext_len); -static void context_struct_compute_av(struct context *scontext, - struct context *tcontext, - u16 tclass, - struct av_decision *avd, - struct extended_perms *xperms); - -struct selinux_mapping { - u16 value; /* policy value */ - unsigned num_perms; - u32 perms[sizeof(u32) * 8]; -}; - -static struct selinux_mapping *current_mapping; -static u16 current_mapping_size; +static void context_struct_compute_av(struct policydb *policydb, + struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd, + struct extended_perms *xperms); static int selinux_set_mapping(struct policydb *pol, struct security_class_mapping *map, - struct selinux_mapping **out_map_p, - u16 *out_map_size) + struct selinux_map *out_map) { - struct selinux_mapping *out_map = NULL; - size_t size = sizeof(struct selinux_mapping); u16 i, j; unsigned k; bool print_unknown_handle = false; @@ -139,15 +118,15 @@ static int selinux_set_mapping(struct policydb *pol, i++; /* Allocate space for the class records, plus one for class zero */ - out_map = kcalloc(++i, size, GFP_ATOMIC); - if (!out_map) + out_map->mapping = kcalloc(++i, sizeof(*out_map->mapping), GFP_ATOMIC); + if (!out_map->mapping) return -ENOMEM; /* Store the raw class and permission values */ j = 0; while (map[j].name) { struct security_class_mapping *p_in = map + (j++); - struct selinux_mapping *p_out = out_map + j; + struct selinux_mapping *p_out = out_map->mapping + j; /* An empty class string skips ahead */ if (!strcmp(p_in->name, "")) { @@ -194,11 +173,11 @@ static int selinux_set_mapping(struct policydb *pol, printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n", pol->allow_unknown ? "allowed" : "denied"); - *out_map_p = out_map; - *out_map_size = i; + out_map->size = i; return 0; err: - kfree(out_map); + kfree(out_map->mapping); + out_map->mapping = NULL; return -EINVAL; } @@ -206,10 +185,10 @@ err: * Get real, policy values from mapped values */ -static u16 unmap_class(u16 tclass) +static u16 unmap_class(struct selinux_map *map, u16 tclass) { - if (tclass < current_mapping_size) - return current_mapping[tclass].value; + if (tclass < map->size) + return map->mapping[tclass].value; return tclass; } @@ -217,42 +196,44 @@ static u16 unmap_class(u16 tclass) /* * Get kernel value for class from its policy value */ -static u16 map_class(u16 pol_value) +static u16 map_class(struct selinux_map *map, u16 pol_value) { u16 i; - for (i = 1; i < current_mapping_size; i++) { - if (current_mapping[i].value == pol_value) + for (i = 1; i < map->size; i++) { + if (map->mapping[i].value == pol_value) return i; } return SECCLASS_NULL; } -static void map_decision(u16 tclass, struct av_decision *avd, +static void map_decision(struct selinux_map *map, + u16 tclass, struct av_decision *avd, int allow_unknown) { - if (tclass < current_mapping_size) { - unsigned i, n = current_mapping[tclass].num_perms; + if (tclass < map->size) { + struct selinux_mapping *mapping = &map->mapping[tclass]; + unsigned int i, n = mapping->num_perms; u32 result; for (i = 0, result = 0; i < n; i++) { - if (avd->allowed & current_mapping[tclass].perms[i]) + if (avd->allowed & mapping->perms[i]) result |= 1<<i; - if (allow_unknown && !current_mapping[tclass].perms[i]) + if (allow_unknown && !mapping->perms[i]) result |= 1<<i; } avd->allowed = result; for (i = 0, result = 0; i < n; i++) - if (avd->auditallow & current_mapping[tclass].perms[i]) + if (avd->auditallow & mapping->perms[i]) result |= 1<<i; avd->auditallow = result; for (i = 0, result = 0; i < n; i++) { - if (avd->auditdeny & current_mapping[tclass].perms[i]) + if (avd->auditdeny & mapping->perms[i]) result |= 1<<i; - if (!allow_unknown && !current_mapping[tclass].perms[i]) + if (!allow_unknown && !mapping->perms[i]) result |= 1<<i; } /* @@ -266,9 +247,11 @@ static void map_decision(u16 tclass, struct av_decision *avd, } } -int security_mls_enabled(void) +int security_mls_enabled(struct selinux_state *state) { - return policydb.mls_enabled; + struct policydb *p = &state->ss->policydb; + + return p->mls_enabled; } /* @@ -282,7 +265,8 @@ int security_mls_enabled(void) * of the process performing the transition. All other callers of * constraint_expr_eval should pass in NULL for xcontext. */ -static int constraint_expr_eval(struct context *scontext, +static int constraint_expr_eval(struct policydb *policydb, + struct context *scontext, struct context *tcontext, struct context *xcontext, struct constraint_expr *cexpr) @@ -326,8 +310,8 @@ static int constraint_expr_eval(struct context *scontext, case CEXPR_ROLE: val1 = scontext->role; val2 = tcontext->role; - r1 = policydb.role_val_to_struct[val1 - 1]; - r2 = policydb.role_val_to_struct[val2 - 1]; + r1 = policydb->role_val_to_struct[val1 - 1]; + r2 = policydb->role_val_to_struct[val2 - 1]; switch (e->op) { case CEXPR_DOM: s[++sp] = ebitmap_get_bit(&r1->dominates, @@ -472,7 +456,8 @@ static int dump_masked_av_helper(void *k, void *d, void *args) return 0; } -static void security_dump_masked_av(struct context *scontext, +static void security_dump_masked_av(struct policydb *policydb, + struct context *scontext, struct context *tcontext, u16 tclass, u32 permissions, @@ -492,8 +477,8 @@ static void security_dump_masked_av(struct context *scontext, if (!permissions) return; - tclass_name = sym_name(&policydb, SYM_CLASSES, tclass - 1); - tclass_dat = policydb.class_val_to_struct[tclass - 1]; + tclass_name = sym_name(policydb, SYM_CLASSES, tclass - 1); + tclass_dat = policydb->class_val_to_struct[tclass - 1]; common_dat = tclass_dat->comdatum; /* init permission_names */ @@ -507,11 +492,11 @@ static void security_dump_masked_av(struct context *scontext, goto out; /* get scontext/tcontext in text form */ - if (context_struct_to_string(scontext, + if (context_struct_to_string(policydb, scontext, &scontext_name, &length) < 0) goto out; - if (context_struct_to_string(tcontext, + if (context_struct_to_string(policydb, tcontext, &tcontext_name, &length) < 0) goto out; @@ -550,7 +535,8 @@ out: * security_boundary_permission - drops violated permissions * on boundary constraint. */ -static void type_attribute_bounds_av(struct context *scontext, +static void type_attribute_bounds_av(struct policydb *policydb, + struct context *scontext, struct context *tcontext, u16 tclass, struct av_decision *avd) @@ -562,14 +548,14 @@ static void type_attribute_bounds_av(struct context *scontext, struct type_datum *target; u32 masked = 0; - source = flex_array_get_ptr(policydb.type_val_to_struct_array, + source = flex_array_get_ptr(policydb->type_val_to_struct_array, scontext->type - 1); BUG_ON(!source); if (!source->bounds) return; - target = flex_array_get_ptr(policydb.type_val_to_struct_array, + target = flex_array_get_ptr(policydb->type_val_to_struct_array, tcontext->type - 1); BUG_ON(!target); @@ -584,7 +570,7 @@ static void type_attribute_bounds_av(struct context *scontext, tcontextp = &lo_tcontext; } - context_struct_compute_av(&lo_scontext, + context_struct_compute_av(policydb, &lo_scontext, tcontextp, tclass, &lo_avd, @@ -599,7 +585,7 @@ static void type_attribute_bounds_av(struct context *scontext, avd->allowed &= ~masked; /* audit masked permissions */ - security_dump_masked_av(scontext, tcontext, + security_dump_masked_av(policydb, scontext, tcontext, tclass, masked, "bounds"); } @@ -632,11 +618,12 @@ void services_compute_xperms_drivers( * Compute access vectors and extended permissions based on a context * structure pair for the permissions in a particular class. */ -static void context_struct_compute_av(struct context *scontext, - struct context *tcontext, - u16 tclass, - struct av_decision *avd, - struct extended_perms *xperms) +static void context_struct_compute_av(struct policydb *policydb, + struct context *scontext, + struct context *tcontext, + u16 tclass, + struct av_decision *avd, + struct extended_perms *xperms) { struct constraint_node *constraint; struct role_allow *ra; @@ -655,13 +642,13 @@ static void context_struct_compute_av(struct context *scontext, xperms->len = 0; } - if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { + if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) { if (printk_ratelimit()) printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); return; } - tclass_datum = policydb.class_val_to_struct[tclass - 1]; + tclass_datum = policydb->class_val_to_struct[tclass - 1]; /* * If a specific type enforcement rule was defined for @@ -669,15 +656,18 @@ static void context_struct_compute_av(struct context *scontext, */ avkey.target_class = tclass; avkey.specified = AVTAB_AV | AVTAB_XPERMS; - sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); + sattr = flex_array_get(policydb->type_attr_map_array, + scontext->type - 1); BUG_ON(!sattr); - tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); + tattr = flex_array_get(policydb->type_attr_map_array, + tcontext->type - 1); BUG_ON(!tattr); ebitmap_for_each_positive_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; avkey.target_type = j + 1; - for (node = avtab_search_node(&policydb.te_avtab, &avkey); + for (node = avtab_search_node(&policydb->te_avtab, + &avkey); node; node = avtab_search_node_next(node, avkey.specified)) { if (node->key.specified == AVTAB_ALLOWED) @@ -691,7 +681,7 @@ static void context_struct_compute_av(struct context *scontext, } /* Check conditional av table for additional permissions */ - cond_compute_av(&policydb.te_cond_avtab, &avkey, + cond_compute_av(&policydb->te_cond_avtab, &avkey, avd, xperms); } @@ -704,7 +694,7 @@ static void context_struct_compute_av(struct context *scontext, constraint = tclass_datum->constraints; while (constraint) { if ((constraint->permissions & (avd->allowed)) && - !constraint_expr_eval(scontext, tcontext, NULL, + !constraint_expr_eval(policydb, scontext, tcontext, NULL, constraint->expr)) { avd->allowed &= ~(constraint->permissions); } @@ -716,16 +706,16 @@ static void context_struct_compute_av(struct context *scontext, * role is changing, then check the (current_role, new_role) * pair. */ - if (tclass == policydb.process_class && - (avd->allowed & policydb.process_trans_perms) && + if (tclass == policydb->process_class && + (avd->allowed & policydb->process_trans_perms) && scontext->role != tcontext->role) { - for (ra = policydb.role_allow; ra; ra = ra->next) { + for (ra = policydb->role_allow; ra; ra = ra->next) { if (scontext->role == ra->role && tcontext->role == ra->new_role) break; } if (!ra) - avd->allowed &= ~policydb.process_trans_perms; + avd->allowed &= ~policydb->process_trans_perms; } /* @@ -733,41 +723,46 @@ static void context_struct_compute_av(struct context *scontext, * constraint, lazy checks have to mask any violated * permission and notice it to userspace via audit. */ - type_attribute_bounds_av(scontext, tcontext, + type_attribute_bounds_av(policydb, scontext, tcontext, tclass, avd); } -static int security_validtrans_handle_fail(struct context *ocontext, +static int security_validtrans_handle_fail(struct selinux_state *state, + struct context *ocontext, struct context *ncontext, struct context *tcontext, u16 tclass) { + struct policydb *p = &state->ss->policydb; char *o = NULL, *n = NULL, *t = NULL; u32 olen, nlen, tlen; - if (context_struct_to_string(ocontext, &o, &olen)) + if (context_struct_to_string(p, ocontext, &o, &olen)) goto out; - if (context_struct_to_string(ncontext, &n, &nlen)) + if (context_struct_to_string(p, ncontext, &n, &nlen)) goto out; - if (context_struct_to_string(tcontext, &t, &tlen)) + if (context_struct_to_string(p, tcontext, &t, &tlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_validate_transition seresult=denied" " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", - o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); + o, n, t, sym_name(p, SYM_CLASSES, tclass-1)); out: kfree(o); kfree(n); kfree(t); - if (!selinux_enforcing) + if (!enforcing_enabled(state)) return 0; return -EPERM; } -static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, +static int security_compute_validatetrans(struct selinux_state *state, + u32 oldsid, u32 newsid, u32 tasksid, u16 orig_tclass, bool user) { + struct policydb *policydb; + struct sidtab *sidtab; struct context *ocontext; struct context *ncontext; struct context *tcontext; @@ -776,23 +771,27 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass; int rc = 0; - if (!ss_initialized) + + if (!state->initialized) return 0; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; if (!user) - tclass = unmap_class(orig_tclass); + tclass = unmap_class(&state->ss->map, orig_tclass); else tclass = orig_tclass; - if (!tclass || tclass > policydb.p_classes.nprim) { + if (!tclass || tclass > policydb->p_classes.nprim) { rc = -EINVAL; goto out; } - tclass_datum = policydb.class_val_to_struct[tclass - 1]; + tclass_datum = policydb->class_val_to_struct[tclass - 1]; - ocontext = sidtab_search(&sidtab, oldsid); + ocontext = sidtab_search(sidtab, oldsid); if (!ocontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, oldsid); @@ -800,7 +799,7 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, goto out; } - ncontext = sidtab_search(&sidtab, newsid); + ncontext = sidtab_search(sidtab, newsid); if (!ncontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, newsid); @@ -808,7 +807,7 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, goto out; } - tcontext = sidtab_search(&sidtab, tasksid); + tcontext = sidtab_search(sidtab, tasksid); if (!tcontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, tasksid); @@ -818,12 +817,13 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, constraint = tclass_datum->validatetrans; while (constraint) { - if (!constraint_expr_eval(ocontext, ncontext, tcontext, - constraint->expr)) { + if (!constraint_expr_eval(policydb, ocontext, ncontext, + tcontext, constraint->expr)) { if (user) rc = -EPERM; else - rc = security_validtrans_handle_fail(ocontext, + rc = security_validtrans_handle_fail(state, + ocontext, ncontext, tcontext, tclass); @@ -833,22 +833,24 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, } out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } -int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, - u16 tclass) +int security_validate_transition_user(struct selinux_state *state, + u32 oldsid, u32 newsid, u32 tasksid, + u16 tclass) { - return security_compute_validatetrans(oldsid, newsid, tasksid, - tclass, true); + return security_compute_validatetrans(state, oldsid, newsid, tasksid, + tclass, true); } -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 orig_tclass) { - return security_compute_validatetrans(oldsid, newsid, tasksid, - orig_tclass, false); + return security_compute_validatetrans(state, oldsid, newsid, tasksid, + orig_tclass, false); } /* @@ -860,20 +862,26 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, * @oldsid : current security identifier * @newsid : destinated security identifier */ -int security_bounded_transition(u32 old_sid, u32 new_sid) +int security_bounded_transition(struct selinux_state *state, + u32 old_sid, u32 new_sid) { + struct policydb *policydb; + struct sidtab *sidtab; struct context *old_context, *new_context; struct type_datum *type; int index; int rc; - if (!ss_initialized) + if (!state->initialized) return 0; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; rc = -EINVAL; - old_context = sidtab_search(&sidtab, old_sid); + old_context = sidtab_search(sidtab, old_sid); if (!old_context) { printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", __func__, old_sid); @@ -881,7 +889,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) } rc = -EINVAL; - new_context = sidtab_search(&sidtab, new_sid); + new_context = sidtab_search(sidtab, new_sid); if (!new_context) { printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", __func__, new_sid); @@ -895,7 +903,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) index = new_context->type; while (true) { - type = flex_array_get_ptr(policydb.type_val_to_struct_array, + type = flex_array_get_ptr(policydb->type_val_to_struct_array, index - 1); BUG_ON(!type); @@ -917,9 +925,9 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) char *new_name = NULL; u32 length; - if (!context_struct_to_string(old_context, + if (!context_struct_to_string(policydb, old_context, &old_name, &length) && - !context_struct_to_string(new_context, + !context_struct_to_string(policydb, new_context, &new_name, &length)) { audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, @@ -932,17 +940,17 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) kfree(old_name); } out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } -static void avd_init(struct av_decision *avd) +static void avd_init(struct selinux_state *state, struct av_decision *avd) { avd->allowed = 0; avd->auditallow = 0; avd->auditdeny = 0xffffffff; - avd->seqno = latest_granting; + avd->seqno = state->ss->latest_granting; avd->flags = 0; } @@ -1000,12 +1008,15 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd, } } -void security_compute_xperms_decision(u32 ssid, - u32 tsid, - u16 orig_tclass, - u8 driver, - struct extended_perms_decision *xpermd) +void security_compute_xperms_decision(struct selinux_state *state, + u32 ssid, + u32 tsid, + u16 orig_tclass, + u8 driver, + struct extended_perms_decision *xpermd) { + struct policydb *policydb; + struct sidtab *sidtab; u16 tclass; struct context *scontext, *tcontext; struct avtab_key avkey; @@ -1020,60 +1031,64 @@ void security_compute_xperms_decision(u32 ssid, memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p)); memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p)); - read_lock(&policy_rwlock); - if (!ss_initialized) + read_lock(&state->ss->policy_rwlock); + if (!state->initialized) goto allow; - scontext = sidtab_search(&sidtab, ssid); + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + scontext = sidtab_search(sidtab, ssid); if (!scontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, ssid); goto out; } - tcontext = sidtab_search(&sidtab, tsid); + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, tsid); goto out; } - tclass = unmap_class(orig_tclass); + tclass = unmap_class(&state->ss->map, orig_tclass); if (unlikely(orig_tclass && !tclass)) { - if (policydb.allow_unknown) + if (policydb->allow_unknown) goto allow; goto out; } - if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { + if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) { pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass); goto out; } avkey.target_class = tclass; avkey.specified = AVTAB_XPERMS; - sattr = flex_array_get(policydb.type_attr_map_array, + sattr = flex_array_get(policydb->type_attr_map_array, scontext->type - 1); BUG_ON(!sattr); - tattr = flex_array_get(policydb.type_attr_map_array, + tattr = flex_array_get(policydb->type_attr_map_array, tcontext->type - 1); BUG_ON(!tattr); ebitmap_for_each_positive_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; avkey.target_type = j + 1; - for (node = avtab_search_node(&policydb.te_avtab, &avkey); + for (node = avtab_search_node(&policydb->te_avtab, + &avkey); node; node = avtab_search_node_next(node, avkey.specified)) services_compute_xperms_decision(xpermd, node); - cond_compute_xperms(&policydb.te_cond_avtab, + cond_compute_xperms(&policydb->te_cond_avtab, &avkey, xpermd); } } out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return; allow: memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p)); @@ -1091,22 +1106,28 @@ allow: * Compute a set of access vector decisions based on the * SID pair (@ssid, @tsid) for the permissions in @tclass. */ -void security_compute_av(u32 ssid, +void security_compute_av(struct selinux_state *state, + u32 ssid, u32 tsid, u16 orig_tclass, struct av_decision *avd, struct extended_perms *xperms) { + struct policydb *policydb; + struct sidtab *sidtab; u16 tclass; struct context *scontext = NULL, *tcontext = NULL; - read_lock(&policy_rwlock); - avd_init(avd); + read_lock(&state->ss->policy_rwlock); + avd_init(state, avd); xperms->len = 0; - if (!ss_initialized) + if (!state->initialized) goto allow; - scontext = sidtab_search(&sidtab, ssid); + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + scontext = sidtab_search(sidtab, ssid); if (!scontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, ssid); @@ -1114,45 +1135,53 @@ void security_compute_av(u32 ssid, } /* permissive domain? */ - if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; - tcontext = sidtab_search(&sidtab, tsid); + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, tsid); goto out; } - tclass = unmap_class(orig_tclass); + tclass = unmap_class(&state->ss->map, orig_tclass); if (unlikely(orig_tclass && !tclass)) { - if (policydb.allow_unknown) + if (policydb->allow_unknown) goto allow; goto out; } - context_struct_compute_av(scontext, tcontext, tclass, avd, xperms); - map_decision(orig_tclass, avd, policydb.allow_unknown); + context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, + xperms); + map_decision(&state->ss->map, orig_tclass, avd, + policydb->allow_unknown); out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return; allow: avd->allowed = 0xffffffff; goto out; } -void security_compute_av_user(u32 ssid, +void security_compute_av_user(struct selinux_state *state, + u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) { + struct policydb *policydb; + struct sidtab *sidtab; struct context *scontext = NULL, *tcontext = NULL; - read_lock(&policy_rwlock); - avd_init(avd); - if (!ss_initialized) + read_lock(&state->ss->policy_rwlock); + avd_init(state, avd); + if (!state->initialized) goto allow; - scontext = sidtab_search(&sidtab, ssid); + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + scontext = sidtab_search(sidtab, ssid); if (!scontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, ssid); @@ -1160,10 +1189,10 @@ void security_compute_av_user(u32 ssid, } /* permissive domain? */ - if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; - tcontext = sidtab_search(&sidtab, tsid); + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, tsid); @@ -1171,14 +1200,15 @@ void security_compute_av_user(u32 ssid, } if (unlikely(!tclass)) { - if (policydb.allow_unknown) + if (policydb->allow_unknown) goto allow; goto out; } - context_struct_compute_av(scontext, tcontext, tclass, avd, NULL); + context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, + NULL); out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return; allow: avd->allowed = 0xffffffff; @@ -1192,7 +1222,9 @@ allow: * to point to this string and set `*scontext_len' to * the length of the string. */ -static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len) +static int context_struct_to_string(struct policydb *p, + struct context *context, + char **scontext, u32 *scontext_len) { char *scontextp; @@ -1211,10 +1243,10 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 } /* Compute the size of the context. */ - *scontext_len += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + 1; - *scontext_len += strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + 1; - *scontext_len += strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)) + 1; - *scontext_len += mls_compute_context_len(context); + *scontext_len += strlen(sym_name(p, SYM_USERS, context->user - 1)) + 1; + *scontext_len += strlen(sym_name(p, SYM_ROLES, context->role - 1)) + 1; + *scontext_len += strlen(sym_name(p, SYM_TYPES, context->type - 1)) + 1; + *scontext_len += mls_compute_context_len(p, context); if (!scontext) return 0; @@ -1229,11 +1261,11 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 * Copy the user name, role name and type name into the context. */ scontextp += sprintf(scontextp, "%s:%s:%s", - sym_name(&policydb, SYM_USERS, context->user - 1), - sym_name(&policydb, SYM_ROLES, context->role - 1), - sym_name(&policydb, SYM_TYPES, context->type - 1)); + sym_name(p, SYM_USERS, context->user - 1), + sym_name(p, SYM_ROLES, context->role - 1), + sym_name(p, SYM_TYPES, context->type - 1)); - mls_sid_to_context(context, &scontextp); + mls_sid_to_context(p, context, &scontextp); *scontextp = 0; @@ -1249,9 +1281,12 @@ const char *security_get_initial_sid_context(u32 sid) return initial_sid_to_string[sid]; } -static int security_sid_to_context_core(u32 sid, char **scontext, +static int security_sid_to_context_core(struct selinux_state *state, + u32 sid, char **scontext, u32 *scontext_len, int force) { + struct policydb *policydb; + struct sidtab *sidtab; struct context *context; int rc = 0; @@ -1259,7 +1294,7 @@ static int security_sid_to_context_core(u32 sid, char **scontext, *scontext = NULL; *scontext_len = 0; - if (!ss_initialized) { + if (!state->initialized) { if (sid <= SECINITSID_NUM) { char *scontextp; @@ -1280,20 +1315,23 @@ static int security_sid_to_context_core(u32 sid, char **scontext, rc = -EINVAL; goto out; } - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; if (force) - context = sidtab_search_force(&sidtab, sid); + context = sidtab_search_force(sidtab, sid); else - context = sidtab_search(&sidtab, sid); + context = sidtab_search(sidtab, sid); if (!context) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, sid); rc = -EINVAL; goto out_unlock; } - rc = context_struct_to_string(context, scontext, scontext_len); + rc = context_struct_to_string(policydb, context, scontext, + scontext_len); out_unlock: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); out: return rc; @@ -1309,14 +1347,18 @@ out: * into a dynamically allocated string of the correct size. Set @scontext * to point to this string and set @scontext_len to the length of the string. */ -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) { - return security_sid_to_context_core(sid, scontext, scontext_len, 0); + return security_sid_to_context_core(state, sid, scontext, + scontext_len, 0); } -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) { - return security_sid_to_context_core(sid, scontext, scontext_len, 1); + return security_sid_to_context_core(state, sid, scontext, + scontext_len, 1); } /* @@ -1404,10 +1446,13 @@ out: return rc; } -static int security_context_to_sid_core(const char *scontext, u32 scontext_len, +static int security_context_to_sid_core(struct selinux_state *state, + const char *scontext, u32 scontext_len, u32 *sid, u32 def_sid, gfp_t gfp_flags, int force) { + struct policydb *policydb; + struct sidtab *sidtab; char *scontext2, *str = NULL; struct context context; int rc = 0; @@ -1421,7 +1466,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, if (!scontext2) return -ENOMEM; - if (!ss_initialized) { + if (!state->initialized) { int i; for (i = 1; i < SECINITSID_NUM; i++) { @@ -1442,9 +1487,10 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, if (!str) goto out; } - - read_lock(&policy_rwlock); - rc = string_to_context_struct(&policydb, &sidtab, scontext2, + read_lock(&state->ss->policy_rwlock); + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + rc = string_to_context_struct(policydb, sidtab, scontext2, scontext_len, &context, def_sid); if (rc == -EINVAL && force) { context.str = str; @@ -1452,10 +1498,10 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, str = NULL; } else if (rc) goto out_unlock; - rc = sidtab_context_to_sid(&sidtab, &context, sid); + rc = sidtab_context_to_sid(sidtab, &context, sid); context_destroy(&context); out_unlock: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); out: kfree(scontext2); kfree(str); @@ -1474,16 +1520,19 @@ out: * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient * memory is available, or 0 on success. */ -int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid, +int security_context_to_sid(struct selinux_state *state, + const char *scontext, u32 scontext_len, u32 *sid, gfp_t gfp) { - return security_context_to_sid_core(scontext, scontext_len, + return security_context_to_sid_core(state, scontext, scontext_len, sid, SECSID_NULL, gfp, 0); } -int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp) +int security_context_str_to_sid(struct selinux_state *state, + const char *scontext, u32 *sid, gfp_t gfp) { - return security_context_to_sid(scontext, strlen(scontext), sid, gfp); + return security_context_to_sid(state, scontext, strlen(scontext), + sid, gfp); } /** @@ -1504,51 +1553,56 @@ int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp) * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient * memory is available, or 0 on success. */ -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 *sid, u32 def_sid, gfp_t gfp_flags) { - return security_context_to_sid_core(scontext, scontext_len, + return security_context_to_sid_core(state, scontext, scontext_len, sid, def_sid, gfp_flags, 1); } -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) { - return security_context_to_sid_core(scontext, scontext_len, + return security_context_to_sid_core(state, scontext, scontext_len, sid, SECSID_NULL, GFP_KERNEL, 1); } static int compute_sid_handle_invalid_context( + struct selinux_state *state, struct context *scontext, struct context *tcontext, u16 tclass, struct context *newcontext) { + struct policydb *policydb = &state->ss->policydb; char *s = NULL, *t = NULL, *n = NULL; u32 slen, tlen, nlen; - if (context_struct_to_string(scontext, &s, &slen)) + if (context_struct_to_string(policydb, scontext, &s, &slen)) goto out; - if (context_struct_to_string(tcontext, &t, &tlen)) + if (context_struct_to_string(policydb, tcontext, &t, &tlen)) goto out; - if (context_struct_to_string(newcontext, &n, &nlen)) + if (context_struct_to_string(policydb, newcontext, &n, &nlen)) goto out; audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_compute_sid invalid_context=%s" " scontext=%s" " tcontext=%s" " tclass=%s", - n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); + n, s, t, sym_name(policydb, SYM_CLASSES, tclass-1)); out: kfree(s); kfree(t); kfree(n); - if (!selinux_enforcing) + if (!enforcing_enabled(state)) return 0; return -EACCES; } -static void filename_compute_type(struct policydb *p, struct context *newcontext, +static void filename_compute_type(struct policydb *policydb, + struct context *newcontext, u32 stype, u32 ttype, u16 tclass, const char *objname) { @@ -1560,7 +1614,7 @@ static void filename_compute_type(struct policydb *p, struct context *newcontext * like /dev or /var/run. This bitmap will quickly skip rule searches * if the ttype does not contain any rules. */ - if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype)) + if (!ebitmap_get_bit(&policydb->filename_trans_ttypes, ttype)) return; ft.stype = stype; @@ -1568,12 +1622,13 @@ static void filename_compute_type(struct policydb *p, struct context *newcontext ft.tclass = tclass; ft.name = objname; - otype = hashtab_search(p->filename_trans, &ft); + otype = hashtab_search(policydb->filename_trans, &ft); if (otype) newcontext->type = otype->otype; } -static int security_compute_sid(u32 ssid, +static int security_compute_sid(struct selinux_state *state, + u32 ssid, u32 tsid, u16 orig_tclass, u32 specified, @@ -1581,6 +1636,8 @@ static int security_compute_sid(u32 ssid, u32 *out_sid, bool kern) { + struct policydb *policydb; + struct sidtab *sidtab; struct class_datum *cladatum = NULL; struct context *scontext = NULL, *tcontext = NULL, newcontext; struct role_trans *roletr = NULL; @@ -1591,7 +1648,7 @@ static int security_compute_sid(u32 ssid, int rc = 0; bool sock; - if (!ss_initialized) { + if (!state->initialized) { switch (orig_tclass) { case SECCLASS_PROCESS: /* kernel value */ *out_sid = ssid; @@ -1605,24 +1662,28 @@ static int security_compute_sid(u32 ssid, context_init(&newcontext); - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); if (kern) { - tclass = unmap_class(orig_tclass); + tclass = unmap_class(&state->ss->map, orig_tclass); sock = security_is_socket_class(orig_tclass); } else { tclass = orig_tclass; - sock = security_is_socket_class(map_class(tclass)); + sock = security_is_socket_class(map_class(&state->ss->map, + tclass)); } - scontext = sidtab_search(&sidtab, ssid); + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + scontext = sidtab_search(sidtab, ssid); if (!scontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, ssid); rc = -EINVAL; goto out_unlock; } - tcontext = sidtab_search(&sidtab, tsid); + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, tsid); @@ -1630,8 +1691,8 @@ static int security_compute_sid(u32 ssid, goto out_unlock; } - if (tclass && tclass <= policydb.p_classes.nprim) - cladatum = policydb.class_val_to_struct[tclass - 1]; + if (tclass && tclass <= policydb->p_classes.nprim) + cladatum = policydb->class_val_to_struct[tclass - 1]; /* Set the user identity. */ switch (specified) { @@ -1657,7 +1718,7 @@ static int security_compute_sid(u32 ssid, } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { newcontext.role = tcontext->role; } else { - if ((tclass == policydb.process_class) || (sock == true)) + if ((tclass == policydb->process_class) || (sock == true)) newcontext.role = scontext->role; else newcontext.role = OBJECT_R_VAL; @@ -1669,7 +1730,7 @@ static int security_compute_sid(u32 ssid, } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { newcontext.type = tcontext->type; } else { - if ((tclass == policydb.process_class) || (sock == true)) { + if ((tclass == policydb->process_class) || (sock == true)) { /* Use the type of process. */ newcontext.type = scontext->type; } else { @@ -1683,11 +1744,11 @@ static int security_compute_sid(u32 ssid, avkey.target_type = tcontext->type; avkey.target_class = tclass; avkey.specified = specified; - avdatum = avtab_search(&policydb.te_avtab, &avkey); + avdatum = avtab_search(&policydb->te_avtab, &avkey); /* If no permanent rule, also check for enabled conditional rules */ if (!avdatum) { - node = avtab_search_node(&policydb.te_cond_avtab, &avkey); + node = avtab_search_node(&policydb->te_cond_avtab, &avkey); for (; node; node = avtab_search_node_next(node, specified)) { if (node->key.specified & AVTAB_ENABLED) { avdatum = &node->datum; @@ -1703,13 +1764,14 @@ static int security_compute_sid(u32 ssid, /* if we have a objname this is a file trans check so check those rules */ if (objname) - filename_compute_type(&policydb, &newcontext, scontext->type, + filename_compute_type(policydb, &newcontext, scontext->type, tcontext->type, tclass, objname); /* Check for class-specific changes. */ if (specified & AVTAB_TRANSITION) { /* Look for a role transition rule. */ - for (roletr = policydb.role_tr; roletr; roletr = roletr->next) { + for (roletr = policydb->role_tr; roletr; + roletr = roletr->next) { if ((roletr->role == scontext->role) && (roletr->type == tcontext->type) && (roletr->tclass == tclass)) { @@ -1722,14 +1784,14 @@ static int security_compute_sid(u32 ssid, /* Set the MLS attributes. This is done last because it may allocate memory. */ - rc = mls_compute_sid(scontext, tcontext, tclass, specified, + rc = mls_compute_sid(policydb, scontext, tcontext, tclass, specified, &newcontext, sock); if (rc) goto out_unlock; /* Check the validity of the context. */ - if (!policydb_context_isvalid(&policydb, &newcontext)) { - rc = compute_sid_handle_invalid_context(scontext, + if (!policydb_context_isvalid(policydb, &newcontext)) { + rc = compute_sid_handle_invalid_context(state, scontext, tcontext, tclass, &newcontext); @@ -1737,9 +1799,9 @@ static int security_compute_sid(u32 ssid, goto out_unlock; } /* Obtain the sid for the context. */ - rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid); + rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); out_unlock: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); context_destroy(&newcontext); out: return rc; @@ -1758,17 +1820,21 @@ out: * if insufficient memory is available, or %0 if the new SID was * computed successfully. */ -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) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + return security_compute_sid(state, ssid, tsid, tclass, + AVTAB_TRANSITION, qstr ? qstr->name : NULL, out_sid, true); } -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) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + return security_compute_sid(state, ssid, tsid, tclass, + AVTAB_TRANSITION, objname, out_sid, false); } @@ -1785,12 +1851,14 @@ int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, * if insufficient memory is available, or %0 if the SID was * computed successfully. */ -int security_member_sid(u32 ssid, +int security_member_sid(struct selinux_state *state, + u32 ssid, u32 tsid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, NULL, + return security_compute_sid(state, ssid, tsid, tclass, + AVTAB_MEMBER, NULL, out_sid, false); } @@ -1807,12 +1875,14 @@ int security_member_sid(u32 ssid, * if insufficient memory is available, or %0 if the SID was * computed successfully. */ -int security_change_sid(u32 ssid, +int security_change_sid(struct selinux_state *state, + u32 ssid, u32 tsid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, NULL, + return security_compute_sid(state, + ssid, tsid, tclass, AVTAB_CHANGE, NULL, out_sid, false); } @@ -1829,15 +1899,18 @@ static int clone_sid(u32 sid, return 0; } -static inline int convert_context_handle_invalid_context(struct context *context) +static inline int convert_context_handle_invalid_context( + struct selinux_state *state, + struct context *context) { + struct policydb *policydb = &state->ss->policydb; char *s; u32 len; - if (selinux_enforcing) + if (enforcing_enabled(state)) return -EINVAL; - if (!context_struct_to_string(context, &s, &len)) { + if (!context_struct_to_string(policydb, context, &s, &len)) { printk(KERN_WARNING "SELinux: Context %s would be invalid if enforcing\n", s); kfree(s); } @@ -1845,6 +1918,7 @@ static inline int convert_context_handle_invalid_context(struct context *context } struct convert_context_args { + struct selinux_state *state; struct policydb *oldp; struct policydb *newp; }; @@ -1971,7 +2045,8 @@ static int convert_context(u32 key, /* Check the validity of the new context. */ if (!policydb_context_isvalid(args->newp, c)) { - rc = convert_context_handle_invalid_context(&oldc); + rc = convert_context_handle_invalid_context(args->state, + &oldc); if (rc) goto bad; } @@ -1983,7 +2058,7 @@ out: return rc; bad: /* Map old representation to string and save it. */ - rc = context_struct_to_string(&oldc, &s, &len); + rc = context_struct_to_string(args->oldp, &oldc, &s, &len); if (rc) return rc; context_destroy(&oldc); @@ -1996,39 +2071,29 @@ bad: goto out; } -static void security_load_policycaps(void) +static void security_load_policycaps(struct selinux_state *state) { + struct policydb *p = &state->ss->policydb; unsigned int i; struct ebitmap_node *node; - selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps, - POLICYDB_CAPABILITY_NETPEER); - selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, - POLICYDB_CAPABILITY_OPENPERM); - selinux_policycap_extsockclass = ebitmap_get_bit(&policydb.policycaps, - POLICYDB_CAPABILITY_EXTSOCKCLASS); - selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, - POLICYDB_CAPABILITY_ALWAYSNETWORK); - selinux_policycap_cgroupseclabel = - ebitmap_get_bit(&policydb.policycaps, - POLICYDB_CAPABILITY_CGROUPSECLABEL); - selinux_policycap_nnp_nosuid_transition = - ebitmap_get_bit(&policydb.policycaps, - POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION); + for (i = 0; i < ARRAY_SIZE(state->policycap); i++) + state->policycap[i] = ebitmap_get_bit(&p->policycaps, i); for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++) pr_info("SELinux: policy capability %s=%d\n", selinux_policycap_names[i], - ebitmap_get_bit(&policydb.policycaps, i)); + ebitmap_get_bit(&p->policycaps, i)); - ebitmap_for_each_positive_bit(&policydb.policycaps, node, i) { + ebitmap_for_each_positive_bit(&p->policycaps, node, i) { if (i >= ARRAY_SIZE(selinux_policycap_names)) pr_info("SELinux: unknown policy capability %u\n", i); } } -static int security_preserve_bools(struct policydb *p); +static int security_preserve_bools(struct selinux_state *state, + struct policydb *newpolicydb); /** * security_load_policy - Load a security policy configuration. @@ -2040,14 +2105,16 @@ static int security_preserve_bools(struct policydb *p); * This function will flush the access vector cache after * loading the new policy. */ -int security_load_policy(void *data, size_t len) +int security_load_policy(struct selinux_state *state, void *data, size_t len) { + struct policydb *policydb; + struct sidtab *sidtab; struct policydb *oldpolicydb, *newpolicydb; struct sidtab oldsidtab, newsidtab; - struct selinux_mapping *oldmap, *map = NULL; + struct selinux_mapping *oldmapping; + struct selinux_map newmap; struct convert_context_args args; u32 seqno; - u16 map_size; int rc = 0; struct policy_file file = { data, len }, *fp = &file; @@ -2058,53 +2125,42 @@ int security_load_policy(void *data, size_t len) } newpolicydb = oldpolicydb + 1; - if (!ss_initialized) { - avtab_cache_init(); - ebitmap_cache_init(); - hashtab_cache_init(); - rc = policydb_read(&policydb, fp); - if (rc) { - avtab_cache_destroy(); - ebitmap_cache_destroy(); - hashtab_cache_destroy(); + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + if (!state->initialized) { + rc = policydb_read(policydb, fp); + if (rc) goto out; - } - policydb.len = len; - rc = selinux_set_mapping(&policydb, secclass_map, - ¤t_mapping, - ¤t_mapping_size); + policydb->len = len; + rc = selinux_set_mapping(policydb, secclass_map, + &state->ss->map); if (rc) { - policydb_destroy(&policydb); - avtab_cache_destroy(); - ebitmap_cache_destroy(); - hashtab_cache_destroy(); + policydb_destroy(policydb); goto out; } - rc = policydb_load_isids(&policydb, &sidtab); + rc = policydb_load_isids(policydb, sidtab); if (rc) { - policydb_destroy(&policydb); - avtab_cache_destroy(); - ebitmap_cache_destroy(); - hashtab_cache_destroy(); + policydb_destroy(policydb); goto out; } - security_load_policycaps(); - ss_initialized = 1; - seqno = ++latest_granting; + security_load_policycaps(state); + state->initialized = 1; + seqno = ++state->ss->latest_granting; selinux_complete_init(); - avc_ss_reset(seqno); + avc_ss_reset(state->avc, seqno); selnl_notify_policyload(seqno); - selinux_status_update_policyload(seqno); + selinux_status_update_policyload(state, seqno); selinux_netlbl_cache_invalidate(); selinux_xfrm_notify_policyload(); goto out; } #if 0 - sidtab_hash_eval(&sidtab, "sids"); + sidtab_hash_eval(sidtab, "sids"); #endif rc = policydb_read(newpolicydb, fp); @@ -2113,9 +2169,9 @@ int security_load_policy(void *data, size_t len) newpolicydb->len = len; /* If switching between different policy types, log MLS status */ - if (policydb.mls_enabled && !newpolicydb->mls_enabled) + if (policydb->mls_enabled && !newpolicydb->mls_enabled) printk(KERN_INFO "SELinux: Disabling MLS support...\n"); - else if (!policydb.mls_enabled && newpolicydb->mls_enabled) + else if (!policydb->mls_enabled && newpolicydb->mls_enabled) printk(KERN_INFO "SELinux: Enabling MLS support...\n"); rc = policydb_load_isids(newpolicydb, &newsidtab); @@ -2125,20 +2181,20 @@ int security_load_policy(void *data, size_t len) goto out; } - rc = selinux_set_mapping(newpolicydb, secclass_map, &map, &map_size); + rc = selinux_set_mapping(newpolicydb, secclass_map, &newmap); if (rc) goto err; - rc = security_preserve_bools(newpolicydb); + rc = security_preserve_bools(state, newpolicydb); if (rc) { printk(KERN_ERR "SELinux: unable to preserve booleans\n"); goto err; } /* Clone the SID table. */ - sidtab_shutdown(&sidtab); + sidtab_shutdown(sidtab); - rc = sidtab_map(&sidtab, clone_sid, &newsidtab); + rc = sidtab_map(sidtab, clone_sid, &newsidtab); if (rc) goto err; @@ -2146,7 +2202,8 @@ int security_load_policy(void *data, size_t len) * Convert the internal representations of contexts * in the new SID table. */ - args.oldp = &policydb; + args.state = state; + args.oldp = policydb; args.newp = newpolicydb; rc = sidtab_map(&newsidtab, convert_context, &args); if (rc) { @@ -2157,28 +2214,28 @@ int security_load_policy(void *data, size_t len) } /* Save the old policydb and SID table to free later. */ - memcpy(oldpolicydb, &policydb, sizeof(policydb)); - sidtab_set(&oldsidtab, &sidtab); + memcpy(oldpolicydb, policydb, sizeof(*policydb)); + sidtab_set(&oldsidtab, sidtab); /* Install the new policydb and SID table. */ - write_lock_irq(&policy_rwlock); - memcpy(&policydb, newpolicydb, sizeof(policydb)); - sidtab_set(&sidtab, &newsidtab); - security_load_policycaps(); - oldmap = current_mapping; - current_mapping = map; - current_mapping_size = map_size; - seqno = ++latest_granting; - write_unlock_irq(&policy_rwlock); + write_lock_irq(&state->ss->policy_rwlock); + memcpy(policydb, newpolicydb, sizeof(*policydb)); + sidtab_set(sidtab, &newsidtab); + security_load_policycaps(state); + oldmapping = state->ss->map.mapping; + state->ss->map.mapping = newmap.mapping; + state->ss->map.size = newmap.size; + seqno = ++state->ss->latest_granting; + write_unlock_irq(&state->ss->policy_rwlock); /* Free the old policydb and SID table. */ policydb_destroy(oldpolicydb); sidtab_destroy(&oldsidtab); - kfree(oldmap); + kfree(oldmapping); - avc_ss_reset(seqno); + avc_ss_reset(state->avc, seqno); selnl_notify_policyload(seqno); - selinux_status_update_policyload(seqno); + selinux_status_update_policyload(state, seqno); selinux_netlbl_cache_invalidate(); selinux_xfrm_notify_policyload(); @@ -2186,7 +2243,7 @@ int security_load_policy(void *data, size_t len) goto out; err: - kfree(map); + kfree(newmap.mapping); sidtab_destroy(&newsidtab); policydb_destroy(newpolicydb); @@ -2195,13 +2252,14 @@ out: return rc; } -size_t security_policydb_len(void) +size_t security_policydb_len(struct selinux_state *state) { + struct policydb *p = &state->ss->policydb; size_t len; - read_lock(&policy_rwlock); - len = policydb.len; - read_unlock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + len = p->len; + read_unlock(&state->ss->policy_rwlock); return len; } @@ -2212,14 +2270,20 @@ size_t security_policydb_len(void) * @port: port number * @out_sid: security identifier */ -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) { + struct policydb *policydb; + struct sidtab *sidtab; struct ocontext *c; int rc = 0; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); - c = policydb.ocontexts[OCON_PORT]; + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + c = policydb->ocontexts[OCON_PORT]; while (c) { if (c->u.port.protocol == protocol && c->u.port.low_port <= port && @@ -2230,7 +2294,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid) if (c) { if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, + rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]); if (rc) @@ -2242,7 +2306,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid) } out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -2252,14 +2316,20 @@ out: * @pkey_num: pkey number * @out_sid: security identifier */ -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) { + struct policydb *policydb; + struct sidtab *sidtab; struct ocontext *c; int rc = 0; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; - c = policydb.ocontexts[OCON_IBPKEY]; + c = policydb->ocontexts[OCON_IBPKEY]; while (c) { if (c->u.ibpkey.low_pkey <= pkey_num && c->u.ibpkey.high_pkey >= pkey_num && @@ -2271,7 +2341,7 @@ int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid) if (c) { if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, + rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]); if (rc) @@ -2282,7 +2352,7 @@ int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid) *out_sid = SECINITSID_UNLABELED; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -2292,14 +2362,20 @@ out: * @port: port number * @out_sid: security identifier */ -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) { + struct policydb *policydb; + struct sidtab *sidtab; struct ocontext *c; int rc = 0; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; - c = policydb.ocontexts[OCON_IBENDPORT]; + c = policydb->ocontexts[OCON_IBENDPORT]; while (c) { if (c->u.ibendport.port == port_num && !strncmp(c->u.ibendport.dev_name, @@ -2312,7 +2388,7 @@ int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid) if (c) { if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, + rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]); if (rc) @@ -2323,7 +2399,7 @@ int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid) *out_sid = SECINITSID_UNLABELED; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -2332,14 +2408,20 @@ out: * @name: interface name * @if_sid: interface SID */ -int security_netif_sid(char *name, u32 *if_sid) +int security_netif_sid(struct selinux_state *state, + char *name, u32 *if_sid) { + struct policydb *policydb; + struct sidtab *sidtab; int rc = 0; struct ocontext *c; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); - c = policydb.ocontexts[OCON_NETIF]; + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + c = policydb->ocontexts[OCON_NETIF]; while (c) { if (strcmp(name, c->u.name) == 0) break; @@ -2348,12 +2430,12 @@ int security_netif_sid(char *name, u32 *if_sid) if (c) { if (!c->sid[0] || !c->sid[1]) { - rc = sidtab_context_to_sid(&sidtab, + rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]); if (rc) goto out; - rc = sidtab_context_to_sid(&sidtab, + rc = sidtab_context_to_sid(sidtab, &c->context[1], &c->sid[1]); if (rc) @@ -2364,7 +2446,7 @@ int security_netif_sid(char *name, u32 *if_sid) *if_sid = SECINITSID_NETIF; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -2388,15 +2470,21 @@ static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask) * @addrlen: address length in bytes * @out_sid: security identifier */ -int security_node_sid(u16 domain, +int security_node_sid(struct selinux_state *state, + u16 domain, void *addrp, u32 addrlen, u32 *out_sid) { + struct policydb *policydb; + struct sidtab *sidtab; int rc; struct ocontext *c; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; switch (domain) { case AF_INET: { @@ -2408,7 +2496,7 @@ int security_node_sid(u16 domain, addr = *((u32 *)addrp); - c = policydb.ocontexts[OCON_NODE]; + c = policydb->ocontexts[OCON_NODE]; while (c) { if (c->u.node.addr == (addr & c->u.node.mask)) break; @@ -2421,7 +2509,7 @@ int security_node_sid(u16 domain, rc = -EINVAL; if (addrlen != sizeof(u64) * 2) goto out; - c = policydb.ocontexts[OCON_NODE6]; + c = policydb->ocontexts[OCON_NODE6]; while (c) { if (match_ipv6_addrmask(addrp, c->u.node6.addr, c->u.node6.mask)) @@ -2438,7 +2526,7 @@ int security_node_sid(u16 domain, if (c) { if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, + rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]); if (rc) @@ -2451,7 +2539,7 @@ int security_node_sid(u16 domain, rc = 0; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -2471,11 +2559,14 @@ out: * number of elements in the array. */ -int security_get_user_sids(u32 fromsid, +int security_get_user_sids(struct selinux_state *state, + u32 fromsid, char *username, u32 **sids, u32 *nel) { + struct policydb *policydb; + struct sidtab *sidtab; struct context *fromcon, usercon; u32 *mysids = NULL, *mysids2, sid; u32 mynel = 0, maxnel = SIDS_NEL; @@ -2487,20 +2578,23 @@ int security_get_user_sids(u32 fromsid, *sids = NULL; *nel = 0; - if (!ss_initialized) + if (!state->initialized) goto out; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; context_init(&usercon); rc = -EINVAL; - fromcon = sidtab_search(&sidtab, fromsid); + fromcon = sidtab_search(sidtab, fromsid); if (!fromcon) goto out_unlock; rc = -EINVAL; - user = hashtab_search(policydb.p_users.table, username); + user = hashtab_search(policydb->p_users.table, username); if (!user) goto out_unlock; @@ -2512,15 +2606,16 @@ int security_get_user_sids(u32 fromsid, goto out_unlock; ebitmap_for_each_positive_bit(&user->roles, rnode, i) { - role = policydb.role_val_to_struct[i]; + role = policydb->role_val_to_struct[i]; usercon.role = i + 1; ebitmap_for_each_positive_bit(&role->types, tnode, j) { usercon.type = j + 1; - if (mls_setup_user_range(fromcon, user, &usercon)) + if (mls_setup_user_range(policydb, fromcon, user, + &usercon)) continue; - rc = sidtab_context_to_sid(&sidtab, &usercon, &sid); + rc = sidtab_context_to_sid(sidtab, &usercon, &sid); if (rc) goto out_unlock; if (mynel < maxnel) { @@ -2540,7 +2635,7 @@ int security_get_user_sids(u32 fromsid, } rc = 0; out_unlock: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); if (rc || !mynel) { kfree(mysids); goto out; @@ -2554,7 +2649,8 @@ out_unlock: } for (i = 0, j = 0; i < mynel; i++) { struct av_decision dummy_avd; - rc = avc_has_perm_noaudit(fromsid, mysids[i], + rc = avc_has_perm_noaudit(state, + fromsid, mysids[i], SECCLASS_PROCESS, /* kernel value */ PROCESS__TRANSITION, AVC_STRICT, &dummy_avd); @@ -2583,11 +2679,14 @@ out: * * The caller must acquire the policy_rwlock before calling this function. */ -static inline int __security_genfs_sid(const char *fstype, +static inline int __security_genfs_sid(struct selinux_state *state, + const char *fstype, char *path, u16 orig_sclass, u32 *sid) { + struct policydb *policydb = &state->ss->policydb; + struct sidtab *sidtab = &state->ss->sidtab; int len; u16 sclass; struct genfs *genfs; @@ -2597,10 +2696,10 @@ static inline int __security_genfs_sid(const char *fstype, while (path[0] == '/' && path[1] == '/') path++; - sclass = unmap_class(orig_sclass); + sclass = unmap_class(&state->ss->map, orig_sclass); *sid = SECINITSID_UNLABELED; - for (genfs = policydb.genfs; genfs; genfs = genfs->next) { + for (genfs = policydb->genfs; genfs; genfs = genfs->next) { cmp = strcmp(fstype, genfs->fstype); if (cmp <= 0) break; @@ -2622,7 +2721,7 @@ static inline int __security_genfs_sid(const char *fstype, goto out; if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]); + rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]); if (rc) goto out; } @@ -2643,16 +2742,17 @@ out: * Acquire policy_rwlock before calling __security_genfs_sid() and release * it afterward. */ -int security_genfs_sid(const char *fstype, +int security_genfs_sid(struct selinux_state *state, + const char *fstype, char *path, u16 orig_sclass, u32 *sid) { int retval; - read_lock(&policy_rwlock); - retval = __security_genfs_sid(fstype, path, orig_sclass, sid); - read_unlock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + retval = __security_genfs_sid(state, fstype, path, orig_sclass, sid); + read_unlock(&state->ss->policy_rwlock); return retval; } @@ -2660,16 +2760,21 @@ int security_genfs_sid(const char *fstype, * security_fs_use - Determine how to handle labeling for a filesystem. * @sb: superblock in question */ -int security_fs_use(struct super_block *sb) +int security_fs_use(struct selinux_state *state, struct super_block *sb) { + struct policydb *policydb; + struct sidtab *sidtab; int rc = 0; struct ocontext *c; struct superblock_security_struct *sbsec = sb->s_security; const char *fstype = sb->s_type->name; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); - c = policydb.ocontexts[OCON_FSUSE]; + policydb = &state->ss->policydb; + sidtab = &state->ss->sidtab; + + c = policydb->ocontexts[OCON_FSUSE]; while (c) { if (strcmp(fstype, c->u.name) == 0) break; @@ -2679,14 +2784,14 @@ int security_fs_use(struct super_block *sb) if (c) { sbsec->behavior = c->v.behavior; if (!c->sid[0]) { - rc = sidtab_context_to_sid(&sidtab, &c->context[0], + rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]); if (rc) goto out; } sbsec->sid = c->sid[0]; } else { - rc = __security_genfs_sid(fstype, "/", SECCLASS_DIR, + rc = __security_genfs_sid(state, fstype, "/", SECCLASS_DIR, &sbsec->sid); if (rc) { sbsec->behavior = SECURITY_FS_USE_NONE; @@ -2697,20 +2802,32 @@ int security_fs_use(struct super_block *sb) } out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } -int security_get_bools(int *len, char ***names, int **values) +int security_get_bools(struct selinux_state *state, + int *len, char ***names, int **values) { + struct policydb *policydb; int i, rc; - read_lock(&policy_rwlock); + if (!state->initialized) { + *len = 0; + *names = NULL; + *values = NULL; + return 0; + } + + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; + *names = NULL; *values = NULL; rc = 0; - *len = policydb.p_bools.nprim; + *len = policydb->p_bools.nprim; if (!*len) goto out; @@ -2725,16 +2842,17 @@ int security_get_bools(int *len, char ***names, int **values) goto err; for (i = 0; i < *len; i++) { - (*values)[i] = policydb.bool_val_to_struct[i]->state; + (*values)[i] = policydb->bool_val_to_struct[i]->state; rc = -ENOMEM; - (*names)[i] = kstrdup(sym_name(&policydb, SYM_BOOLS, i), GFP_ATOMIC); + (*names)[i] = kstrdup(sym_name(policydb, SYM_BOOLS, i), + GFP_ATOMIC); if (!(*names)[i]) goto err; } rc = 0; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; err: if (*names) { @@ -2746,90 +2864,98 @@ err: } -int security_set_bools(int len, int *values) +int security_set_bools(struct selinux_state *state, int len, int *values) { + struct policydb *policydb; int i, rc; int lenp, seqno = 0; struct cond_node *cur; - write_lock_irq(&policy_rwlock); + write_lock_irq(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; rc = -EFAULT; - lenp = policydb.p_bools.nprim; + lenp = policydb->p_bools.nprim; if (len != lenp) goto out; for (i = 0; i < len; i++) { - if (!!values[i] != policydb.bool_val_to_struct[i]->state) { + if (!!values[i] != policydb->bool_val_to_struct[i]->state) { audit_log(current->audit_context, GFP_ATOMIC, AUDIT_MAC_CONFIG_CHANGE, "bool=%s val=%d old_val=%d auid=%u ses=%u", - sym_name(&policydb, SYM_BOOLS, i), + sym_name(policydb, SYM_BOOLS, i), !!values[i], - policydb.bool_val_to_struct[i]->state, + policydb->bool_val_to_struct[i]->state, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); } if (values[i]) - policydb.bool_val_to_struct[i]->state = 1; + policydb->bool_val_to_struct[i]->state = 1; else - policydb.bool_val_to_struct[i]->state = 0; + policydb->bool_val_to_struct[i]->state = 0; } - for (cur = policydb.cond_list; cur; cur = cur->next) { - rc = evaluate_cond_node(&policydb, cur); + for (cur = policydb->cond_list; cur; cur = cur->next) { + rc = evaluate_cond_node(policydb, cur); if (rc) goto out; } - seqno = ++latest_granting; + seqno = ++state->ss->latest_granting; rc = 0; out: - write_unlock_irq(&policy_rwlock); + write_unlock_irq(&state->ss->policy_rwlock); if (!rc) { - avc_ss_reset(seqno); + avc_ss_reset(state->avc, seqno); selnl_notify_policyload(seqno); - selinux_status_update_policyload(seqno); + selinux_status_update_policyload(state, seqno); selinux_xfrm_notify_policyload(); } return rc; } -int security_get_bool_value(int index) +int security_get_bool_value(struct selinux_state *state, + int index) { + struct policydb *policydb; int rc; int len; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + + policydb = &state->ss->policydb; rc = -EFAULT; - len = policydb.p_bools.nprim; + len = policydb->p_bools.nprim; if (index >= len) goto out; - rc = policydb.bool_val_to_struct[index]->state; + rc = policydb->bool_val_to_struct[index]->state; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } -static int security_preserve_bools(struct policydb *p) +static int security_preserve_bools(struct selinux_state *state, + struct policydb *policydb) { int rc, nbools = 0, *bvalues = NULL, i; char **bnames = NULL; struct cond_bool_datum *booldatum; struct cond_node *cur; - rc = security_get_bools(&nbools, &bnames, &bvalues); + rc = security_get_bools(state, &nbools, &bnames, &bvalues); if (rc) goto out; for (i = 0; i < nbools; i++) { - booldatum = hashtab_search(p->p_bools.table, bnames[i]); + booldatum = hashtab_search(policydb->p_bools.table, bnames[i]); if (booldatum) booldatum->state = bvalues[i]; } - for (cur = p->cond_list; cur; cur = cur->next) { - rc = evaluate_cond_node(p, cur); + for (cur = policydb->cond_list; cur; cur = cur->next) { + rc = evaluate_cond_node(policydb, cur); if (rc) goto out; } @@ -2848,8 +2974,11 @@ out: * security_sid_mls_copy() - computes a new sid based on the given * sid and the mls portion of mls_sid. */ -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) { + struct policydb *policydb = &state->ss->policydb; + struct sidtab *sidtab = &state->ss->sidtab; struct context *context1; struct context *context2; struct context newcon; @@ -2858,17 +2987,17 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) int rc; rc = 0; - if (!ss_initialized || !policydb.mls_enabled) { + if (!state->initialized || !policydb->mls_enabled) { *new_sid = sid; goto out; } context_init(&newcon); - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); rc = -EINVAL; - context1 = sidtab_search(&sidtab, sid); + context1 = sidtab_search(sidtab, sid); if (!context1) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, sid); @@ -2876,7 +3005,7 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) } rc = -EINVAL; - context2 = sidtab_search(&sidtab, mls_sid); + context2 = sidtab_search(sidtab, mls_sid); if (!context2) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, mls_sid); @@ -2891,10 +3020,11 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) goto out_unlock; /* Check the validity of the new context. */ - if (!policydb_context_isvalid(&policydb, &newcon)) { - rc = convert_context_handle_invalid_context(&newcon); + if (!policydb_context_isvalid(policydb, &newcon)) { + rc = convert_context_handle_invalid_context(state, &newcon); if (rc) { - if (!context_struct_to_string(&newcon, &s, &len)) { + if (!context_struct_to_string(policydb, &newcon, &s, + &len)) { audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_sid_mls_copy " @@ -2905,9 +3035,9 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) } } - rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid); + rc = sidtab_context_to_sid(sidtab, &newcon, new_sid); out_unlock: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); context_destroy(&newcon); out: return rc; @@ -2933,10 +3063,13 @@ out: * multiple, inconsistent labels | -<errno> | SECSID_NULL * */ -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) { + struct policydb *policydb = &state->ss->policydb; + struct sidtab *sidtab = &state->ss->sidtab; int rc; struct context *nlbl_ctx; struct context *xfrm_ctx; @@ -2958,23 +3091,25 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, return 0; } - /* we don't need to check ss_initialized here since the only way both + /* + * We don't need to check initialized here since the only way both * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the - * security server was initialized and ss_initialized was true */ - if (!policydb.mls_enabled) + * security server was initialized and state->initialized was true. + */ + if (!policydb->mls_enabled) return 0; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); rc = -EINVAL; - nlbl_ctx = sidtab_search(&sidtab, nlbl_sid); + nlbl_ctx = sidtab_search(sidtab, nlbl_sid); if (!nlbl_ctx) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, nlbl_sid); goto out; } rc = -EINVAL; - xfrm_ctx = sidtab_search(&sidtab, xfrm_sid); + xfrm_ctx = sidtab_search(sidtab, xfrm_sid); if (!xfrm_ctx) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, xfrm_sid); @@ -2991,7 +3126,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, * expressive */ *peer_sid = xfrm_sid; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -3008,19 +3143,27 @@ static int get_classes_callback(void *k, void *d, void *args) return 0; } -int security_get_classes(char ***classes, int *nclasses) +int security_get_classes(struct selinux_state *state, + char ***classes, int *nclasses) { + struct policydb *policydb = &state->ss->policydb; int rc; - read_lock(&policy_rwlock); + if (!state->initialized) { + *nclasses = 0; + *classes = NULL; + return 0; + } + + read_lock(&state->ss->policy_rwlock); rc = -ENOMEM; - *nclasses = policydb.p_classes.nprim; + *nclasses = policydb->p_classes.nprim; *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC); if (!*classes) goto out; - rc = hashtab_map(policydb.p_classes.table, get_classes_callback, + rc = hashtab_map(policydb->p_classes.table, get_classes_callback, *classes); if (rc) { int i; @@ -3030,7 +3173,7 @@ int security_get_classes(char ***classes, int *nclasses) } out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -3047,15 +3190,17 @@ static int get_permissions_callback(void *k, void *d, void *args) return 0; } -int security_get_permissions(char *class, char ***perms, int *nperms) +int security_get_permissions(struct selinux_state *state, + char *class, char ***perms, int *nperms) { + struct policydb *policydb = &state->ss->policydb; int rc, i; struct class_datum *match; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); rc = -EINVAL; - match = hashtab_search(policydb.p_classes.table, class); + match = hashtab_search(policydb->p_classes.table, class); if (!match) { printk(KERN_ERR "SELinux: %s: unrecognized class %s\n", __func__, class); @@ -3081,25 +3226,25 @@ int security_get_permissions(char *class, char ***perms, int *nperms) goto err; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; err: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); for (i = 0; i < *nperms; i++) kfree((*perms)[i]); kfree(*perms); return rc; } -int security_get_reject_unknown(void) +int security_get_reject_unknown(struct selinux_state *state) { - return policydb.reject_unknown; + return state->ss->policydb.reject_unknown; } -int security_get_allow_unknown(void) +int security_get_allow_unknown(struct selinux_state *state) { - return policydb.allow_unknown; + return state->ss->policydb.allow_unknown; } /** @@ -3112,13 +3257,15 @@ int security_get_allow_unknown(void) * supported, false (0) if it isn't supported. * */ -int security_policycap_supported(unsigned int req_cap) +int security_policycap_supported(struct selinux_state *state, + unsigned int req_cap) { + struct policydb *policydb = &state->ss->policydb; int rc; - read_lock(&policy_rwlock); - rc = ebitmap_get_bit(&policydb.policycaps, req_cap); - read_unlock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + rc = ebitmap_get_bit(&policydb->policycaps, req_cap); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -3140,6 +3287,8 @@ void selinux_audit_rule_free(void *vrule) int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) { + struct selinux_state *state = &selinux_state; + struct policydb *policydb = &state->ss->policydb; struct selinux_audit_rule *tmprule; struct role_datum *roledatum; struct type_datum *typedatum; @@ -3149,7 +3298,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) *rule = NULL; - if (!ss_initialized) + if (!state->initialized) return -EOPNOTSUPP; switch (field) { @@ -3182,15 +3331,15 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) context_init(&tmprule->au_ctxt); - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); - tmprule->au_seqno = latest_granting; + tmprule->au_seqno = state->ss->latest_granting; switch (field) { case AUDIT_SUBJ_USER: case AUDIT_OBJ_USER: rc = -EINVAL; - userdatum = hashtab_search(policydb.p_users.table, rulestr); + userdatum = hashtab_search(policydb->p_users.table, rulestr); if (!userdatum) goto out; tmprule->au_ctxt.user = userdatum->value; @@ -3198,7 +3347,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) case AUDIT_SUBJ_ROLE: case AUDIT_OBJ_ROLE: rc = -EINVAL; - roledatum = hashtab_search(policydb.p_roles.table, rulestr); + roledatum = hashtab_search(policydb->p_roles.table, rulestr); if (!roledatum) goto out; tmprule->au_ctxt.role = roledatum->value; @@ -3206,7 +3355,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) case AUDIT_SUBJ_TYPE: case AUDIT_OBJ_TYPE: rc = -EINVAL; - typedatum = hashtab_search(policydb.p_types.table, rulestr); + typedatum = hashtab_search(policydb->p_types.table, rulestr); if (!typedatum) goto out; tmprule->au_ctxt.type = typedatum->value; @@ -3215,14 +3364,15 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) case AUDIT_SUBJ_CLR: case AUDIT_OBJ_LEV_LOW: case AUDIT_OBJ_LEV_HIGH: - rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC); + rc = mls_from_string(policydb, rulestr, &tmprule->au_ctxt, + GFP_ATOMIC); if (rc) goto out; break; } rc = 0; out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); if (rc) { selinux_audit_rule_free(tmprule); @@ -3262,6 +3412,7 @@ int selinux_audit_rule_known(struct audit_krule *rule) int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, struct audit_context *actx) { + struct selinux_state *state = &selinux_state; struct context *ctxt; struct mls_level *level; struct selinux_audit_rule *rule = vrule; @@ -3272,14 +3423,14 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, return -ENOENT; } - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); - if (rule->au_seqno < latest_granting) { + if (rule->au_seqno < state->ss->latest_granting) { match = -ESTALE; goto out; } - ctxt = sidtab_search(&sidtab, sid); + ctxt = sidtab_search(&state->ss->sidtab, sid); if (unlikely(!ctxt)) { WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", sid); @@ -3363,7 +3514,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, } out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return match; } @@ -3437,19 +3588,22 @@ static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr, * failure. * */ -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) { + struct policydb *policydb = &state->ss->policydb; + struct sidtab *sidtab = &state->ss->sidtab; int rc; struct context *ctx; struct context ctx_new; - if (!ss_initialized) { + if (!state->initialized) { *sid = SECSID_NULL; return 0; } - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); if (secattr->flags & NETLBL_SECATTR_CACHE) *sid = *(u32 *)secattr->cache->data; @@ -3457,7 +3611,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, *sid = secattr->attr.secid; else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { rc = -EIDRM; - ctx = sidtab_search(&sidtab, SECINITSID_NETMSG); + ctx = sidtab_search(sidtab, SECINITSID_NETMSG); if (ctx == NULL) goto out; @@ -3465,17 +3619,17 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, ctx_new.user = ctx->user; ctx_new.role = ctx->role; ctx_new.type = ctx->type; - mls_import_netlbl_lvl(&ctx_new, secattr); + mls_import_netlbl_lvl(policydb, &ctx_new, secattr); if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { - rc = mls_import_netlbl_cat(&ctx_new, secattr); + rc = mls_import_netlbl_cat(policydb, &ctx_new, secattr); if (rc) goto out; } rc = -EIDRM; - if (!mls_context_isvalid(&policydb, &ctx_new)) + if (!mls_context_isvalid(policydb, &ctx_new)) goto out_free; - rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid); + rc = sidtab_context_to_sid(sidtab, &ctx_new, sid); if (rc) goto out_free; @@ -3485,12 +3639,12 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, } else *sid = SECSID_NULL; - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return 0; out_free: ebitmap_destroy(&ctx_new.range.level[0].cat); out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } @@ -3504,33 +3658,35 @@ out: * Returns zero on success, negative values on failure. * */ -int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) +int security_netlbl_sid_to_secattr(struct selinux_state *state, + u32 sid, struct netlbl_lsm_secattr *secattr) { + struct policydb *policydb = &state->ss->policydb; int rc; struct context *ctx; - if (!ss_initialized) + if (!state->initialized) return 0; - read_lock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); rc = -ENOENT; - ctx = sidtab_search(&sidtab, sid); + ctx = sidtab_search(&state->ss->sidtab, sid); if (ctx == NULL) goto out; rc = -ENOMEM; - secattr->domain = kstrdup(sym_name(&policydb, SYM_TYPES, ctx->type - 1), + secattr->domain = kstrdup(sym_name(policydb, SYM_TYPES, ctx->type - 1), GFP_ATOMIC); if (secattr->domain == NULL) goto out; secattr->attr.secid = sid; secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID; - mls_export_netlbl_lvl(ctx, secattr); - rc = mls_export_netlbl_cat(ctx, secattr); + mls_export_netlbl_lvl(policydb, ctx, secattr); + rc = mls_export_netlbl_cat(policydb, ctx, secattr); out: - read_unlock(&policy_rwlock); + read_unlock(&state->ss->policy_rwlock); return rc; } #endif /* CONFIG_NETLABEL */ @@ -3541,15 +3697,17 @@ out: * @len: length of data in bytes * */ -int security_read_policy(void **data, size_t *len) +int security_read_policy(struct selinux_state *state, + void **data, size_t *len) { + struct policydb *policydb = &state->ss->policydb; int rc; struct policy_file fp; - if (!ss_initialized) + if (!state->initialized) return -EINVAL; - *len = security_policydb_len(); + *len = security_policydb_len(state); *data = vmalloc_user(*len); if (!*data) @@ -3558,9 +3716,9 @@ int security_read_policy(void **data, size_t *len) fp.data = *data; fp.len = *len; - read_lock(&policy_rwlock); - rc = policydb_write(&policydb, &fp); - read_unlock(&policy_rwlock); + read_lock(&state->ss->policy_rwlock); + rc = policydb_write(policydb, &fp); + read_unlock(&state->ss->policy_rwlock); if (rc) return rc; diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h index 356bdd36cf6d..24c7bdcc8075 100644 --- a/security/selinux/ss/services.h +++ b/security/selinux/ss/services.h @@ -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_ */ - diff --git a/security/selinux/ss/status.c b/security/selinux/ss/status.c index d982365f9d1a..a121de45ac0e 100644 --- a/security/selinux/ss/status.c +++ b/security/selinux/ss/status.c @@ -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); } diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 928188902901..91dc3783ed94 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -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); } |