mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 04:34:08 +08:00
selinux/stable-5.10 PR 20201012
-----BEGIN PGP SIGNATURE----- iQJIBAABCAAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAl+E9UoUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXMG2BAApHLKLsfH5gf7gZNjHmQxddg8maCl BGt7K1xc9iYBZN56Cbc7v9uKc5pM+UOoOlVmWh+8jaROpX10jJmvhsebQzpcWEEs O/BDg/Y/AafoLr5e7gbAnlA7TJXNSR9MG9RB7c9xC14LG/bqBmkaUNsv8isWlLgl J2atHLsdlvCbmqJvnc6Fh3VJCbY/I0kt9L04GBQ4pEK3TKOxtORQaQcjVgLhlcw9 YdMPKYIwy2Ze2HUuyW2o9OuryHhoMrwxpN/35/PAxrRwpO0LVnjjiw6njQqYVGH3 el8mPXlhHah/7QUKcngSsvcvUcaSencp9sUBrp1vK9C1vkSFyubZweVi4A2TEWnh Ctceje7XP/YWDcJ+5BgASvosQdqOBB7huuOOKVpvaBXqgUHFgaxphV4/FDNnlF62 AteX5RcWb/JiFJ4YnbknPNa/MWxVYuVn78AlNsM2ZponWYWs9JZ17lX4tHAKF1Qm x6ZMvMCDJTj8622l8nw3dTZKNDE3nFblDThX8aSrAhCQQE6HvugbKU4Fzo1oiSPl 84PlCPgb+3tP3OsvZDIOPCJxC6IHgS+meA0IjhjwuCb+U+YWaAIeOlOPSkxUmfLu iJVWHmDtsAM3bTBxwQudhgXF3a1oKCEqeqNxM6P6p55jti7xal9FnZNHTbSh2sO1 Km4oIqTEb1XWNdU= =NNLw -----END PGP SIGNATURE----- Merge tag 'selinux-pr-20201012' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux Pull selinux updates from Paul Moore: "A decent number of SELinux patches for v5.10, twenty two in total. The highlights are listed below, but all of the patches pass our test suite and merge cleanly. - A number of changes to how the SELinux policy is loaded and managed inside the kernel with the goal of improving the atomicity of a SELinux policy load operation. These changes account for the bulk of the diffstat as well as the patch count. A special thanks to everyone who contributed patches and fixes for this work. - Convert the SELinux policy read-write lock to RCU. - A tracepoint was added for audited SELinux access control events; this should help provide a more unified backtrace across kernel and userspace. - Allow the removal of security.selinux xattrs when a SELinux policy is not loaded. - Enable policy capabilities in SELinux policies created with the scripts/selinux/mdp tool. - Provide some "no sooner than" dates for the SELinux checkreqprot sysfs deprecation" * tag 'selinux-pr-20201012' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: (22 commits) selinux: provide a "no sooner than" date for the checkreqprot removal selinux: Add helper functions to get and set checkreqprot selinux: access policycaps with READ_ONCE/WRITE_ONCE selinux: simplify away security_policydb_len() selinux: move policy mutex to selinux_state, use in lockdep checks selinux: fix error handling bugs in security_load_policy() selinux: convert policy read-write lock to RCU selinux: delete repeated words in comments selinux: add basic filtering for audit trace events selinux: add tracepoint on audited events selinux: Create new booleans and class dirs out of tree selinux: Standardize string literal usage for selinuxfs directory names selinux: Refactor selinuxfs directory populating functions selinux: Create function for selinuxfs directory cleanup selinux: permit removing security.selinux xattr before policy load selinux: fix memdup.cocci warnings selinux: avoid dereferencing the policy prior to initialization selinux: fix allocation failure check on newpolicy->sidtab selinux: refactor changing booleans selinux: move policy commit after updating selinuxfs ...
This commit is contained in:
commit
7b540812cc
@ -15,7 +15,7 @@ Description:
|
||||
actual protection), and Android and Linux distributions have been
|
||||
explicitly writing a "0" to /sys/fs/selinux/checkreqprot during
|
||||
initialization for some time. Support for setting checkreqprot to 1
|
||||
will be removed in a future kernel release, at which point the kernel
|
||||
will be removed no sooner than June 2021, at which point the kernel
|
||||
will always cease using checkreqprot internally and will always
|
||||
check the actual protections being applied upon mmap/mprotect calls.
|
||||
The checkreqprot selinuxfs node will remain for backward compatibility
|
||||
|
@ -15621,6 +15621,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git
|
||||
F: Documentation/ABI/obsolete/sysfs-selinux-checkreqprot
|
||||
F: Documentation/ABI/obsolete/sysfs-selinux-disable
|
||||
F: Documentation/admin-guide/LSM/SELinux.rst
|
||||
F: include/trace/events/avc.h
|
||||
F: include/uapi/linux/selinux_netlink.h
|
||||
F: scripts/selinux/
|
||||
F: security/selinux/
|
||||
|
53
include/trace/events/avc.h
Normal file
53
include/trace/events/avc.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Authors: Thiébaud Weksteen <tweek@google.com>
|
||||
* Peter Enderborg <Peter.Enderborg@sony.com>
|
||||
*/
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM avc
|
||||
|
||||
#if !defined(_TRACE_SELINUX_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_SELINUX_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(selinux_audited,
|
||||
|
||||
TP_PROTO(struct selinux_audit_data *sad,
|
||||
char *scontext,
|
||||
char *tcontext,
|
||||
const char *tclass
|
||||
),
|
||||
|
||||
TP_ARGS(sad, scontext, tcontext, tclass),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, requested)
|
||||
__field(u32, denied)
|
||||
__field(u32, audited)
|
||||
__field(int, result)
|
||||
__string(scontext, scontext)
|
||||
__string(tcontext, tcontext)
|
||||
__string(tclass, tclass)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->requested = sad->requested;
|
||||
__entry->denied = sad->denied;
|
||||
__entry->audited = sad->audited;
|
||||
__entry->result = sad->result;
|
||||
__assign_str(tcontext, tcontext);
|
||||
__assign_str(scontext, scontext);
|
||||
__assign_str(tclass, tclass);
|
||||
),
|
||||
|
||||
TP_printk("requested=0x%x denied=0x%x audited=0x%x result=%d scontext=%s tcontext=%s tclass=%s",
|
||||
__entry->requested, __entry->denied, __entry->audited, __entry->result,
|
||||
__get_str(scontext), __get_str(tcontext), __get_str(tclass)
|
||||
)
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
@ -35,6 +35,9 @@ struct security_class_mapping {
|
||||
|
||||
#include "classmap.h"
|
||||
#include "initial_sid_to_string.h"
|
||||
#include "policycap_names.h"
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -115,6 +118,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
/* enable all policy capabilities */
|
||||
for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++)
|
||||
fprintf(fout, "policycap %s;\n", selinux_policycap_names[i]);
|
||||
|
||||
/* types, roles, and allows */
|
||||
fprintf(fout, "type base_t;\n");
|
||||
fprintf(fout, "role base_r;\n");
|
||||
|
@ -31,6 +31,9 @@
|
||||
#include "avc_ss.h"
|
||||
#include "classmap.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/avc.h>
|
||||
|
||||
#define AVC_CACHE_SLOTS 512
|
||||
#define AVC_DEF_CACHE_THRESHOLD 512
|
||||
#define AVC_CACHE_RECLAIM 16
|
||||
@ -702,33 +705,37 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
|
||||
{
|
||||
struct common_audit_data *ad = a;
|
||||
struct selinux_audit_data *sad = ad->selinux_audit_data;
|
||||
char *scontext;
|
||||
char *scontext = NULL;
|
||||
char *tcontext = NULL;
|
||||
const char *tclass = NULL;
|
||||
u32 scontext_len;
|
||||
u32 tcontext_len;
|
||||
int rc;
|
||||
|
||||
rc = security_sid_to_context(sad->state, sad->ssid, &scontext,
|
||||
&scontext_len);
|
||||
if (rc)
|
||||
audit_log_format(ab, " ssid=%d", sad->ssid);
|
||||
else {
|
||||
else
|
||||
audit_log_format(ab, " scontext=%s", scontext);
|
||||
kfree(scontext);
|
||||
}
|
||||
|
||||
rc = security_sid_to_context(sad->state, sad->tsid, &scontext,
|
||||
&scontext_len);
|
||||
rc = security_sid_to_context(sad->state, sad->tsid, &tcontext,
|
||||
&tcontext_len);
|
||||
if (rc)
|
||||
audit_log_format(ab, " tsid=%d", sad->tsid);
|
||||
else {
|
||||
audit_log_format(ab, " tcontext=%s", scontext);
|
||||
kfree(scontext);
|
||||
}
|
||||
else
|
||||
audit_log_format(ab, " tcontext=%s", tcontext);
|
||||
|
||||
audit_log_format(ab, " tclass=%s", secclass_map[sad->tclass-1].name);
|
||||
tclass = secclass_map[sad->tclass-1].name;
|
||||
audit_log_format(ab, " tclass=%s", tclass);
|
||||
|
||||
if (sad->denied)
|
||||
audit_log_format(ab, " permissive=%u", sad->result ? 0 : 1);
|
||||
|
||||
trace_selinux_audited(sad, scontext, tcontext, tclass);
|
||||
kfree(tcontext);
|
||||
kfree(scontext);
|
||||
|
||||
/* in case of invalid context report also the actual context string */
|
||||
rc = security_sid_to_context_inval(sad->state, sad->ssid, &scontext,
|
||||
&scontext_len);
|
||||
|
@ -1978,7 +1978,7 @@ static inline u32 file_to_av(struct file *file)
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a file to an access vector and include the correct open
|
||||
* Convert a file to an access vector and include the correct
|
||||
* open permission.
|
||||
*/
|
||||
static inline u32 open_file_to_av(struct file *file)
|
||||
@ -3271,6 +3271,9 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
|
||||
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
|
||||
}
|
||||
|
||||
if (!selinux_initialized(&selinux_state))
|
||||
return 0;
|
||||
|
||||
/* No one is allowed to remove a SELinux security label.
|
||||
You can change the label, but all data must be labeled. */
|
||||
return -EACCES;
|
||||
@ -3709,7 +3712,7 @@ static int selinux_mmap_file(struct file *file, unsigned long reqprot,
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (selinux_state.checkreqprot)
|
||||
if (checkreqprot_get(&selinux_state))
|
||||
prot = reqprot;
|
||||
|
||||
return file_map_prot_check(file, prot,
|
||||
@ -3723,7 +3726,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
||||
const struct cred *cred = current_cred();
|
||||
u32 sid = cred_sid(cred);
|
||||
|
||||
if (selinux_state.checkreqprot)
|
||||
if (checkreqprot_get(&selinux_state))
|
||||
prot = reqprot;
|
||||
|
||||
if (default_noexec &&
|
||||
@ -4438,7 +4441,7 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
|
||||
*
|
||||
* If @skb_sid is valid then the user:role:type information from @sk_sid is
|
||||
* combined with the MLS information from @skb_sid in order to create
|
||||
* @conn_sid. If @skb_sid is not valid then then @conn_sid is simply a copy
|
||||
* @conn_sid. If @skb_sid is not valid then @conn_sid is simply a copy
|
||||
* of @sk_sid. Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
@ -5308,7 +5311,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
|
||||
|
||||
/* As selinux_sctp_bind_connect() is called by the
|
||||
* SCTP protocol layer, the socket is already locked,
|
||||
* therefore selinux_netlbl_socket_connect_locked() is
|
||||
* therefore selinux_netlbl_socket_connect_locked()
|
||||
* 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
|
||||
@ -7225,10 +7228,10 @@ static __init int selinux_init(void)
|
||||
|
||||
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);
|
||||
checkreqprot_set(&selinux_state, selinux_checkreqprot_boot);
|
||||
selinux_avc_init(&selinux_state.avc);
|
||||
mutex_init(&selinux_state.status_lock);
|
||||
mutex_init(&selinux_state.policy_mutex);
|
||||
|
||||
/* Set the security state for the initial task. */
|
||||
cred_init_security();
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include "security.h"
|
||||
|
||||
int security_get_bools(struct selinux_state *state,
|
||||
int security_get_bools(struct selinux_policy *policy,
|
||||
u32 *len, char ***names, int **values);
|
||||
|
||||
int security_set_bools(struct selinux_state *state, u32 len, int *values);
|
||||
|
20
security/selinux/include/policycap.h
Normal file
20
security/selinux/include/policycap.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _SELINUX_POLICYCAP_H_
|
||||
#define _SELINUX_POLICYCAP_H_
|
||||
|
||||
/* Policy capabilities */
|
||||
enum {
|
||||
POLICYDB_CAPABILITY_NETPEER,
|
||||
POLICYDB_CAPABILITY_OPENPERM,
|
||||
POLICYDB_CAPABILITY_EXTSOCKCLASS,
|
||||
POLICYDB_CAPABILITY_ALWAYSNETWORK,
|
||||
POLICYDB_CAPABILITY_CGROUPSECLABEL,
|
||||
POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION,
|
||||
POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS,
|
||||
__POLICYDB_CAPABILITY_MAX
|
||||
};
|
||||
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
|
||||
|
||||
extern const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
|
||||
|
||||
#endif /* _SELINUX_POLICYCAP_H_ */
|
18
security/selinux/include/policycap_names.h
Normal file
18
security/selinux/include/policycap_names.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _SELINUX_POLICYCAP_NAMES_H_
|
||||
#define _SELINUX_POLICYCAP_NAMES_H_
|
||||
|
||||
#include "policycap.h"
|
||||
|
||||
/* Policy capability names */
|
||||
const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
|
||||
"network_peer_controls",
|
||||
"open_perms",
|
||||
"extended_socket_class",
|
||||
"always_check_network",
|
||||
"cgroup_seclabel",
|
||||
"nnp_nosuid_transition",
|
||||
"genfs_seclabel_symlinks"
|
||||
};
|
||||
|
||||
#endif /* _SELINUX_POLICYCAP_NAMES_H_ */
|
@ -13,9 +13,11 @@
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "flask.h"
|
||||
#include "policycap.h"
|
||||
|
||||
#define SECSID_NULL 0x00000000 /* unspecified SID */
|
||||
#define SECSID_WILD 0xffffffff /* wildcard SID */
|
||||
@ -72,21 +74,6 @@ struct netlbl_lsm_secattr;
|
||||
|
||||
extern int selinux_enabled_boot;
|
||||
|
||||
/* Policy capabilities */
|
||||
enum {
|
||||
POLICYDB_CAPABILITY_NETPEER,
|
||||
POLICYDB_CAPABILITY_OPENPERM,
|
||||
POLICYDB_CAPABILITY_EXTSOCKCLASS,
|
||||
POLICYDB_CAPABILITY_ALWAYSNETWORK,
|
||||
POLICYDB_CAPABILITY_CGROUPSECLABEL,
|
||||
POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION,
|
||||
POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS,
|
||||
__POLICYDB_CAPABILITY_MAX
|
||||
};
|
||||
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
|
||||
|
||||
extern const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
|
||||
|
||||
/*
|
||||
* type_datum properties
|
||||
* available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY
|
||||
@ -98,7 +85,7 @@ extern const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
|
||||
#define POLICYDB_BOUNDS_MAXDEPTH 4
|
||||
|
||||
struct selinux_avc;
|
||||
struct selinux_ss;
|
||||
struct selinux_policy;
|
||||
|
||||
struct selinux_state {
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
@ -115,10 +102,10 @@ struct selinux_state {
|
||||
struct mutex status_lock;
|
||||
|
||||
struct selinux_avc *avc;
|
||||
struct selinux_ss *ss;
|
||||
struct selinux_policy __rcu *policy;
|
||||
struct mutex policy_mutex;
|
||||
} __randomize_layout;
|
||||
|
||||
void selinux_ss_init(struct selinux_ss **ss);
|
||||
void selinux_avc_init(struct selinux_avc **avc);
|
||||
|
||||
extern struct selinux_state selinux_state;
|
||||
@ -156,6 +143,16 @@ static inline void enforcing_set(struct selinux_state *state, bool value)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool checkreqprot_get(const struct selinux_state *state)
|
||||
{
|
||||
return READ_ONCE(state->checkreqprot);
|
||||
}
|
||||
|
||||
static inline void checkreqprot_set(struct selinux_state *state, bool value)
|
||||
{
|
||||
WRITE_ONCE(state->checkreqprot, value);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
static inline bool selinux_disabled(struct selinux_state *state)
|
||||
{
|
||||
@ -177,57 +174,61 @@ static inline bool selinux_policycap_netpeer(void)
|
||||
{
|
||||
struct selinux_state *state = &selinux_state;
|
||||
|
||||
return state->policycap[POLICYDB_CAPABILITY_NETPEER];
|
||||
return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_NETPEER]);
|
||||
}
|
||||
|
||||
static inline bool selinux_policycap_openperm(void)
|
||||
{
|
||||
struct selinux_state *state = &selinux_state;
|
||||
|
||||
return state->policycap[POLICYDB_CAPABILITY_OPENPERM];
|
||||
return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_OPENPERM]);
|
||||
}
|
||||
|
||||
static inline bool selinux_policycap_extsockclass(void)
|
||||
{
|
||||
struct selinux_state *state = &selinux_state;
|
||||
|
||||
return state->policycap[POLICYDB_CAPABILITY_EXTSOCKCLASS];
|
||||
return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_EXTSOCKCLASS]);
|
||||
}
|
||||
|
||||
static inline bool selinux_policycap_alwaysnetwork(void)
|
||||
{
|
||||
struct selinux_state *state = &selinux_state;
|
||||
|
||||
return state->policycap[POLICYDB_CAPABILITY_ALWAYSNETWORK];
|
||||
return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_ALWAYSNETWORK]);
|
||||
}
|
||||
|
||||
static inline bool selinux_policycap_cgroupseclabel(void)
|
||||
{
|
||||
struct selinux_state *state = &selinux_state;
|
||||
|
||||
return state->policycap[POLICYDB_CAPABILITY_CGROUPSECLABEL];
|
||||
return READ_ONCE(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];
|
||||
return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION]);
|
||||
}
|
||||
|
||||
static inline bool selinux_policycap_genfs_seclabel_symlinks(void)
|
||||
{
|
||||
struct selinux_state *state = &selinux_state;
|
||||
|
||||
return state->policycap[POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS];
|
||||
return READ_ONCE(state->policycap[POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS]);
|
||||
}
|
||||
|
||||
int security_mls_enabled(struct selinux_state *state);
|
||||
int security_load_policy(struct selinux_state *state,
|
||||
void *data, size_t len);
|
||||
void *data, size_t len,
|
||||
struct selinux_policy **newpolicyp);
|
||||
void selinux_policy_commit(struct selinux_state *state,
|
||||
struct selinux_policy *newpolicy);
|
||||
void selinux_policy_cancel(struct selinux_state *state,
|
||||
struct selinux_policy *policy);
|
||||
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);
|
||||
@ -358,9 +359,9 @@ int security_net_peersid_resolve(struct selinux_state *state,
|
||||
u32 xfrm_sid,
|
||||
u32 *peer_sid);
|
||||
|
||||
int security_get_classes(struct selinux_state *state,
|
||||
int security_get_classes(struct selinux_policy *policy,
|
||||
char ***classes, int *nclasses);
|
||||
int security_get_permissions(struct selinux_state *state,
|
||||
int security_get_permissions(struct selinux_policy *policy,
|
||||
char *class, char ***perms, int *nperms);
|
||||
int security_get_reject_unknown(struct selinux_state *state);
|
||||
int security_get_allow_unknown(struct selinux_state *state);
|
||||
@ -380,6 +381,10 @@ int security_genfs_sid(struct selinux_state *state,
|
||||
const char *fstype, char *name, u16 sclass,
|
||||
u32 *sid);
|
||||
|
||||
int selinux_policy_genfs_sid(struct selinux_policy *policy,
|
||||
const char *fstype, char *name, u16 sclass,
|
||||
u32 *sid);
|
||||
|
||||
#ifdef CONFIG_NETLABEL
|
||||
int security_netlbl_secattr_to_sid(struct selinux_state *state,
|
||||
struct netlbl_lsm_secattr *secattr,
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/security.h>
|
||||
@ -74,7 +75,6 @@ struct selinux_fs_info {
|
||||
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;
|
||||
@ -88,7 +88,6 @@ static int selinux_fs_info_create(struct super_block *sb)
|
||||
if (!fsi)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&fsi->mutex);
|
||||
fsi->last_ino = SEL_INO_NEXT - 1;
|
||||
fsi->state = &selinux_state;
|
||||
fsi->sb = sb;
|
||||
@ -117,6 +116,10 @@ static void selinux_fs_info_free(struct super_block *sb)
|
||||
#define SEL_POLICYCAP_INO_OFFSET 0x08000000
|
||||
#define SEL_INO_MASK 0x00ffffff
|
||||
|
||||
#define BOOL_DIR_NAME "booleans"
|
||||
#define CLASS_DIR_NAME "class"
|
||||
#define POLICYCAP_DIR_NAME "policy_capabilities"
|
||||
|
||||
#define TMPBUFLEN 12
|
||||
static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
@ -346,14 +349,24 @@ static const struct file_operations sel_policyvers_ops = {
|
||||
};
|
||||
|
||||
/* declaration for sel_write_load */
|
||||
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);
|
||||
static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir,
|
||||
unsigned int *bool_num, char ***bool_pending_names,
|
||||
unsigned int **bool_pending_values);
|
||||
static int sel_make_classes(struct selinux_policy *newpolicy,
|
||||
struct dentry *class_dir,
|
||||
unsigned long *last_class_ino);
|
||||
|
||||
/* declaration for sel_make_class_dirs */
|
||||
static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
|
||||
unsigned long *ino);
|
||||
|
||||
/* declaration for sel_make_policy_nodes */
|
||||
static struct dentry *sel_make_disconnected_dir(struct super_block *sb,
|
||||
unsigned long *ino);
|
||||
|
||||
/* declaration for sel_make_policy_nodes */
|
||||
static void sel_remove_entries(struct dentry *de);
|
||||
|
||||
static ssize_t sel_read_mls(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -385,7 +398,7 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
|
||||
|
||||
BUG_ON(filp->private_data);
|
||||
|
||||
mutex_lock(&fsi->mutex);
|
||||
mutex_lock(&fsi->state->policy_mutex);
|
||||
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
current_sid(), SECINITSID_SECURITY,
|
||||
@ -402,25 +415,25 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
|
||||
if (!plm)
|
||||
goto err;
|
||||
|
||||
if (i_size_read(inode) != security_policydb_len(state)) {
|
||||
inode_lock(inode);
|
||||
i_size_write(inode, security_policydb_len(state));
|
||||
inode_unlock(inode);
|
||||
}
|
||||
|
||||
rc = security_read_policy(state, &plm->data, &plm->len);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
if ((size_t)i_size_read(inode) != plm->len) {
|
||||
inode_lock(inode);
|
||||
i_size_write(inode, plm->len);
|
||||
inode_unlock(inode);
|
||||
}
|
||||
|
||||
fsi->policy_opened = 1;
|
||||
|
||||
filp->private_data = plm;
|
||||
|
||||
mutex_unlock(&fsi->mutex);
|
||||
mutex_unlock(&fsi->state->policy_mutex);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
mutex_unlock(&fsi->mutex);
|
||||
mutex_unlock(&fsi->state->policy_mutex);
|
||||
|
||||
if (plm)
|
||||
vfree(plm->data);
|
||||
@ -508,29 +521,94 @@ static const struct file_operations sel_policy_ops = {
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static int sel_make_policy_nodes(struct selinux_fs_info *fsi)
|
||||
static void sel_remove_old_bool_data(unsigned int bool_num, char **bool_names,
|
||||
unsigned int *bool_values)
|
||||
{
|
||||
int ret;
|
||||
u32 i;
|
||||
|
||||
ret = sel_make_bools(fsi);
|
||||
/* bool_dir cleanup */
|
||||
for (i = 0; i < bool_num; i++)
|
||||
kfree(bool_names[i]);
|
||||
kfree(bool_names);
|
||||
kfree(bool_values);
|
||||
}
|
||||
|
||||
static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
|
||||
struct selinux_policy *newpolicy)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir, *old_dentry;
|
||||
unsigned int tmp_bool_num, old_bool_num;
|
||||
char **tmp_bool_names, **old_bool_names;
|
||||
unsigned int *tmp_bool_values, *old_bool_values;
|
||||
unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */
|
||||
|
||||
tmp_parent = sel_make_disconnected_dir(fsi->sb, &tmp_ino);
|
||||
if (IS_ERR(tmp_parent))
|
||||
return PTR_ERR(tmp_parent);
|
||||
|
||||
tmp_ino = fsi->bool_dir->d_inode->i_ino - 1; /* sel_make_dir will increment and set */
|
||||
tmp_bool_dir = sel_make_dir(tmp_parent, BOOL_DIR_NAME, &tmp_ino);
|
||||
if (IS_ERR(tmp_bool_dir)) {
|
||||
ret = PTR_ERR(tmp_bool_dir);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp_ino = fsi->class_dir->d_inode->i_ino - 1; /* sel_make_dir will increment and set */
|
||||
tmp_class_dir = sel_make_dir(tmp_parent, CLASS_DIR_NAME, &tmp_ino);
|
||||
if (IS_ERR(tmp_class_dir)) {
|
||||
ret = PTR_ERR(tmp_class_dir);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sel_make_bools(newpolicy, tmp_bool_dir, &tmp_bool_num,
|
||||
&tmp_bool_names, &tmp_bool_values);
|
||||
if (ret) {
|
||||
pr_err("SELinux: failed to load policy booleans\n");
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sel_make_classes(fsi);
|
||||
ret = sel_make_classes(newpolicy, tmp_class_dir,
|
||||
&fsi->last_class_ino);
|
||||
if (ret) {
|
||||
pr_err("SELinux: failed to load policy classes\n");
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sel_make_policycap(fsi);
|
||||
if (ret) {
|
||||
pr_err("SELinux: failed to load policy capabilities\n");
|
||||
return ret;
|
||||
}
|
||||
/* booleans */
|
||||
old_dentry = fsi->bool_dir;
|
||||
lock_rename(tmp_bool_dir, old_dentry);
|
||||
d_exchange(tmp_bool_dir, fsi->bool_dir);
|
||||
|
||||
return 0;
|
||||
old_bool_num = fsi->bool_num;
|
||||
old_bool_names = fsi->bool_pending_names;
|
||||
old_bool_values = fsi->bool_pending_values;
|
||||
|
||||
fsi->bool_num = tmp_bool_num;
|
||||
fsi->bool_pending_names = tmp_bool_names;
|
||||
fsi->bool_pending_values = tmp_bool_values;
|
||||
|
||||
sel_remove_old_bool_data(old_bool_num, old_bool_names, old_bool_values);
|
||||
|
||||
fsi->bool_dir = tmp_bool_dir;
|
||||
unlock_rename(tmp_bool_dir, old_dentry);
|
||||
|
||||
/* classes */
|
||||
old_dentry = fsi->class_dir;
|
||||
lock_rename(tmp_class_dir, old_dentry);
|
||||
d_exchange(tmp_class_dir, fsi->class_dir);
|
||||
fsi->class_dir = tmp_class_dir;
|
||||
unlock_rename(tmp_class_dir, old_dentry);
|
||||
|
||||
out:
|
||||
/* Since the other temporary dirs are children of tmp_parent
|
||||
* this will handle all the cleanup in the case of a failure before
|
||||
* the swapover
|
||||
*/
|
||||
sel_remove_entries(tmp_parent);
|
||||
dput(tmp_parent); /* d_genocide() only handles the children */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t sel_write_load(struct file *file, const char __user *buf,
|
||||
@ -538,10 +616,11 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
|
||||
|
||||
{
|
||||
struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
|
||||
struct selinux_policy *newpolicy;
|
||||
ssize_t length;
|
||||
void *data = NULL;
|
||||
|
||||
mutex_lock(&fsi->mutex);
|
||||
mutex_lock(&fsi->state->policy_mutex);
|
||||
|
||||
length = avc_has_perm(&selinux_state,
|
||||
current_sid(), SECINITSID_SECURITY,
|
||||
@ -563,15 +642,19 @@ 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(fsi->state, data, count);
|
||||
length = security_load_policy(fsi->state, data, count, &newpolicy);
|
||||
if (length) {
|
||||
pr_warn_ratelimited("SELinux: failed to load policy\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
length = sel_make_policy_nodes(fsi);
|
||||
if (length)
|
||||
length = sel_make_policy_nodes(fsi, newpolicy);
|
||||
if (length) {
|
||||
selinux_policy_cancel(fsi->state, newpolicy);
|
||||
goto out1;
|
||||
}
|
||||
|
||||
selinux_policy_commit(fsi->state, newpolicy);
|
||||
|
||||
length = count;
|
||||
|
||||
@ -581,7 +664,7 @@ out1:
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
out:
|
||||
mutex_unlock(&fsi->mutex);
|
||||
mutex_unlock(&fsi->state->policy_mutex);
|
||||
vfree(data);
|
||||
return length;
|
||||
}
|
||||
@ -634,7 +717,8 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
|
||||
char tmpbuf[TMPBUFLEN];
|
||||
ssize_t length;
|
||||
|
||||
length = scnprintf(tmpbuf, TMPBUFLEN, "%u", fsi->state->checkreqprot);
|
||||
length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
|
||||
checkreqprot_get(fsi->state));
|
||||
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
|
||||
}
|
||||
|
||||
@ -676,7 +760,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
|
||||
comm, current->pid);
|
||||
}
|
||||
|
||||
fsi->state->checkreqprot = new_value ? 1 : 0;
|
||||
checkreqprot_set(fsi->state, (new_value ? 1 : 0));
|
||||
length = count;
|
||||
out:
|
||||
kfree(page);
|
||||
@ -1186,7 +1270,7 @@ 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(&fsi->mutex);
|
||||
mutex_lock(&fsi->state->policy_mutex);
|
||||
|
||||
ret = -EINVAL;
|
||||
if (index >= fsi->bool_num || strcmp(name,
|
||||
@ -1205,14 +1289,14 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
|
||||
}
|
||||
length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
|
||||
fsi->bool_pending_values[index]);
|
||||
mutex_unlock(&fsi->mutex);
|
||||
mutex_unlock(&fsi->state->policy_mutex);
|
||||
ret = simple_read_from_buffer(buf, count, ppos, page, length);
|
||||
out_free:
|
||||
free_page((unsigned long)page);
|
||||
return ret;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&fsi->mutex);
|
||||
mutex_unlock(&fsi->state->policy_mutex);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
@ -1237,7 +1321,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
mutex_lock(&fsi->mutex);
|
||||
mutex_lock(&fsi->state->policy_mutex);
|
||||
|
||||
length = avc_has_perm(&selinux_state,
|
||||
current_sid(), SECINITSID_SECURITY,
|
||||
@ -1262,7 +1346,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
|
||||
length = count;
|
||||
|
||||
out:
|
||||
mutex_unlock(&fsi->mutex);
|
||||
mutex_unlock(&fsi->state->policy_mutex);
|
||||
kfree(page);
|
||||
return length;
|
||||
}
|
||||
@ -1293,7 +1377,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
mutex_lock(&fsi->mutex);
|
||||
mutex_lock(&fsi->state->policy_mutex);
|
||||
|
||||
length = avc_has_perm(&selinux_state,
|
||||
current_sid(), SECINITSID_SECURITY,
|
||||
@ -1315,7 +1399,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
|
||||
length = count;
|
||||
|
||||
out:
|
||||
mutex_unlock(&fsi->mutex);
|
||||
mutex_unlock(&fsi->state->policy_mutex);
|
||||
kfree(page);
|
||||
return length;
|
||||
}
|
||||
@ -1331,14 +1415,13 @@ static void sel_remove_entries(struct dentry *de)
|
||||
shrink_dcache_parent(de);
|
||||
}
|
||||
|
||||
#define BOOL_DIR_NAME "booleans"
|
||||
|
||||
static int sel_make_bools(struct selinux_fs_info *fsi)
|
||||
static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir,
|
||||
unsigned int *bool_num, char ***bool_pending_names,
|
||||
unsigned int **bool_pending_values)
|
||||
{
|
||||
int ret;
|
||||
ssize_t len;
|
||||
struct dentry *dentry = NULL;
|
||||
struct dentry *dir = fsi->bool_dir;
|
||||
struct inode *inode = NULL;
|
||||
struct inode_security_struct *isec;
|
||||
char **names = NULL, *page;
|
||||
@ -1346,34 +1429,23 @@ static int sel_make_bools(struct selinux_fs_info *fsi)
|
||||
int *values = NULL;
|
||||
u32 sid;
|
||||
|
||||
/* remove any existing files */
|
||||
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);
|
||||
|
||||
ret = -ENOMEM;
|
||||
page = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!page)
|
||||
goto out;
|
||||
|
||||
ret = security_get_bools(fsi->state, &num, &names, &values);
|
||||
ret = security_get_bools(newpolicy, &num, &names, &values);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = -ENOMEM;
|
||||
dentry = d_alloc_name(dir, names[i]);
|
||||
dentry = d_alloc_name(bool_dir, names[i]);
|
||||
if (!dentry)
|
||||
goto out;
|
||||
|
||||
ret = -ENOMEM;
|
||||
inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
|
||||
inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
|
||||
if (!inode) {
|
||||
dput(dentry);
|
||||
goto out;
|
||||
@ -1388,7 +1460,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi)
|
||||
}
|
||||
|
||||
isec = selinux_inode(inode);
|
||||
ret = security_genfs_sid(fsi->state, "selinuxfs", page,
|
||||
ret = selinux_policy_genfs_sid(newpolicy, "selinuxfs", page,
|
||||
SECCLASS_FILE, &sid);
|
||||
if (ret) {
|
||||
pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n",
|
||||
@ -1402,9 +1474,9 @@ static int sel_make_bools(struct selinux_fs_info *fsi)
|
||||
inode->i_ino = i|SEL_BOOL_INO_OFFSET;
|
||||
d_add(dentry, inode);
|
||||
}
|
||||
fsi->bool_num = num;
|
||||
fsi->bool_pending_names = names;
|
||||
fsi->bool_pending_values = values;
|
||||
*bool_num = num;
|
||||
*bool_pending_names = names;
|
||||
*bool_pending_values = values;
|
||||
|
||||
free_page((unsigned long)page);
|
||||
return 0;
|
||||
@ -1417,7 +1489,7 @@ out:
|
||||
kfree(names);
|
||||
}
|
||||
kfree(values);
|
||||
sel_remove_entries(dir);
|
||||
sel_remove_entries(bool_dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1791,14 +1863,14 @@ static const struct file_operations sel_policycap_ops = {
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static int sel_make_perm_files(char *objclass, int classvalue,
|
||||
struct dentry *dir)
|
||||
static int sel_make_perm_files(struct selinux_policy *newpolicy,
|
||||
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(fsi->state, objclass, &perms, &nperms);
|
||||
rc = security_get_permissions(newpolicy, objclass, &perms, &nperms);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -1831,8 +1903,9 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sel_make_class_dir_entries(char *classname, int index,
|
||||
struct dentry *dir)
|
||||
static int sel_make_class_dir_entries(struct selinux_policy *newpolicy,
|
||||
char *classname, int index,
|
||||
struct dentry *dir)
|
||||
{
|
||||
struct super_block *sb = dir->d_sb;
|
||||
struct selinux_fs_info *fsi = sb->s_fs_info;
|
||||
@ -1858,39 +1931,38 @@ static int sel_make_class_dir_entries(char *classname, int index,
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
rc = sel_make_perm_files(classname, index, dentry);
|
||||
rc = sel_make_perm_files(newpolicy, classname, index, dentry);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sel_make_classes(struct selinux_fs_info *fsi)
|
||||
static int sel_make_classes(struct selinux_policy *newpolicy,
|
||||
struct dentry *class_dir,
|
||||
unsigned long *last_class_ino)
|
||||
{
|
||||
|
||||
int rc, nclasses, i;
|
||||
char **classes;
|
||||
|
||||
/* delete any existing entries */
|
||||
sel_remove_entries(fsi->class_dir);
|
||||
|
||||
rc = security_get_classes(fsi->state, &classes, &nclasses);
|
||||
rc = security_get_classes(newpolicy, &classes, &nclasses);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* +2 since classes are 1-indexed */
|
||||
fsi->last_class_ino = sel_class_to_ino(nclasses + 2);
|
||||
*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(fsi->class_dir, classes[i],
|
||||
&fsi->last_class_ino);
|
||||
class_name_dir = sel_make_dir(class_dir, classes[i],
|
||||
last_class_ino);
|
||||
if (IS_ERR(class_name_dir)) {
|
||||
rc = PTR_ERR(class_name_dir);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* i+1 since class values are 1-indexed */
|
||||
rc = sel_make_class_dir_entries(classes[i], i + 1,
|
||||
rc = sel_make_class_dir_entries(newpolicy, classes[i], i + 1,
|
||||
class_name_dir);
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -1909,8 +1981,6 @@ static int sel_make_policycap(struct selinux_fs_info *fsi)
|
||||
struct dentry *dentry = NULL;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
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(fsi->policycap_dir,
|
||||
@ -1962,6 +2032,22 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static struct dentry *sel_make_disconnected_dir(struct super_block *sb,
|
||||
unsigned long *ino)
|
||||
{
|
||||
struct inode *inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
|
||||
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_ino = ++(*ino);
|
||||
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
||||
inc_nlink(inode);
|
||||
return d_obtain_alias(inode);
|
||||
}
|
||||
|
||||
#define NULL_FILE_NAME "null"
|
||||
|
||||
static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
@ -2060,14 +2146,14 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
fsi->class_dir = sel_make_dir(sb->s_root, "class", &fsi->last_ino);
|
||||
fsi->class_dir = sel_make_dir(sb->s_root, CLASS_DIR_NAME, &fsi->last_ino);
|
||||
if (IS_ERR(fsi->class_dir)) {
|
||||
ret = PTR_ERR(fsi->class_dir);
|
||||
fsi->class_dir = NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
fsi->policycap_dir = sel_make_dir(sb->s_root, "policy_capabilities",
|
||||
fsi->policycap_dir = sel_make_dir(sb->s_root, POLICYCAP_DIR_NAME,
|
||||
&fsi->last_ino);
|
||||
if (IS_ERR(fsi->policycap_dir)) {
|
||||
ret = PTR_ERR(fsi->policycap_dir);
|
||||
@ -2075,9 +2161,12 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = sel_make_policy_nodes(fsi);
|
||||
if (ret)
|
||||
ret = sel_make_policycap(fsi);
|
||||
if (ret) {
|
||||
pr_err("SELinux: failed to load policy capabilities\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
pr_err("SELinux: %s: failed while creating inodes\n",
|
||||
|
@ -301,7 +301,6 @@ void avtab_destroy(struct avtab *h)
|
||||
|
||||
void avtab_init(struct avtab *h)
|
||||
{
|
||||
kvfree(h->htable);
|
||||
h->htable = NULL;
|
||||
h->nel = 0;
|
||||
}
|
||||
@ -340,6 +339,54 @@ int avtab_alloc(struct avtab *h, u32 nrules)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avtab_duplicate(struct avtab *new, struct avtab *orig)
|
||||
{
|
||||
int i;
|
||||
struct avtab_node *node, *tmp, *tail;
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->htable = kvcalloc(orig->nslot, sizeof(void *), GFP_KERNEL);
|
||||
if (!new->htable)
|
||||
return -ENOMEM;
|
||||
new->nslot = orig->nslot;
|
||||
new->mask = orig->mask;
|
||||
|
||||
for (i = 0; i < orig->nslot; i++) {
|
||||
tail = NULL;
|
||||
for (node = orig->htable[i]; node; node = node->next) {
|
||||
tmp = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto error;
|
||||
tmp->key = node->key;
|
||||
if (tmp->key.specified & AVTAB_XPERMS) {
|
||||
tmp->datum.u.xperms =
|
||||
kmem_cache_zalloc(avtab_xperms_cachep,
|
||||
GFP_KERNEL);
|
||||
if (!tmp->datum.u.xperms) {
|
||||
kmem_cache_free(avtab_node_cachep, tmp);
|
||||
goto error;
|
||||
}
|
||||
tmp->datum.u.xperms = node->datum.u.xperms;
|
||||
} else
|
||||
tmp->datum.u.data = node->datum.u.data;
|
||||
|
||||
if (tail)
|
||||
tail->next = tmp;
|
||||
else
|
||||
new->htable[i] = tmp;
|
||||
|
||||
tail = tmp;
|
||||
new->nel++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
avtab_destroy(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void avtab_hash_eval(struct avtab *h, char *tag)
|
||||
{
|
||||
int i, chain_len, slots_used, max_chain_len;
|
||||
|
@ -89,6 +89,7 @@ struct avtab {
|
||||
|
||||
void avtab_init(struct avtab *h);
|
||||
int avtab_alloc(struct avtab *, u32);
|
||||
int avtab_duplicate(struct avtab *new, struct avtab *orig);
|
||||
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
|
||||
void avtab_destroy(struct avtab *h);
|
||||
void avtab_hash_eval(struct avtab *h, char *tag);
|
||||
|
@ -600,3 +600,158 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||
services_compute_xperms_drivers(xperms, node);
|
||||
}
|
||||
}
|
||||
|
||||
static int cond_dup_av_list(struct cond_av_list *new,
|
||||
struct cond_av_list *orig,
|
||||
struct avtab *avtab)
|
||||
{
|
||||
struct avtab_node *avnode;
|
||||
u32 i;
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->nodes = kcalloc(orig->len, sizeof(*new->nodes), GFP_KERNEL);
|
||||
if (!new->nodes)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < orig->len; i++) {
|
||||
avnode = avtab_search_node(avtab, &orig->nodes[i]->key);
|
||||
if (WARN_ON(!avnode))
|
||||
return -EINVAL;
|
||||
new->nodes[i] = avnode;
|
||||
new->len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int duplicate_policydb_cond_list(struct policydb *newp,
|
||||
struct policydb *origp)
|
||||
{
|
||||
int rc, i, j;
|
||||
|
||||
rc = avtab_duplicate(&newp->te_cond_avtab, &origp->te_cond_avtab);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
newp->cond_list_len = 0;
|
||||
newp->cond_list = kcalloc(origp->cond_list_len,
|
||||
sizeof(*newp->cond_list),
|
||||
GFP_KERNEL);
|
||||
if (!newp->cond_list)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < origp->cond_list_len; i++) {
|
||||
struct cond_node *newn = &newp->cond_list[i];
|
||||
struct cond_node *orign = &origp->cond_list[i];
|
||||
|
||||
newp->cond_list_len++;
|
||||
|
||||
newn->cur_state = orign->cur_state;
|
||||
newn->expr.nodes = kcalloc(orign->expr.len,
|
||||
sizeof(*newn->expr.nodes), GFP_KERNEL);
|
||||
if (!newn->expr.nodes)
|
||||
goto error;
|
||||
for (j = 0; j < orign->expr.len; j++)
|
||||
newn->expr.nodes[j] = orign->expr.nodes[j];
|
||||
newn->expr.len = orign->expr.len;
|
||||
|
||||
rc = cond_dup_av_list(&newn->true_list, &orign->true_list,
|
||||
&newp->te_cond_avtab);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
rc = cond_dup_av_list(&newn->false_list, &orign->false_list,
|
||||
&newp->te_cond_avtab);
|
||||
if (rc)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
avtab_destroy(&newp->te_cond_avtab);
|
||||
cond_list_destroy(newp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int cond_bools_destroy(void *key, void *datum, void *args)
|
||||
{
|
||||
/* key was not copied so no need to free here */
|
||||
kfree(datum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, void *args)
|
||||
{
|
||||
struct cond_bool_datum *datum;
|
||||
|
||||
datum = kmemdup(orig->datum, sizeof(struct cond_bool_datum),
|
||||
GFP_KERNEL);
|
||||
if (!datum)
|
||||
return -ENOMEM;
|
||||
|
||||
new->key = orig->key; /* No need to copy, never modified */
|
||||
new->datum = datum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cond_bools_index(void *key, void *datum, void *args)
|
||||
{
|
||||
struct cond_bool_datum *booldatum, **cond_bool_array;
|
||||
|
||||
booldatum = datum;
|
||||
cond_bool_array = args;
|
||||
cond_bool_array[booldatum->value - 1] = booldatum;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int duplicate_policydb_bools(struct policydb *newdb,
|
||||
struct policydb *orig)
|
||||
{
|
||||
struct cond_bool_datum **cond_bool_array;
|
||||
int rc;
|
||||
|
||||
cond_bool_array = kmalloc_array(orig->p_bools.nprim,
|
||||
sizeof(*orig->bool_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!cond_bool_array)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = hashtab_duplicate(&newdb->p_bools.table, &orig->p_bools.table,
|
||||
cond_bools_copy, cond_bools_destroy, NULL);
|
||||
if (rc) {
|
||||
kfree(cond_bool_array);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hashtab_map(&newdb->p_bools.table, cond_bools_index, cond_bool_array);
|
||||
newdb->bool_val_to_struct = cond_bool_array;
|
||||
|
||||
newdb->p_bools.nprim = orig->p_bools.nprim;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cond_policydb_destroy_dup(struct policydb *p)
|
||||
{
|
||||
hashtab_map(&p->p_bools.table, cond_bools_destroy, NULL);
|
||||
hashtab_destroy(&p->p_bools.table);
|
||||
cond_policydb_destroy(p);
|
||||
}
|
||||
|
||||
int cond_policydb_dup(struct policydb *new, struct policydb *orig)
|
||||
{
|
||||
cond_policydb_init(new);
|
||||
|
||||
if (duplicate_policydb_bools(new, orig))
|
||||
return -ENOMEM;
|
||||
|
||||
if (duplicate_policydb_cond_list(new, orig)) {
|
||||
cond_policydb_destroy_dup(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -79,5 +79,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
|
||||
struct extended_perms_decision *xpermd);
|
||||
void evaluate_cond_nodes(struct policydb *p);
|
||||
void cond_policydb_destroy_dup(struct policydb *p);
|
||||
int cond_policydb_dup(struct policydb *new, struct policydb *orig);
|
||||
|
||||
#endif /* _CONDITIONAL_H_ */
|
||||
|
@ -122,6 +122,59 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
|
||||
info->max_chain_len = max_chain_len;
|
||||
}
|
||||
|
||||
int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
|
||||
int (*copy)(struct hashtab_node *new,
|
||||
struct hashtab_node *orig, void *args),
|
||||
int (*destroy)(void *k, void *d, void *args),
|
||||
void *args)
|
||||
{
|
||||
struct hashtab_node *cur, *tmp, *tail;
|
||||
int i, rc;
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->htable = kcalloc(orig->size, sizeof(*new->htable), GFP_KERNEL);
|
||||
if (!new->htable)
|
||||
return -ENOMEM;
|
||||
|
||||
new->size = orig->size;
|
||||
|
||||
for (i = 0; i < orig->size; i++) {
|
||||
tail = NULL;
|
||||
for (cur = orig->htable[i]; cur; cur = cur->next) {
|
||||
tmp = kmem_cache_zalloc(hashtab_node_cachep,
|
||||
GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto error;
|
||||
rc = copy(tmp, cur, args);
|
||||
if (rc) {
|
||||
kmem_cache_free(hashtab_node_cachep, tmp);
|
||||
goto error;
|
||||
}
|
||||
tmp->next = NULL;
|
||||
if (!tail)
|
||||
new->htable[i] = tmp;
|
||||
else
|
||||
tail->next = tmp;
|
||||
tail = tmp;
|
||||
new->nel++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
for (i = 0; i < new->size; i++) {
|
||||
for (cur = new->htable[i]; cur; cur = tmp) {
|
||||
tmp = cur->next;
|
||||
destroy(cur->key, cur->datum, args);
|
||||
kmem_cache_free(hashtab_node_cachep, cur);
|
||||
}
|
||||
}
|
||||
kmem_cache_free(hashtab_node_cachep, new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void __init hashtab_cache_init(void)
|
||||
{
|
||||
hashtab_node_cachep = kmem_cache_create("hashtab_node",
|
||||
|
@ -136,6 +136,12 @@ int hashtab_map(struct hashtab *h,
|
||||
int (*apply)(void *k, void *d, void *args),
|
||||
void *args);
|
||||
|
||||
int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
|
||||
int (*copy)(struct hashtab_node *new,
|
||||
struct hashtab_node *orig, void *args),
|
||||
int (*destroy)(void *k, void *d, void *args),
|
||||
void *args);
|
||||
|
||||
/* Fill info with some hash table statistics */
|
||||
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,12 +22,11 @@ struct selinux_map {
|
||||
u16 size; /* array size of mapping */
|
||||
};
|
||||
|
||||
struct selinux_ss {
|
||||
struct selinux_policy {
|
||||
struct sidtab *sidtab;
|
||||
struct policydb policydb;
|
||||
rwlock_t policy_rwlock;
|
||||
u32 latest_granting;
|
||||
struct selinux_map map;
|
||||
u32 latest_granting;
|
||||
} __randomize_layout;
|
||||
|
||||
void services_compute_xperms_drivers(struct extended_perms *xperms,
|
||||
|
@ -464,6 +464,16 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sidtab_cancel_convert(struct sidtab *s)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* cancelling policy load - disable live convert of sidtab */
|
||||
spin_lock_irqsave(&s->lock, flags);
|
||||
s->convert = NULL;
|
||||
spin_unlock_irqrestore(&s->lock, flags);
|
||||
}
|
||||
|
||||
static void sidtab_destroy_entry(struct sidtab_entry *entry)
|
||||
{
|
||||
context_destroy(&entry->context);
|
||||
|
@ -123,6 +123,8 @@ static inline struct context *sidtab_search_force(struct sidtab *s, u32 sid)
|
||||
|
||||
int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
|
||||
|
||||
void sidtab_cancel_convert(struct sidtab *s);
|
||||
|
||||
int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
|
||||
|
||||
void sidtab_destroy(struct sidtab *s);
|
||||
|
Loading…
Reference in New Issue
Block a user