Commit b3aa112d authored by Linus Torvalds's avatar Linus Torvalds

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

Pull SELinux updates from Paul Moore:
 "We've got twenty SELinux patches for the v5.7 merge window, the
  highlights are below:

   - Deprecate setting /sys/fs/selinux/checkreqprot to 1.

     This flag was originally created to deal with legacy userspace and
     the READ_IMPLIES_EXEC personality flag. We changed the default from
     1 to 0 back in Linux v4.4 and now we are taking the next step of
     deprecating it, at some point in the future we will take the final
     step of rejecting 1.

   - Allow kernfs symlinks to inherit the SELinux label of the parent
     directory. In order to preserve backwards compatibility this is
     protected by the genfs_seclabel_symlinks SELinux policy capability.

   - Optimize how we store filename transitions in the kernel, resulting
     in some significant improvements to policy load times.

   - Do a better job calculating our internal hash table sizes which
     resulted in additional policy load improvements and likely general
     SELinux performance improvements as well.

   - Remove the unused initial SIDs (labels) and improve how we handle
     initial SIDs.

   - Enable per-file labeling for the bpf filesystem.

   - Ensure that we properly label NFS v4.2 filesystems to avoid a
     temporary unlabeled condition.

   - Add some missing XFS quota command types to the SELinux quota
     access controls.

   - Fix a problem where we were not updating the seq_file position
     index correctly in selinuxfs.

   - We consolidate some duplicated code into helper functions.

   - A number of list to array conversions.

   - Update Stephen Smalley's email address in MAINTAINERS"

* tag 'selinux-pr-20200330' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: clean up indentation issue with assignment statement
  NFS: Ensure security label is set for root inode
  MAINTAINERS: Update my email address
  selinux: avtab_init() and cond_policydb_init() return void
  selinux: clean up error path in policydb_init()
  selinux: remove unused initial SIDs and improve handling
  selinux: reduce the use of hard-coded hash sizes
  selinux: Add xfs quota command types
  selinux: optimize storage of filename transitions
  selinux: factor out loop body from filename_trans_read()
  security: selinux: allow per-file labeling for bpffs
  selinux: generalize evaluate_cond_node()
  selinux: convert cond_expr to array
  selinux: convert cond_av_list to array
  selinux: convert cond_list to array
  selinux: sel_avc_get_stat_idx should increase position index
  selinux: allow kernfs symlinks to inherit parent directory context
  selinux: simplify evaluate_cond_node()
  Documentation,selinux: deprecate setting checkreqprot to 1
  selinux: move status variables out of selinux_ss
parents 674d85eb c753924b
What: /sys/fs/selinux/checkreqprot
Date: April 2005 (predates git)
KernelVersion: 2.6.12-rc2 (predates git)
Contact: selinux@vger.kernel.org
Description:
The selinuxfs "checkreqprot" node allows SELinux to be configured
to check the protection requested by userspace for mmap/mprotect
calls instead of the actual protection applied by the kernel.
This was a compatibility mechanism for legacy userspace and
for the READ_IMPLIES_EXEC personality flag. However, if set to
1, it weakens security by allowing mappings to be made executable
without authorization by policy. The default value of checkreqprot
at boot was changed starting in Linux v4.4 to 0 (i.e. check the
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 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
but will discard writes of the "0" value and will reject writes of the
"1" value when this mechanism is removed.
......@@ -525,6 +525,7 @@
Default value is set via a kernel config option.
Value can be changed at runtime via
/sys/fs/selinux/checkreqprot.
Setting checkreqprot to 1 is deprecated.
cio_ignore= [S390]
See Documentation/s390/common_io.rst for details.
......
......@@ -15027,7 +15027,7 @@ X: security/selinux/
SELINUX SECURITY MODULE
M: Paul Moore <paul@paul-moore.com>
M: Stephen Smalley <sds@tycho.nsa.gov>
M: Stephen Smalley <stephen.smalley.work@gmail.com>
M: Eric Paris <eparis@parisplace.org>
L: selinux@vger.kernel.org
W: https://selinuxproject.org
......@@ -15039,6 +15039,7 @@ F: security/selinux/
F: scripts/selinux/
F: Documentation/admin-guide/LSM/SELinux.rst
F: Documentation/ABI/obsolete/sysfs-selinux-disable
F: Documentation/ABI/obsolete/sysfs-selinux-checkreqprot
SENSABLE PHANTOM
M: Jiri Slaby <jirislaby@gmail.com>
......
......@@ -73,6 +73,7 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
struct inode *inode;
char *name;
int error = -ENOMEM;
unsigned long kflags = 0, kflags_out = 0;
name = kstrdup(fc->source, GFP_KERNEL);
if (!name)
......@@ -83,11 +84,14 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
if (fsinfo.fattr == NULL)
goto out_name;
fsinfo.fattr->label = nfs4_label_alloc(server, GFP_KERNEL);
if (IS_ERR(fsinfo.fattr->label))
goto out_fattr;
error = server->nfs_client->rpc_ops->getroot(server, ctx->mntfh, &fsinfo);
if (error < 0) {
dprintk("nfs_get_root: getattr error = %d\n", -error);
nfs_errorf(fc, "NFS: Couldn't getattr on root");
goto out_fattr;
goto out_label;
}
inode = nfs_fhget(s, ctx->mntfh, fsinfo.fattr, NULL);
......@@ -95,12 +99,12 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
dprintk("nfs_get_root: get root inode failed\n");
error = PTR_ERR(inode);
nfs_errorf(fc, "NFS: Couldn't get root inode");
goto out_fattr;
goto out_label;
}
error = nfs_superblock_set_dummy_root(s, inode);
if (error != 0)
goto out_fattr;
goto out_label;
/* root dentries normally start off anonymous and get spliced in later
* if the dentry tree reaches them; however if the dentry already
......@@ -111,7 +115,7 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
dprintk("nfs_get_root: get root dentry failed\n");
error = PTR_ERR(root);
nfs_errorf(fc, "NFS: Couldn't get root dentry");
goto out_fattr;
goto out_label;
}
security_d_instantiate(root, inode);
......@@ -123,12 +127,39 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
}
spin_unlock(&root->d_lock);
fc->root = root;
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
kflags |= SECURITY_LSM_NATIVE_LABELS;
if (ctx->clone_data.sb) {
if (d_inode(fc->root)->i_fop != &nfs_dir_operations) {
error = -ESTALE;
goto error_splat_root;
}
/* clone lsm security options from the parent to the new sb */
error = security_sb_clone_mnt_opts(ctx->clone_data.sb,
s, kflags, &kflags_out);
} else {
error = security_sb_set_mnt_opts(s, fc->security,
kflags, &kflags_out);
}
if (error)
goto error_splat_root;
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
nfs_setsecurity(inode, fsinfo.fattr, fsinfo.fattr->label);
error = 0;
out_label:
nfs4_label_free(fsinfo.fattr->label);
out_fattr:
nfs_free_fattr(fsinfo.fattr);
out_name:
kfree(name);
out:
return error;
error_splat_root:
dput(fc->root);
fc->root = NULL;
goto out_label;
}
......@@ -4002,7 +4002,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
{
int error;
struct nfs_fattr *fattr = info->fattr;
struct nfs4_label *label = NULL;
struct nfs4_label *label = fattr->label;
error = nfs4_server_capabilities(server, mntfh);
if (error < 0) {
......@@ -4010,23 +4010,17 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
return error;
}
label = nfs4_label_alloc(server, GFP_KERNEL);
if (IS_ERR(label))
return PTR_ERR(label);
error = nfs4_proc_getattr(server, mntfh, fattr, label, NULL);
if (error < 0) {
dprintk("nfs4_get_root: getattr error = %d\n", -error);
goto err_free_label;
goto out;
}
if (fattr->valid & NFS_ATTR_FATTR_FSID &&
!nfs_fsid_equal(&server->fsid, &fattr->fsid))
memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
err_free_label:
nfs4_label_free(label);
out:
return error;
}
......
......@@ -1179,7 +1179,6 @@ int nfs_get_tree_common(struct fs_context *fc)
struct super_block *s;
int (*compare_super)(struct super_block *, struct fs_context *) = nfs_compare_super;
struct nfs_server *server = ctx->server;
unsigned long kflags = 0, kflags_out = 0;
int error;
ctx->server = NULL;
......@@ -1239,26 +1238,6 @@ int nfs_get_tree_common(struct fs_context *fc)
goto error_splat_super;
}
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
kflags |= SECURITY_LSM_NATIVE_LABELS;
if (ctx->clone_data.sb) {
if (d_inode(fc->root)->i_fop != &nfs_dir_operations) {
error = -ESTALE;
goto error_splat_root;
}
/* clone any lsm security options from the parent to the new sb */
error = security_sb_clone_mnt_opts(ctx->clone_data.sb, s, kflags,
&kflags_out);
} else {
error = security_sb_set_mnt_opts(s, fc->security,
kflags, &kflags_out);
}
if (error)
goto error_splat_root;
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
!(kflags_out & SECURITY_LSM_NATIVE_LABELS))
NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
s->s_flags |= SB_ACTIVE;
error = 0;
......@@ -1268,10 +1247,6 @@ int nfs_get_tree_common(struct fs_context *fc)
out_err_nosb:
nfs_free_server(server);
goto out;
error_splat_root:
dput(fc->root);
fc->root = NULL;
error_splat_super:
deactivate_locked_super(s);
goto out;
......
......@@ -75,6 +75,7 @@ struct nfs_fattr {
struct nfs4_string *owner_name;
struct nfs4_string *group_name;
struct nfs4_threshold *mdsthreshold; /* pNFS threshold hints */
struct nfs4_label *label;
};
#define NFS_ATTR_FATTR_TYPE (1U << 0)
......
......@@ -67,8 +67,12 @@ int main(int argc, char *argv[])
}
isids_len = sizeof(initial_sid_to_string) / sizeof (char *);
for (i = 1; i < isids_len; i++)
initial_sid_to_string[i] = stoupperx(initial_sid_to_string[i]);
for (i = 1; i < isids_len; i++) {
const char *s = initial_sid_to_string[i];
if (s)
initial_sid_to_string[i] = stoupperx(s);
}
fprintf(fout, "/* This file is automatically generated. Do not edit. */\n");
fprintf(fout, "#ifndef _SELINUX_FLASK_H_\n#define _SELINUX_FLASK_H_\n\n");
......@@ -82,7 +86,8 @@ int main(int argc, char *argv[])
for (i = 1; i < isids_len; i++) {
const char *s = initial_sid_to_string[i];
fprintf(fout, "#define SECINITSID_%-39s %2d\n", s, i);
if (s)
fprintf(fout, "#define SECINITSID_%-39s %2d\n", s, i);
}
fprintf(fout, "\n#define SECINITSID_NUM %d\n", i-1);
fprintf(fout, "\nstatic inline bool security_is_socket_class(u16 kern_tclass)\n");
......
......@@ -88,6 +88,9 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE
'checkreqprot=' boot parameter. It may also be changed at runtime
via /sys/fs/selinux/checkreqprot if authorized by policy.
WARNING: this option is deprecated and will be removed in a future
kernel release.
If you are unsure how to answer this question, answer 0.
config SECURITY_SELINUX_SIDTAB_HASH_BITS
......
......@@ -6,9 +6,9 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
netnode.o netport.o \
netnode.o netport.o status.o \
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
......
......@@ -142,8 +142,11 @@ static int __init checkreqprot_setup(char *str)
{
unsigned long checkreqprot;
if (!kstrtoul(str, 0, &checkreqprot))
if (!kstrtoul(str, 0, &checkreqprot)) {
selinux_checkreqprot_boot = checkreqprot ? 1 : 0;
if (checkreqprot)
pr_warn("SELinux: checkreqprot set to 1 via kernel parameter. This is deprecated and will be rejected in a future kernel release.\n");
}
return 1;
}
__setup("checkreqprot=", checkreqprot_setup);
......@@ -699,6 +702,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
if (!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "binder") ||
!strcmp(sb->s_type->name, "bpf") ||
!strcmp(sb->s_type->name, "pstore"))
sbsec->flags |= SE_SBGENFS;
......@@ -1475,7 +1479,9 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
/* Default to the fs superblock SID. */
sid = sbsec->sid;
if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) {
if ((sbsec->flags & SE_SBGENFS) &&
(!S_ISLNK(inode->i_mode) ||
selinux_policycap_genfs_seclabel_symlinks())) {
/* We must have a dentry to determine the label on
* procfs inodes */
if (opt_dentry) {
......@@ -2139,11 +2145,18 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
case Q_QUOTAOFF:
case Q_SETINFO:
case Q_SETQUOTA:
case Q_XQUOTAOFF:
case Q_XQUOTAON:
case Q_XSETQLIM:
rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL);
break;
case Q_GETFMT:
case Q_GETINFO:
case Q_GETQUOTA:
case Q_XGETQUOTA:
case Q_XGETQSTAT:
case Q_XGETQSTATV:
case Q_XGETNEXTQUOTA:
rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL);
break;
default:
......@@ -7161,6 +7174,7 @@ static __init int selinux_init(void)
selinux_state.checkreqprot = selinux_checkreqprot_boot;
selinux_ss_init(&selinux_state.ss);
selinux_avc_init(&selinux_state.avc);
mutex_init(&selinux_state.status_lock);
/* Set the security state for the initial task. */
cred_init_security();
......
......@@ -14,12 +14,10 @@
#include "security.h"
int security_get_bools(struct selinux_state *state,
int *len, char ***names, int **values);
u32 *len, char ***names, int **values);
int security_set_bools(struct selinux_state *state,
int len, int *values);
int security_set_bools(struct selinux_state *state, u32 len, int *values);
int security_get_bool_value(struct selinux_state *state,
int index);
int security_get_bool_value(struct selinux_state *state, u32 index);
#endif
/* SPDX-License-Identifier: GPL-2.0 */
/* This file is automatically generated. Do not edit. */
static const char *initial_sid_to_string[] =
{
"null",
"kernel",
"security",
"unlabeled",
"fs",
"file",
"file_labels",
"init",
"any_socket",
"port",
"netif",
"netmsg",
"node",
"igmp_packet",
"icmp_socket",
"tcp_socket",
"sysctl_modprobe",
"sysctl",
"sysctl_fs",
"sysctl_kernel",
"sysctl_net",
"sysctl_net_unix",
"sysctl_vm",
"sysctl_dev",
"kmod",
"policy",
"scmp_packet",
"devnull",
NULL,
"kernel",
"security",
"unlabeled",
NULL,
"file",
NULL,
NULL,
"any_socket",
"port",
"netif",
"netmsg",
"node",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"devnull",
};
......@@ -79,6 +79,7 @@ enum {
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)
......@@ -108,6 +109,10 @@ struct selinux_state {
bool checkreqprot;
bool initialized;
bool policycap[__POLICYDB_CAPABILITY_MAX];
struct page *status_page;
struct mutex status_lock;
struct selinux_avc *avc;
struct selinux_ss *ss;
} __randomize_layout;
......@@ -209,6 +214,13 @@ static inline bool selinux_policycap_nnp_nosuid_transition(void)
return 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];
}
int security_mls_enabled(struct selinux_state *state);
int security_load_policy(struct selinux_state *state,
void *data, size_t len);
......
......@@ -668,6 +668,14 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
if (sscanf(page, "%u", &new_value) != 1)
goto out;
if (new_value) {
char comm[sizeof(current->comm)];
memcpy(comm, current->comm, sizeof(comm));
pr_warn_once("SELinux: %s (%d) set checkreqprot to 1. This is deprecated and will be rejected in a future kernel release.\n",
comm, current->pid);
}
fsi->state->checkreqprot = new_value ? 1 : 0;
length = count;
out:
......@@ -1327,14 +1335,14 @@ static void sel_remove_entries(struct dentry *de)
static int sel_make_bools(struct selinux_fs_info *fsi)
{
int i, ret;
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;
int num;
u32 i, num;
int *values = NULL;
u32 sid;
......@@ -1536,6 +1544,7 @@ static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
*idx = cpu + 1;
return &per_cpu(avc_cache_stats, cpu);
}
(*idx)++;
return NULL;
}
......@@ -1692,7 +1701,11 @@ static int sel_make_initcon_files(struct dentry *dir)
for (i = 1; i <= SECINITSID_NUM; i++) {
struct inode *inode;
struct dentry *dentry;
dentry = d_alloc_name(dir, security_get_initial_sid_context(i));
const char *s = security_get_initial_sid_context(i);
if (!s)
continue;
dentry = d_alloc_name(dir, s);
if (!dentry)
return -ENOMEM;
......
......@@ -299,12 +299,11 @@ void avtab_destroy(struct avtab *h)
h->mask = 0;
}
int avtab_init(struct avtab *h)
void avtab_init(struct avtab *h)
{
kvfree(h->htable);
h->htable = NULL;
h->nel = 0;
return 0;
}
int avtab_alloc(struct avtab *h, u32 nrules)
......
......@@ -87,7 +87,7 @@ struct avtab {
u32 mask; /* mask to compute hash func */
};
int avtab_init(struct avtab *);
void avtab_init(struct avtab *h);
int avtab_alloc(struct avtab *, u32);
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
void avtab_destroy(struct avtab *h);
......
......@@ -23,18 +23,19 @@
*/
static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
{
struct cond_expr *cur;
u32 i;
int s[COND_EXPR_MAXDEPTH];
int sp = -1;
for (cur = expr; cur; cur = cur->next) {
switch (cur->expr_type) {
for (i = 0; i < expr->len; i++) {
struct cond_expr_node *node = &expr->nodes[i];
switch (node->expr_type) {
case COND_BOOL:
if (sp == (COND_EXPR_MAXDEPTH - 1))
return -1;
sp++;
s[sp] = p->bool_val_to_struct[cur->bool - 1]->state;
s[sp] = p->bool_val_to_struct[node->bool - 1]->state;
break;
case COND_NOT:
if (sp < 0)
......@@ -85,90 +86,76 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
* list appropriately. If the result of the expression is undefined
* all of the rules are disabled for safety.
*/
int evaluate_cond_node(struct policydb *p, struct cond_node *node)
static void evaluate_cond_node(struct policydb *p, struct cond_node *node)
{
struct avtab_node *avnode;
int new_state;
struct cond_av_list *cur;
u32 i;
new_state = cond_evaluate_expr(p, node->expr);
new_state = cond_evaluate_expr(p, &node->expr);
if (new_state != node->cur_state) {
node->cur_state = new_state;
if (new_state == -1)
pr_err("SELinux: expression result was undefined - disabling all rules.\n");
/* turn the rules on or off */
for (cur = node->true_list; cur; cur = cur->next) {
for (i = 0; i < node->true_list.len; i++) {
avnode = node->true_list.nodes[i];
if (new_state <= 0)
cur->node->key.specified &= ~AVTAB_ENABLED;
avnode->key.specified &= ~AVTAB_ENABLED;
else
cur->node->key.specified |= AVTAB_ENABLED;
avnode->key.specified |= AVTAB_ENABLED;
}
for (cur = node->false_list; cur; cur = cur->next) {
for (i = 0; i < node->false_list.len; i++) {
avnode = node->false_list.nodes[i];
/* -1 or 1 */
if (new_state)
cur->node->key.specified &= ~AVTAB_ENABLED;
avnode->key.specified &= ~AVTAB_ENABLED;
else
cur->node->key.specified |= AVTAB_ENABLED;
avnode->key.specified |= AVTAB_ENABLED;
}
}
return 0;
}
int cond_policydb_init(struct policydb *p)
void evaluate_cond_nodes(struct policydb *p)
{
int rc;
u32 i;
p->bool_val_to_struct = NULL;
p->cond_list = NULL;
rc = avtab_init(&p->te_cond_avtab);
if (rc)
return rc;
return 0;
for (i = 0; i < p->cond_list_len; i++)
evaluate_cond_node(p, &p->cond_list[i]);
}
static void cond_av_list_destroy(struct cond_av_list *list)
void cond_policydb_init(struct policydb *p)
{
struct cond_av_list *cur, *next;
for (cur = list; cur; cur = next) {
next = cur->next;
/* the avtab_ptr_t node is destroy by the avtab */
kfree(cur);
}
p->bool_val_to_struct = NULL;
p->cond_list = NULL;
p->cond_list_len = 0;
avtab_init(&p->te_cond_avtab);
}
static void cond_node_destroy(struct cond_node *node)
{
struct cond_expr *cur_expr, *next_expr;
for (cur_expr = node->expr; cur_expr; cur_expr = next_expr) {
next_expr = cur_expr->next;
kfree(cur_expr);
}
cond_av_list_destroy(node->true_list);
cond_av_list_destroy(node->false_list);
kfree(node);
kfree(node->expr.nodes);
/* the avtab_ptr_t nodes are destroyed by the avtab */
kfree(node->true_list.nodes);
kfree(node->false_list.nodes);
}
static void cond_list_destroy(struct cond_node *list)
static void cond_list_destroy(struct policydb *p)
{
struct cond_node *next, *cur;
if (list == NULL)
return;
u32 i;
for (cur = list; cur; cur = next) {
next = cur->next;
cond_node_destroy(cur);
}
for (i = 0; i < p->cond_list_len; i++)
cond_node_destroy(&p->cond_list[i]);
kfree(p->cond_list);
}
void cond_policydb_destroy(struct policydb *p)
{
kfree(p->bool_val_to_struct);
avtab_destroy(&p->te_cond_avtab);
cond_list_destroy(p->cond_list);
cond_list_destroy(p);
}
int cond_init_bool_indexes(struct policydb *p)
......@@ -260,19 +247,18 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
struct cond_insertf_data {
struct policydb *p;
struct avtab_node **dst;
struct cond_av_list *other;
struct cond_av_list *head;
struct cond_av_list *tail;
};
static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr)
{
struct cond_insertf_data *data = ptr;
struct policydb *p = data->p;
struct cond_av_list *other = data->other, *list, *cur;
struct cond_av_list *other = data->other;
struct avtab_node *node_ptr;
u8 found;
int rc = -EINVAL;
u32 i;
bool found;
/*
* For type rules we have to make certain there aren't any
......@@ -282,7 +268,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
if (k->specified & AVTAB_TYPE) {
if (avtab_search(&p->te_avtab, k)) {
pr_err("SELinux: type rule already exists outside of a conditional.\n");
goto err;
return -EINVAL;
}
/*
* If we are reading the false list other will be a pointer to
......@@ -297,24 +283,24 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
if (node_ptr) {
if (avtab_search_node_next(node_ptr, k->specified)) {
pr_err("SELinux: too many conflicting type rules.\n");
goto err;
return -EINVAL;
}
found = 0;
for (cur = other; cur; cur = cur->next) {
if (cur->node == node_ptr) {
found = 1;
found = false;
for (i = 0; i < other->len; i++) {
if (other->nodes[i] == node_ptr) {
found = true;
break;
}
}
if (!found) {
pr_err("SELinux: conflicting type rules.\n");
goto err;
return -EINVAL;
}
}
} else {
if (avtab_search(&p->te_cond_avtab, k)) {
pr_err("SELinux: conflicting type rules when adding type rule for true.\n");
goto err;
return -EINVAL;
}
}
}
......@@ -322,39 +308,22 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
if (!node_ptr) {
pr_err("SELinux: could not insert rule.\n");
rc = -ENOMEM;
goto err;
}
list = kzalloc(sizeof(*list), GFP_KERNEL);
if (!list) {
rc = -ENOMEM;
goto err;
return -ENOMEM;
}
list->node = node_ptr;
if (!data->head)
data->head = list;
else
data->tail->next = list;
data->tail = list;
*data->dst = node_ptr;
return 0;
err:
cond_av_list_destroy(data->head);
data->head = NULL;
return rc;
}
static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other)
static int cond_read_av_list(struct policydb *p, void *fp,
struct cond_av_list *list,
struct cond_av_list *other)
{
int i, rc;
int rc;
__le32 buf[1];
u32 len;
u32 i, len;
struct cond_insertf_data data;
*ret_list = NULL;
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
return rc;
......@@ -363,22 +332,28 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list *
if (len == 0)
return 0;
list->nodes = kcalloc(len, sizeof(*list->nodes), GFP_KERNEL);
if (!list->nodes)
return -ENOMEM;
data.p = p;
data.other = other;
data.head = NULL;
data.tail = NULL;
for (i = 0; i < len; i++) {
data.dst = &list->nodes[i];
rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf,
&data);
if (rc)
if (rc) {
kfree(list->nodes);
list->nodes = NULL;
return rc;
}
}
*ret_list = data.head;
list->len = len;
return 0;
}
static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
{
if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
pr_err("SELinux: conditional expressions uses unknown operator.\n");
......@@ -395,49 +370,43 @@ static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
{
__le32 buf[2];
u32 len, i;
u32 i, len;
int rc;
struct cond_expr *expr = NULL, *last = NULL;
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto err;
return rc;
node->cur_state = le32_to_cpu(buf[0]);
/* expr */
len = le32_to_cpu(buf[1]);
node->expr.nodes = kcalloc(len, sizeof(*node->expr.nodes), GFP_KERNEL);
if (!node->expr.nodes)
return -ENOMEM;
node->expr.len = len;
for (i = 0; i < len; i++) {
struct cond_expr_node *expr = &node->expr.nodes[i];
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto err;
rc = -ENOMEM;
expr = kzalloc(sizeof(*expr), GFP_KERNEL);
if (!expr)
goto err;
expr->expr_type = le32_to_cpu(buf[0]);
expr->bool = le32_to_cpu(buf[1]);
if (!expr_isvalid(p, expr)) {
if (!expr_node_isvalid(p, expr)) {
rc = -EINVAL;
kfree(expr);
goto err;
}
if (i == 0)
node->expr = expr;
else
last->next = expr;
last = expr;
}
rc = cond_read_av_list(p, fp, &node->true_list, NULL);
if (rc)
goto err;
rc = cond_read_av_list(p, fp, &node->false_list, node->true_list);
rc = cond_read_av_list(p, fp, &node->false_list, &node->true_list);
if (rc)
goto err;
return 0;
......@@ -448,7 +417,6 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
int cond_read_list(struct policydb *p, void *fp)
{
struct cond_node *node, *last = NULL;
__le32 buf[1];
u32 i, len;
int rc;
......@@ -459,29 +427,24 @@ int cond_read_list(struct policydb *p, void *fp)
len = le32_to_cpu(buf[0]);
p->cond_list = kcalloc(len, sizeof(*p->cond_list), GFP_KERNEL);
if (!p->cond_list)
return rc;
rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel);
if (rc)
goto err;
for (i = 0; i < len; i++) {
rc = -ENOMEM;
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
goto err;
p->cond_list_len = len;
rc = cond_read_node(p, node, fp);
for (i = 0; i < len; i++) {
rc = cond_read_node(p, &p->cond_list[i], fp);
if (rc)
goto err;
if (i == 0)
p->cond_list = node;
else
last->next = node;
last = node;
}
return 0;
err:
cond_list_destroy(p->cond_list);
cond_list_destroy(p);
p->cond_list = NULL;
return rc;
}
......@@ -522,24 +485,16 @@ static int cond_write_av_list(struct policydb *p,
struct cond_av_list *list, struct policy_file *fp)
{
__le32 buf[1];
struct cond_av_list *cur_list;
u32 len;
u32 i;
int rc;
len = 0;
for (cur_list = list; cur_list != NULL; cur_list = cur_list->next)
len++;
buf[0] = cpu_to_le32(len);
buf[0] = cpu_to_le32(list->len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
if (len == 0)
return 0;
for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {
rc = avtab_write_item(p, cur_list->node, fp);
for (i = 0; i < list->len; i++) {
rc = avtab_write_item(p, list->nodes[i], fp);
if (rc)
return rc;
}
......@@ -550,59 +505,51 @@ static int cond_write_av_list(struct policydb *p,
static int cond_write_node(struct policydb *p, struct cond_node *node,
struct policy_file *fp)
{
struct cond_expr *cur_expr;
__le32 buf[2];
int rc;
u32 len = 0;
u32 i;
buf[0] = cpu_to_le32(node->cur_state);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next)
len++;
buf[0] = cpu_to_le32(len);
buf[0] = cpu_to_le32(node->expr.len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) {
buf[0] = cpu_to_le32(cur_expr->expr_type);
buf[1] = cpu_to_le32(cur_expr->bool);
for (i = 0; i < node->expr.len; i++) {
buf[0] = cpu_to_le32(node->expr.nodes[i].expr_type);
buf[1] = cpu_to_le32(node->expr.nodes[i].bool);
rc = put_entry(buf, sizeof(u32), 2, fp);
if (rc)
return rc;
}
rc = cond_write_av_list(p, node->true_list, fp);
rc = cond_write_av_list(p, &node->true_list, fp);
if (rc)
return rc;
rc = cond_write_av_list(p, node->false_list, fp);
rc = cond_write_av_list(p, &node->false_list, fp);
if (rc)
return rc;
return 0;
}
int cond_write_list(struct policydb *p, struct cond_node *list, void *fp)
int cond_write_list(struct policydb *p, void *fp)
{
struct cond_node *cur;
u32 len;
u32 i;
__le32 buf[1];
int rc;
len = 0;
for (cur = list; cur != NULL; cur = cur->next)
len++;
buf[0] = cpu_to_le32(len);
buf[0] = cpu_to_le32(p->cond_list_len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
for (cur = list; cur != NULL; cur = cur->next) {
rc = cond_write_node(p, cur, fp);
for (i = 0; i < p->cond_list_len; i++) {
rc = cond_write_node(p, &p->cond_list[i], fp);
if (rc)
return rc;
}
......
......@@ -19,7 +19,7 @@
* A conditional expression is a list of operators and operands
* in reverse polish notation.
*/
struct cond_expr {
struct cond_expr_node {
#define COND_BOOL 1 /* plain bool */
#define COND_NOT 2 /* !bool */
#define COND_OR 3 /* bool || bool */
......@@ -28,9 +28,13 @@ struct cond_expr {
#define COND_EQ 6 /* bool == bool */
#define COND_NEQ 7 /* bool != bool */
#define COND_LAST COND_NEQ
__u32 expr_type;
__u32 bool;
struct cond_expr *next;
u32 expr_type;
u32 bool;
};
struct cond_expr {
struct cond_expr_node *nodes;
u32 len;
};
/*
......@@ -39,8 +43,8 @@ struct cond_expr {
* struct is for that list.
*/
struct cond_av_list {
struct avtab_node *node;
struct cond_av_list *next;
struct avtab_node **nodes;
u32 len;
};
/*
......@@ -52,13 +56,12 @@ struct cond_av_list {
*/
struct cond_node {
int cur_state;
struct cond_expr *expr;
struct cond_av_list *true_list;
struct cond_av_list *false_list;
struct cond_node *next;
struct cond_expr expr;
struct cond_av_list true_list;
struct cond_av_list false_list;
};
int cond_policydb_init(struct policydb *p);
void cond_policydb_init(struct policydb *p);
void cond_policydb_destroy(struct policydb *p);
int cond_init_bool_indexes(struct policydb *p);
......@@ -69,12 +72,12 @@ int cond_index_bool(void *key, void *datum, void *datap);
int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp);
int cond_read_list(struct policydb *p, void *fp);
int cond_write_bool(void *key, void *datum, void *ptr);
int cond_write_list(struct policydb *p, struct cond_node *list, void *fp);
int cond_write_list(struct policydb *p, void *fp);
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
struct av_decision *avd, struct extended_perms *xperms);
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
struct extended_perms_decision *xpermd);
int evaluate_cond_node(struct policydb *p, struct cond_node *node);
void evaluate_cond_nodes(struct policydb *p);
#endif /* _CONDITIONAL_H_ */
......@@ -12,12 +12,29 @@
static struct kmem_cache *hashtab_node_cachep;
/*
* Here we simply round the number of elements up to the nearest power of two.
* I tried also other options like rouding down or rounding to the closest
* power of two (up or down based on which is closer), but I was unable to
* find any significant difference in lookup/insert performance that would
* justify switching to a different (less intuitive) formula. It could be that
* a different formula is actually more optimal, but any future changes here
* should be supported with performance/memory usage data.
*
* The total memory used by the htable arrays (only) with Fedora policy loaded
* is approximately 163 KB at the time of writing.
*/
static u32 hashtab_compute_size(u32 nel)
{
return nel == 0 ? 0 : roundup_pow_of_two(nel);
}
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
u32 size)
u32 nel_hint)
{
struct hashtab *p;
u32 i;
u32 i, size = hashtab_compute_size(nel_hint);
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
......@@ -27,6 +44,9 @@ struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *
p->nel = 0;
p->hash_value = hash_value;
p->keycmp = keycmp;
if (!size)
return p;
p->htable = kmalloc_array(size, sizeof(*p->htable), GFP_KERNEL);
if (!p->htable) {
kfree(p);
......@@ -46,7 +66,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum)
cond_resched();
if (!h || h->nel == HASHTAB_MAX_NODES)
if (!h || !h->size || h->nel == HASHTAB_MAX_NODES)
return -EINVAL;
hvalue = h->hash_value(h, key);
......@@ -82,7 +102,7 @@ void *hashtab_search(struct hashtab *h, const void *key)
u32 hvalue;
struct hashtab_node *cur;
if (!h)
if (!h || !h->size)
return NULL;
hvalue = h->hash_value(h, key);
......
......@@ -42,7 +42,7 @@ struct hashtab_info {
*/
struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
u32 size);
u32 nel_hint);
/*
* Inserts the specified (key, datum) pair into the specified hash table.
......
......@@ -56,17 +56,6 @@ static const char *symtab_name[SYM_NUM] = {
};
#endif
static unsigned int symtab_sizes[SYM_NUM] = {
2,
32,
16,
512,
128,
16,
16,
16,
};
struct policydb_compat_info {
int version;
int sym_num;
......@@ -336,11 +325,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
static int filenametr_destroy(void *key, void *datum, void *p)
{
struct filename_trans *ft = key;
struct filename_trans_key *ft = key;
struct filename_trans_datum *next, *d = datum;
kfree(ft->name);
kfree(key);
kfree(datum);
do {
ebitmap_destroy(&d->stypes);
next = d->next;
kfree(d);
d = next;
} while (unlikely(d));
cond_resched();
return 0;
}
......@@ -406,12 +401,12 @@ static int roles_init(struct policydb *p)
static u32 filenametr_hash(struct hashtab *h, const void *k)
{
const struct filename_trans *ft = k;
const struct filename_trans_key *ft = k;
unsigned long hash;
unsigned int byte_num;
unsigned char focus;
hash = ft->stype ^ ft->ttype ^ ft->tclass;
hash = ft->ttype ^ ft->tclass;
byte_num = 0;
while ((focus = ft->name[byte_num++]))
......@@ -421,14 +416,10 @@ static u32 filenametr_hash(struct hashtab *h, const void *k)
static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
{
const struct filename_trans *ft1 = k1;
const struct filename_trans *ft2 = k2;
const struct filename_trans_key *ft1 = k1;
const struct filename_trans_key *ft2 = k2;
int v;
v = ft1->stype - ft2->stype;
if (v)
return v;
v = ft1->ttype - ft2->ttype;
if (v)
return v;
......@@ -472,54 +463,21 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
*/
static int policydb_init(struct policydb *p)
{
int i, rc;
memset(p, 0, sizeof(*p));
for (i = 0; i < SYM_NUM; i++) {
rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
if (rc)
goto out;
}
rc = avtab_init(&p->te_avtab);
if (rc)
goto out;
rc = roles_init(p);
if (rc)
goto out;
rc = cond_policydb_init(p);
if (rc)
goto out;
avtab_init(&p->te_avtab);
cond_policydb_init(p);
p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp,
(1 << 10));
if (!p->filename_trans) {
rc = -ENOMEM;
goto out;
}
p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
if (!p->range_tr) {
rc = -ENOMEM;
goto out;
}
(1 << 11));
if (!p->filename_trans)
return -ENOMEM;
ebitmap_init(&p->filename_trans_ttypes);
ebitmap_init(&p->policycaps);
ebitmap_init(&p->permissive_map);
return 0;
out:
hashtab_destroy(p->filename_trans);
hashtab_destroy(p->range_tr);
for (i = 0; i < SYM_NUM; i++) {
hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
hashtab_destroy(p->symtab[i].table);
}
return rc;
}
/*
......@@ -865,29 +823,28 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
head = p->ocontexts[OCON_ISID];
for (c = head; c; c = c->next) {
rc = -EINVAL;
if (!c->context[0].user) {
pr_err("SELinux: SID %s was never defined.\n",
c->u.name);
sidtab_destroy(s);
goto out;
}
if (c->sid[0] == SECSID_NULL || c->sid[0] > SECINITSID_NUM) {
pr_err("SELinux: Initial SID %s out of range.\n",
c->u.name);
u32 sid = c->sid[0];
const char *name = security_get_initial_sid_context(sid);
if (sid == SECSID_NULL) {
pr_err("SELinux: SID 0 was assigned a context.\n");
sidtab_destroy(s);
goto out;
}
/* Ignore initial SIDs unused by this kernel. */
if (!name)
continue;
rc = context_add_hash(p, &c->context[0]);
if (rc) {
sidtab_destroy(s);
goto out;
}
rc = sidtab_set_initial(s, c->sid[0], &c->context[0]);
rc = sidtab_set_initial(s, sid, &c->context[0]);
if (rc) {
pr_err("SELinux: unable to load initial SID %s.\n",
c->u.name);
name);
sidtab_destroy(s);
goto out;
}
......@@ -1140,12 +1097,12 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
len = le32_to_cpu(buf[0]);
comdatum->value = le32_to_cpu(buf[1]);
nel = le32_to_cpu(buf[3]);
rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE);
rc = symtab_init(&comdatum->permissions, nel);
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[3]);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
......@@ -1262,10 +1219,9 @@ static int read_cons_helper(struct policydb *p,
if (rc)
return rc;
if (p->policyvers >=
POLICYDB_VERSION_CONSTRAINT_NAMES) {
e->type_names = kzalloc(sizeof
(*e->type_names),
GFP_KERNEL);
POLICYDB_VERSION_CONSTRAINT_NAMES) {
e->type_names = kzalloc(sizeof
(*e->type_names), GFP_KERNEL);
if (!e->type_names)
return -ENOMEM;
type_set_init(e->type_names);
......@@ -1306,12 +1262,12 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
len = le32_to_cpu(buf[0]);
len2 = le32_to_cpu(buf[1]);
cladatum->value = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[4]);
rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE);
rc = symtab_init(&cladatum->permissions, nel);
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
nel = le32_to_cpu(buf[4]);
ncons = le32_to_cpu(buf[5]);
......@@ -1824,6 +1780,11 @@ static int range_read(struct policydb *p, void *fp)
return rc;
nel = le32_to_cpu(buf[0]);
p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, nel);
if (!p->range_tr)
return -ENOMEM;
for (i = 0; i < nel; i++) {
rc = -ENOMEM;
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
......@@ -1880,88 +1841,114 @@ static int range_read(struct policydb *p, void *fp)
return rc;
}
static int filename_trans_read(struct policydb *p, void *fp)
static int filename_trans_read_one(struct policydb *p, void *fp)
{
struct filename_trans *ft;
struct filename_trans_datum *otype;
char *name;
u32 nel, len;
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
char *name = NULL;
u32 len, stype, otype;
__le32 buf[4];
int rc, i;
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
return 0;
int rc;
/* length of the path component string */
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
return rc;
nel = le32_to_cpu(buf[0]);
len = le32_to_cpu(buf[0]);
for (i = 0; i < nel; i++) {
otype = NULL;
name = NULL;
/* path component string */
rc = str_read(&name, GFP_KERNEL, fp, len);
if (rc)
return rc;
rc = -ENOMEM;
ft = kzalloc(sizeof(*ft), GFP_KERNEL);
if (!ft)
goto out;
rc = next_entry(buf, fp, sizeof(u32) * 4);
if (rc)
goto out;
rc = -ENOMEM;
otype = kmalloc(sizeof(*otype), GFP_KERNEL);
if (!otype)
goto out;
stype = le32_to_cpu(buf[0]);
key.ttype = le32_to_cpu(buf[1]);
key.tclass = le32_to_cpu(buf[2]);
key.name = name;
/* length of the path component string */
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto out;
len = le32_to_cpu(buf[0]);
otype = le32_to_cpu(buf[3]);
/* path component string */
rc = str_read(&name, GFP_KERNEL, fp, len);
if (rc)
last = NULL;
datum = hashtab_search(p->filename_trans, &key);
while (datum) {
if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) {
/* conflicting/duplicate rules are ignored */
datum = NULL;
goto out;
ft->name = name;
rc = next_entry(buf, fp, sizeof(u32) * 4);
if (rc)
}
if (likely(datum->otype == otype))
break;
last = datum;
datum = datum->next;
}
if (!datum) {
rc = -ENOMEM;
datum = kmalloc(sizeof(*datum), GFP_KERNEL);
if (!datum)
goto out;
ft->stype = le32_to_cpu(buf[0]);
ft->ttype = le32_to_cpu(buf[1]);
ft->tclass = le32_to_cpu(buf[2]);
otype->otype = le32_to_cpu(buf[3]);
ebitmap_init(&datum->stypes);
datum->otype = otype;
datum->next = NULL;
rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1);
if (rc)
goto out;
if (unlikely(last)) {
last->next = datum;
} else {
rc = -ENOMEM;
ft = kmemdup(&key, sizeof(key), GFP_KERNEL);
if (!ft)
goto out;
rc = hashtab_insert(p->filename_trans, ft, otype);
if (rc) {
/*
* Do not return -EEXIST to the caller, or the system
* will not boot.
*/
if (rc != -EEXIST)
rc = hashtab_insert(p->filename_trans, ft, datum);
if (rc)
goto out;
/* But free memory to avoid memory leak. */
kfree(ft);
kfree(name);
kfree(otype);
name = NULL;
rc = ebitmap_set_bit(&p->filename_trans_ttypes,
key.ttype, 1);
if (rc)
return rc;
}
}
hash_eval(p->filename_trans, "filenametr");
return 0;
kfree(name);
return ebitmap_set_bit(&datum->stypes, stype - 1, 1);
out:
kfree(ft);
kfree(name);
kfree(otype);
kfree(datum);
return rc;
}
static int filename_trans_read(struct policydb *p, void *fp)
{
u32 nel;
__le32 buf[1];
int rc, i;
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
return 0;
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
return rc;
nel = le32_to_cpu(buf[0]);
p->filename_trans_count = nel;
for (i = 0; i < nel; i++) {
rc = filename_trans_read_one(p, fp);
if (rc)
return rc;
}
hash_eval(p->filename_trans, "filenametr");
return 0;
}
static int genfs_read(struct policydb *p, void *fp)
{
int i, j, rc;
......@@ -2390,6 +2377,17 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
nprim = le32_to_cpu(buf[0]);
nel = le32_to_cpu(buf[1]);
rc = symtab_init(&p->symtab[i], nel);
if (rc)
goto out;
if (i == SYM_ROLES) {
rc = roles_init(p);
if (rc)
goto out;
}
for (j = 0; j < nel; j++) {
rc = read_f[i](p, p->symtab[i].table, fp);
if (rc)
......@@ -3330,50 +3328,50 @@ static int range_write(struct policydb *p, void *fp)
static int filename_write_helper(void *key, void *data, void *ptr)
{
__le32 buf[4];
struct filename_trans *ft = key;
struct filename_trans_datum *otype = data;
struct filename_trans_key *ft = key;
struct filename_trans_datum *datum = data;
struct ebitmap_node *node;
void *fp = ptr;
__le32 buf[4];
int rc;
u32 len;
u32 bit, len = strlen(ft->name);
len = strlen(ft->name);
buf[0] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
do {
ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
buf[0] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
rc = put_entry(ft->name, sizeof(char), len, fp);
if (rc)
return rc;
rc = put_entry(ft->name, sizeof(char), len, fp);
if (rc)
return rc;
buf[0] = cpu_to_le32(ft->stype);
buf[1] = cpu_to_le32(ft->ttype);
buf[2] = cpu_to_le32(ft->tclass);
buf[3] = cpu_to_le32(otype->otype);
buf[0] = cpu_to_le32(bit + 1);
buf[1] = cpu_to_le32(ft->ttype);
buf[2] = cpu_to_le32(ft->tclass);
buf[3] = cpu_to_le32(datum->otype);
rc = put_entry(buf, sizeof(u32), 4, fp);
if (rc)
return rc;
rc = put_entry(buf, sizeof(u32), 4, fp);
if (rc)
return rc;
}
datum = datum->next;
} while (unlikely(datum));
return 0;
}
static int filename_trans_write(struct policydb *p, void *fp)
{
u32 nel;
__le32 buf[1];
int rc;
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
return 0;
nel = 0;
rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel);
if (rc)
return rc;
buf[0] = cpu_to_le32(nel);
buf[0] = cpu_to_le32(p->filename_trans_count);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
......@@ -3483,7 +3481,7 @@ int policydb_write(struct policydb *p, void *fp)
if (rc)
return rc;
rc = cond_write_list(p, p->cond_list, fp);
rc = cond_write_list(p, fp);
if (rc)
return rc;
......
......@@ -89,15 +89,16 @@ struct role_trans {
struct role_trans *next;
};
struct filename_trans {
u32 stype; /* current process */
struct filename_trans_key {
u32 ttype; /* parent dir context */
u16 tclass; /* class of new object */
const char *name; /* last path component */
};
struct filename_trans_datum {
u32 otype; /* expected of new object */
struct ebitmap stypes; /* bitmap of source types for this otype */
u32 otype; /* resulting type of new object */
struct filename_trans_datum *next; /* record for next otype*/
};
struct role_allow {
......@@ -267,13 +268,15 @@ struct policydb {
struct ebitmap filename_trans_ttypes;
/* actual set of filename_trans rules */
struct hashtab *filename_trans;
u32 filename_trans_count;
/* bools indexed by (value - 1) */
struct cond_bool_datum **bool_val_to_struct;
/* type enforcement conditional access vectors and transitions */
struct avtab te_cond_avtab;
/* linked list indexing te_cond_avtab by conditional */
/* array indexing te_cond_avtab by conditional */
struct cond_node *cond_list;
u32 cond_list_len;
/* role allows */
struct role_allow *role_allow;
......@@ -318,8 +321,6 @@ extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
extern int policydb_read(struct policydb *p, void *fp);
extern int policydb_write(struct policydb *p, void *fp);
#define PERM_SYMTAB_SIZE 32
#define POLICYDB_CONFIG_MLS 1
/* the config flags related to unknown classes/perms are bits 2 and 3 */
......
......@@ -46,7 +46,6 @@
#include <linux/in.h>
#include <linux/sched.h>
#include <linux/audit.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include <net/netlabel.h>
......@@ -73,7 +72,8 @@ const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
"extended_socket_class",
"always_check_network",
"cgroup_seclabel",
"nnp_nosuid_transition"
"nnp_nosuid_transition",
"genfs_seclabel_symlinks"
};
static struct selinux_ss selinux_ss;
......@@ -81,7 +81,6 @@ static struct selinux_ss selinux_ss;
void selinux_ss_init(struct selinux_ss **ss)
{
rwlock_init(&selinux_ss.policy_rwlock);
mutex_init(&selinux_ss.status_lock);
*ss = &selinux_ss;
}
......@@ -1323,23 +1322,22 @@ static int security_sid_to_context_core(struct selinux_state *state,
if (!selinux_initialized(state)) {
if (sid <= SECINITSID_NUM) {
char *scontextp;
const char *s = initial_sid_to_string[sid];
*scontext_len = strlen(initial_sid_to_string[sid]) + 1;
if (!s)
return -EINVAL;
*scontext_len = strlen(s) + 1;
if (!scontext)
goto out;
scontextp = kmemdup(initial_sid_to_string[sid],
*scontext_len, GFP_ATOMIC);
if (!scontextp) {
rc = -ENOMEM;
goto out;
}
return 0;
scontextp = kmemdup(s, *scontext_len, GFP_ATOMIC);
if (!scontextp)
return -ENOMEM;
*scontext = scontextp;
goto out;
return 0;
}
pr_err("SELinux: %s: called before initial "
"load_policy on unknown SID %d\n", __func__, sid);
rc = -EINVAL;
goto out;
return -EINVAL;
}
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
......@@ -1363,7 +1361,6 @@ static int security_sid_to_context_core(struct selinux_state *state,
out_unlock:
read_unlock(&state->ss->policy_rwlock);
out:
return rc;
}
......@@ -1553,7 +1550,9 @@ static int security_context_to_sid_core(struct selinux_state *state,
int i;
for (i = 1; i < SECINITSID_NUM; i++) {
if (!strcmp(initial_sid_to_string[i], scontext2)) {
const char *s = initial_sid_to_string[i];
if (s && !strcmp(s, scontext2)) {
*sid = i;
goto out;
}
......@@ -1693,8 +1692,8 @@ static void filename_compute_type(struct policydb *policydb,
u32 stype, u32 ttype, u16 tclass,
const char *objname)
{
struct filename_trans ft;
struct filename_trans_datum *otype;
struct filename_trans_key ft;
struct filename_trans_datum *datum;
/*
* Most filename trans rules are going to live in specific directories
......@@ -1704,14 +1703,18 @@ static void filename_compute_type(struct policydb *policydb,
if (!ebitmap_get_bit(&policydb->filename_trans_ttypes, ttype))
return;
ft.stype = stype;
ft.ttype = ttype;
ft.tclass = tclass;
ft.name = objname;
otype = hashtab_search(policydb->filename_trans, &ft);
if (otype)
newcontext->type = otype->otype;
datum = hashtab_search(policydb->filename_trans, &ft);
while (datum) {
if (ebitmap_get_bit(&datum->stypes, stype - 1)) {
newcontext->type = datum->otype;
return;
}
datum = datum->next;
}
}
static int security_compute_sid(struct selinux_state *state,
......@@ -2868,10 +2871,11 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
}
int security_get_bools(struct selinux_state *state,
int *len, char ***names, int **values)
u32 *len, char ***names, int **values)
{
struct policydb *policydb;
int i, rc;
u32 i;
int rc;
if (!selinux_initialized(state)) {
*len = 0;
......@@ -2925,12 +2929,11 @@ int security_get_bools(struct selinux_state *state,
}
int security_set_bools(struct selinux_state *state, int len, int *values)
int security_set_bools(struct selinux_state *state, u32 len, int *values)
{
struct policydb *policydb;
int i, rc;
int lenp, seqno = 0;
struct cond_node *cur;
int rc;
u32 i, lenp, seqno = 0;
write_lock_irq(&state->ss->policy_rwlock);
......@@ -2958,11 +2961,7 @@ int security_set_bools(struct selinux_state *state, int len, int *values)
policydb->bool_val_to_struct[i]->state = 0;
}
for (cur = policydb->cond_list; cur; cur = cur->next) {
rc = evaluate_cond_node(policydb, cur);
if (rc)
goto out;
}
evaluate_cond_nodes(policydb);
seqno = ++state->ss->latest_granting;
rc = 0;
......@@ -2978,11 +2977,11 @@ int security_set_bools(struct selinux_state *state, int len, int *values)
}
int security_get_bool_value(struct selinux_state *state,
int index)
u32 index)
{
struct policydb *policydb;
int rc;
int len;
u32 len;
read_lock(&state->ss->policy_rwlock);
......@@ -3002,10 +3001,10 @@ int security_get_bool_value(struct selinux_state *state,
static int security_preserve_bools(struct selinux_state *state,
struct policydb *policydb)
{
int rc, nbools = 0, *bvalues = NULL, i;
int rc, *bvalues = NULL;
char **bnames = NULL;
struct cond_bool_datum *booldatum;
struct cond_node *cur;
u32 i, nbools = 0;
rc = security_get_bools(state, &nbools, &bnames, &bvalues);
if (rc)
......@@ -3015,11 +3014,7 @@ static int security_preserve_bools(struct selinux_state *state,
if (booldatum)
booldatum->state = bvalues[i];
}
for (cur = policydb->cond_list; cur; cur = cur->next) {
rc = evaluate_cond_node(policydb, cur);
if (rc)
goto out;
}
evaluate_cond_nodes(policydb);
out:
if (bnames) {
......
......@@ -29,8 +29,6 @@ struct selinux_ss {
rwlock_t policy_rwlock;
u32 latest_granting;
struct selinux_map map;
struct page *status_page;
struct mutex status_lock;
} __randomize_layout;
void services_compute_xperms_drivers(struct extended_perms *xperms,
......
......@@ -11,7 +11,7 @@
#include <linux/mm.h>
#include <linux/mutex.h>
#include "avc.h"
#include "services.h"
#include "security.h"
/*
* The selinux_status_page shall be exposed to userspace applications
......@@ -44,12 +44,12 @@ struct page *selinux_kernel_status_page(struct selinux_state *state)
struct selinux_kernel_status *status;
struct page *result = NULL;
mutex_lock(&state->ss->status_lock);
if (!state->ss->status_page) {
state->ss->status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
mutex_lock(&state->status_lock);
if (!state->status_page) {
state->status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
if (state->ss->status_page) {
status = page_address(state->ss->status_page);
if (state->status_page) {
status = page_address(state->status_page);
status->version = SELINUX_KERNEL_STATUS_VERSION;
status->sequence = 0;
......@@ -65,8 +65,8 @@ struct page *selinux_kernel_status_page(struct selinux_state *state)
!security_get_allow_unknown(state);
}
}
result = state->ss->status_page;
mutex_unlock(&state->ss->status_lock);
result = state->status_page;
mutex_unlock(&state->status_lock);
return result;
}
......@@ -81,9 +81,9 @@ void selinux_status_update_setenforce(struct selinux_state *state,
{
struct selinux_kernel_status *status;
mutex_lock(&state->ss->status_lock);
if (state->ss->status_page) {
status = page_address(state->ss->status_page);
mutex_lock(&state->status_lock);
if (state->status_page) {
status = page_address(state->status_page);
status->sequence++;
smp_wmb();
......@@ -93,7 +93,7 @@ void selinux_status_update_setenforce(struct selinux_state *state,
smp_wmb();
status->sequence++;
}
mutex_unlock(&state->ss->status_lock);
mutex_unlock(&state->status_lock);
}
/*
......@@ -107,9 +107,9 @@ void selinux_status_update_policyload(struct selinux_state *state,
{
struct selinux_kernel_status *status;
mutex_lock(&state->ss->status_lock);
if (state->ss->status_page) {
status = page_address(state->ss->status_page);
mutex_lock(&state->status_lock);
if (state->status_page) {
status = page_address(state->status_page);
status->sequence++;
smp_wmb();
......@@ -120,5 +120,5 @@ void selinux_status_update_policyload(struct selinux_state *state,
smp_wmb();
status->sequence++;
}
mutex_unlock(&state->ss->status_lock);
mutex_unlock(&state->status_lock);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment