@@ -8,7 +8,8 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.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/policydb.o ss/services.o ss/conditional.o ss/mls.o \
+ ss/sandbox.o
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
@@ -42,7 +42,8 @@ struct security_class_mapping secclass_map[] = {
{ "compute_av", "compute_create", "compute_member",
"check_context", "load_policy", "compute_relabel",
"compute_user", "setenforce", "setbool", "setsecparam",
- "setcheckreqprot", "read_policy", "validate_trans", NULL } },
+ "setcheckreqprot", "read_policy", "validate_trans",
+ "sandbox", NULL } },
{ "process",
{ "fork", "transition", "sigchld", "sigkill",
"sigstop", "signull", "signal", "ptrace", "getsched", "setsched",
@@ -248,6 +249,8 @@ struct security_class_mapping secclass_map[] = {
{"open", "cpu", "kernel", "tracepoint", "read", "write"} },
{ "lockdown",
{ "integrity", "confidentiality", NULL } },
+ { "sandbox",
+ { "load_policy", "unload_policy"} },
{ NULL }
};
@@ -231,6 +231,12 @@ size_t security_policydb_len(struct selinux_state *state);
int security_policycap_supported(struct selinux_state *state,
unsigned int req_cap);
+int security_sandbox_load(struct selinux_state *state, u32 ssid,
+ void *data, size_t len);
+int security_sandbox_unload(struct selinux_state *state, u32 ssid,
+ void *data, size_t len);
+int security_sandbox_list(struct selinux_state *state);
+
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
@@ -1935,6 +1935,154 @@ static int sel_make_policycap(struct selinux_fs_info *fsi)
return 0;
}
+#define SEL_SANDBOX_LOAD_SIZELIMIT (32*1024*1024)
+#define SEL_SANDBOX_UNLOAD_SIZELIMIT (32*1024*1024)
+
+static ssize_t sel_sandbox_unload_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct selinux_fs_info *fsi;
+ unsigned long length;
+ u32 ssid;
+ int denied;
+ int rc;
+ void *data = NULL;
+
+ if (count == 0 || count >= SEL_SANDBOX_UNLOAD_SIZELIMIT)
+ return -ERANGE;
+
+ /* no partial writes */
+ if (*ppos != 0)
+ return -EINVAL;
+
+ ssid = current_sid();
+ denied = avc_has_perm(&selinux_state,
+ ssid, ssid,
+ SECCLASS_SECURITY, SECURITY__SANDBOX, NULL);
+ if (denied)
+ return denied;
+
+ data = vmalloc(count);
+ if (!data) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ length = copy_from_user(data, buf, count);
+ if (length != 0) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ fsi = file_inode(file)->i_sb->s_fs_info;
+ mutex_lock(&fsi->mutex);
+ rc = security_sandbox_unload(fsi->state, ssid, data, count);
+ mutex_unlock(&fsi->mutex);
+ if (rc)
+ goto out;
+
+ rc = count;
+out:
+ vfree(data);
+
+ return rc;
+}
+
+static const struct file_operations sel_sandbox_unload_ops = {
+ .write = sel_sandbox_unload_write,
+ .llseek = generic_file_llseek,
+};
+
+static ssize_t sel_sandbox_load_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+
+{
+ struct selinux_fs_info *fsi;
+ unsigned long length;
+ u32 ssid;
+ int denied;
+ int rc;
+ void *data = NULL;
+
+ if (count <= 0 || count >= SEL_SANDBOX_LOAD_SIZELIMIT)
+ return -ENOMEM;
+
+ /* no partial writes */
+ if (*ppos != 0)
+ return -EINVAL;
+
+ ssid = current_sid();
+ denied = avc_has_perm(&selinux_state,
+ ssid, ssid,
+ SECCLASS_SECURITY, SECURITY__SANDBOX, NULL);
+ if (denied)
+ return denied;
+
+ data = vmalloc(count);
+ if (!data) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ length = copy_from_user(data, buf, count);
+ if (length != 0) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ fsi = file_inode(file)->i_sb->s_fs_info;
+ mutex_lock(&fsi->mutex);
+ rc = security_sandbox_load(fsi->state, ssid, data, count);
+ mutex_unlock(&fsi->mutex);
+ if (rc)
+ goto out;
+
+ rc = count;
+out:
+ vfree(data);
+
+ return rc;
+}
+
+static const struct file_operations sel_sandbox_load_ops = {
+ .write = sel_sandbox_load_write,
+ .llseek = generic_file_llseek,
+};
+
+static int sel_make_sandbox_files(struct dentry *dir)
+{
+ struct super_block *sb = dir->d_sb;
+ struct selinux_fs_info *fsi = sb->s_fs_info;
+ int i;
+
+ static const struct tree_descr files[] = {
+ {"sandbox_load", &sel_sandbox_load_ops, 0222},
+ {"sandbox_unload", &sel_sandbox_unload_ops, 0222},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(files); i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+
+ dentry = d_alloc_name(dir, files[i].name);
+ if (!dentry)
+ return -ENOMEM;
+
+ inode = sel_make_inode(sb, S_IFREG|files[i].mode);
+ if (!inode) {
+ dput(dentry);
+ return -ENOMEM;
+ }
+
+ inode->i_fop = files[i].ops;
+ inode->i_ino = ++fsi->last_ino;
+ d_add(dentry, inode);
+ }
+
+ return 0;
+}
+
static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
unsigned long *ino)
{
@@ -2078,6 +2226,17 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
ret = sel_make_policy_nodes(fsi);
if (ret)
goto err;
+
+ dentry = sel_make_dir(sb->s_root, "sandbox", &fsi->last_ino);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto err;
+ }
+
+ ret = sel_make_sandbox_files(dentry);
+ if (ret)
+ goto err;
+
return 0;
err:
pr_err("SELinux: %s: failed while creating inodes\n",
@@ -103,7 +103,8 @@ avtab_insert_node(struct avtab *h, int hvalue,
return newnode;
}
-static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
+int avtab_insert(struct avtab *h, struct avtab_key *key,
+ struct avtab_datum *datum)
{
int hvalue;
struct avtab_node *prev, *cur, *newnode;
@@ -375,6 +376,7 @@ static uint16_t spec_order[] = {
AVTAB_ALLOWED,
AVTAB_AUDITDENY,
AVTAB_AUDITALLOW,
+ AVTAB_SANDBOXDENY,
AVTAB_TRANSITION,
AVTAB_CHANGE,
AVTAB_MEMBER,
@@ -654,6 +656,29 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp)
return rc;
}
+int avtab_map(struct avtab *h,
+ int (*apply)(struct avtab_key *k, struct avtab_datum *d,
+ void *args),
+ void *args)
+{
+ int i;
+ int rc;
+ struct avtab_node *cur;
+
+ if (!h)
+ return 0;
+
+ for (i = 0; i < h->nslot; i++) {
+ for (cur = h->htable[i]; cur; cur = cur->next) {
+ rc = apply(&cur->key, &cur->datum, args);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
void __init avtab_cache_init(void)
{
avtab_node_cachep = kmem_cache_create("avtab_node",
@@ -30,7 +30,9 @@ struct avtab_key {
#define AVTAB_ALLOWED 0x0001
#define AVTAB_AUDITALLOW 0x0002
#define AVTAB_AUDITDENY 0x0004
-#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
+#define AVTAB_SANDBOXDENY 0x1000
+#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | \
+ AVTAB_AUDITDENY | AVTAB_SANDBOXDENY)
#define AVTAB_TRANSITION 0x0010
#define AVTAB_MEMBER 0x0020
#define AVTAB_CHANGE 0x0040
@@ -105,10 +107,16 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp);
struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key,
struct avtab_datum *datum);
+int avtab_insert(struct avtab *h, struct avtab_key *key,
+ struct avtab_datum *datum);
struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key);
struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
+int avtab_map(struct avtab *h,
+ int (*apply)(struct avtab_key *k, struct avtab_datum *d,
+ void *args),
+ void *args);
#define MAX_AVTAB_HASH_BITS 16
#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
@@ -116,6 +116,41 @@ void *hashtab_search(struct hashtab *h, const void *key)
return cur->datum;
}
+struct hashtab_node *hashtab_rsearch(struct hashtab *h,
+ int (*cmp_f)(void *d, u32 value),
+ u32 value)
+{
+ struct hashtab_node *cur;
+ int i;
+
+ if (!h)
+ return NULL;
+
+ for (i = 0; i < h->size; i++) {
+ cur = h->htable[i];
+ while (cur) {
+ if (cmp_f(cur->datum, value) == 0)
+ return cur;
+ cur = cur->next;
+ }
+ }
+
+ return NULL;
+
+}
+
+void *hashtab_compare_search(struct hashtab *base, struct hashtab *child,
+ int (cmp_f)(void *d, u32 value),
+ u32 value)
+{
+ struct hashtab_node *node;
+
+ node = hashtab_rsearch(child, cmp_f, value);
+ if (!node)
+ return NULL;
+ return hashtab_search(base, node->key);
+}
+
void hashtab_destroy(struct hashtab *h)
{
u32 i;
@@ -62,6 +62,14 @@ int hashtab_insert(struct hashtab *h, void *k, void *d);
*/
void *hashtab_search(struct hashtab *h, const void *k);
+struct hashtab_node *hashtab_rsearch(struct hashtab *h,
+ int (*cmp_f)(void *d, u32 value),
+ u32 value);
+
+void *hashtab_compare_search(struct hashtab *base, struct hashtab *child,
+ int (*cmp_f)(void *d, u32 value),
+ u32 value);
+
/*
* Destroys the specified hash table.
*/
@@ -2541,6 +2541,48 @@ int policydb_read(struct policydb *p, void *fp)
goto out;
}
+int policydb_read_symbols(struct policydb *p, struct symtab *s,
+ int symbol_index, void *fp)
+{
+ __le32 data;
+ u32 nprim, nel;
+ int rc, i;
+
+ rc = next_entry(&data, fp, sizeof(__le32));
+ if (rc)
+ return rc;
+ nprim = le32_to_cpu(data);
+
+ rc = next_entry(&data, fp, sizeof(__le32));
+ if (rc)
+ return rc;
+ nel = le32_to_cpu(data);
+
+ rc = symtab_init(s, nel);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < nel; i++) {
+ rc = read_f[symbol_index](p, s->table, fp);
+ if (rc)
+ goto bad;
+ }
+
+ return 0;
+bad:
+ policydb_destroy_symbols(s, symbol_index);
+ s->table = NULL;
+ return rc;
+}
+
+void policydb_destroy_symbols(struct symtab *s, int symbol_index)
+{
+ if (!s)
+ return;
+ hashtab_map(s->table, destroy_f[symbol_index], NULL);
+ hashtab_destroy(s->table);
+}
+
/*
* Write a MLS level structure to a policydb binary
* representation file.
@@ -29,6 +29,7 @@
#include "mls_types.h"
#include "context.h"
#include "constraint.h"
+#include "sandbox.h"
/*
* A datum type is defined for each kind of symbol
@@ -320,6 +321,9 @@ extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
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);
+extern int policydb_read_symbols(struct policydb *p, struct symtab *s,
+ int symbol_index, void *fp);
+extern void policydb_destroy_symbols(struct symtab *s, int symbol_index);
#define POLICYDB_CONFIG_MLS 1
new file mode 100644
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+
+#include "flask.h"
+#include "avc.h"
+#include "avc_ss.h"
+#include "security.h"
+#include "services.h"
+#include "policydb.h"
+#include "sandbox.h"
+#include "avtab.h"
+#include "symtab.h"
+#include "hashtab.h"
+
+#define SANDBOX_CONTEXT_LENGTH PAGE_SIZE
+
+struct fix_args {
+ struct policydb *p;
+ struct symtab *symbols;
+ struct sandbox *s;
+};
+
+/*
+ * It is expected that the caller frees the string.
+ */
+static int context_read(char **context_str, size_t *context_strlen, void *fp)
+{
+ int rc;
+ __le32 data;
+ char *str = NULL;
+ size_t strlen = 0;
+
+ /* read the context length */
+ rc = next_entry(&data, fp, sizeof(data));
+ if (rc)
+ goto out;
+
+ strlen = le32_to_cpu(data);
+ if (strlen == 0 || strlen >= SANDBOX_CONTEXT_LENGTH) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* read context */
+ str = kmalloc(strlen + 1, GFP_KERNEL);
+ if (!str) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = next_entry(str, fp, strlen);
+ if (rc) {
+ kfree(str);
+ goto out;
+ }
+
+ /* null-terminate */
+ str[strlen] = '\0';
+
+out:
+ *context_strlen = strlen;
+ *context_str = str;
+ return rc;
+}
+
+static int type_value_compare(void *d, u32 value)
+{
+ struct type_datum *datum = d;
+
+ if (datum->value == value)
+ return 0;
+ return -EINVAL;
+}
+
+static int class_value_compare(void *d, u32 value)
+{
+ struct class_datum *datum = d;
+
+ if (datum->value == value)
+ return 0;
+ return -EINVAL;
+}
+
+static int perm_value_compare(void *d, u32 value)
+{
+ struct perm_datum *datum = d;
+
+ if (datum->value == value)
+ return 0;
+ return -EINVAL;
+}
+
+void sandbox_destroy(struct sandbox *s)
+{
+ if (!s)
+ return;
+ kfree(s->context_str);
+ avtab_destroy(&s->te);
+ kfree(s);
+}
+
+void sandbox_detach(struct sidtab_entry *entry)
+{
+ struct sandbox *s_old;
+
+ s_old = sidtab_sandbox_detach(entry);
+ if (s_old)
+ sandbox_destroy(s_old);
+}
+
+int sandbox_attach(struct sidtab_entry *entry, struct sandbox *s)
+{
+ sandbox_detach(entry);
+ return sidtab_sandbox_attach(entry, s);
+}
+
+int sandbox_fix_node(struct avtab_key *k, struct avtab_datum *d, void *args)
+{
+ struct fix_args *fargs = args;
+ struct policydb *p = fargs->p;
+ struct symtab *symbols = fargs->symbols;
+ struct sandbox *s_new = fargs->s;
+
+ struct avtab_key key;
+ struct avtab_datum datum;
+
+ struct type_datum *base_type;
+ struct class_datum *base_class;
+ struct perm_datum *base_perm;
+ struct common_datum *base_common;
+
+ struct hashtab_node *rnode;
+ struct class_datum *sandbox_class;
+ struct common_datum *sandbox_common;
+ int rc, i;
+
+ /* fix source type */
+ base_type = hashtab_compare_search(p->symtab[SYM_TYPES].table,
+ symbols[SYM_TYPES].table,
+ type_value_compare,
+ k->source_type);
+ if (!base_type)
+ return -EINVAL;
+ key.source_type = base_type->value;
+
+ /* fix target type */
+ base_type = hashtab_compare_search(p->symtab[SYM_TYPES].table,
+ symbols[SYM_TYPES].table,
+ type_value_compare,
+ k->target_type);
+ if (!base_type)
+ return -EINVAL;
+ key.target_type = base_type->value;
+
+ /* fix class */
+ base_class = hashtab_compare_search(p->symtab[SYM_CLASSES].table,
+ symbols[SYM_CLASSES].table,
+ class_value_compare,
+ k->target_class);
+ if (!base_class)
+ return -EINVAL;
+ key.target_class = base_class->value;
+
+ /* fix specified */
+ key.specified = k->specified;
+
+ /* fix data */
+ rnode = hashtab_rsearch(symbols[SYM_CLASSES].table,
+ class_value_compare,
+ k->target_class);
+ if (!rnode)
+ return -EINVAL;
+
+ sandbox_class = rnode->datum;
+ sandbox_common = sandbox_class->comdatum;
+ base_common = base_class->comdatum;
+ base_perm = NULL;
+ datum.u.data = 0x00;
+ rnode = NULL;
+
+ for (i = 0; i < (sizeof(u32)*8); i++) {
+ if (d->u.data & (1<<i)) {
+ if (sandbox_common)
+ rnode = hashtab_rsearch(
+ sandbox_common->permissions.table,
+ perm_value_compare,
+ i+1);
+ if (!rnode)
+ rnode = hashtab_rsearch(
+ sandbox_class->permissions.table,
+ perm_value_compare,
+ i+1);
+ if (!rnode)
+ return -EINVAL;
+
+ if (base_common)
+ base_perm = hashtab_search(
+ base_common->permissions.table,
+ rnode->key);
+ if (!base_perm)
+ base_perm = hashtab_search(
+ base_class->permissions.table,
+ rnode->key);
+ if (!base_perm)
+ return -EINVAL;
+ datum.u.data |= (1<<(base_perm->value-1));
+ }
+ }
+
+ rc = avtab_insert(&s_new->te, &key, &datum);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+int sandbox_fix(struct policydb *p, struct symtab *symbols, struct sandbox **sp)
+{
+ struct sandbox *s;
+ struct sandbox *s_new;
+ int rc;
+ struct fix_args args = { p, symbols, NULL };
+
+ s = *sp;
+ s_new = kmalloc(sizeof(*s_new), GFP_KERNEL);
+ if (!s_new)
+ return -ENOMEM;
+
+ s_new->context_strlen = s->context_strlen;
+ s_new->context_str = kstrdup(s->context_str, GFP_KERNEL);
+ s_new->mode = s->mode;
+ s_new->te.htable = NULL;
+ s_new->te.nel = 0;
+ if (s_new->context_str == NULL) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+
+ rc = avtab_alloc(&s_new->te, s->te.nel);
+ if (rc)
+ goto bad;
+ args.s = s_new;
+
+ rc = avtab_map(&s->te, sandbox_fix_node, &args);
+ if (rc)
+ goto bad;
+
+ *sp = s_new;
+ sandbox_destroy(s);
+ return 0;
+
+bad:
+ sandbox_destroy(s_new);
+ return rc;
+}
+
+int sandbox_load(struct selinux_state *state, u32 ssid, void *fp)
+{
+ u32 tsid;
+ int rc;
+ struct sandbox *s = NULL;
+ char *context_str = NULL;
+ size_t context_strlen = 0;
+ struct sidtab_entry *entry = NULL;
+ struct policydb *policydb = &state->ss->policydb;
+ struct symtab symbols[SYM_NUM] = {{ NULL, 0 }};
+ u32 mode = 0;
+
+ rc = context_read(&context_str, &context_strlen, fp);
+ if (rc)
+ return rc;
+
+ rc = security_context_to_sid(state, context_str, context_strlen,
+ &tsid, GFP_KERNEL);
+ if (rc)
+ goto exit;
+
+ rc = avc_has_perm(state,
+ ssid, tsid,
+ SECCLASS_SANDBOX, SANDBOX__LOAD_POLICY, NULL);
+ if (rc)
+ goto exit;
+
+ rc = next_entry(&mode, fp, sizeof(mode));
+ if (rc)
+ goto exit;
+ if (mode != SANDBOX_MODE_ALLOW && mode != SANDBOX_MODE_DENY) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = policydb_read_symbols(policydb, &symbols[SYM_COMMONS],
+ SYM_COMMONS, fp);
+ if (rc)
+ goto exit;
+
+ rc = policydb_read_symbols(policydb, &symbols[SYM_CLASSES],
+ SYM_CLASSES, fp);
+ if (rc)
+ goto exit;
+
+ rc = policydb_read_symbols(policydb, &symbols[SYM_TYPES],
+ SYM_TYPES, fp);
+ if (rc)
+ goto exit;
+
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ s->context_strlen = context_strlen;
+ s->context_str = kstrdup(context_str, GFP_KERNEL);
+ s->mode = mode;
+ s->te.htable = NULL;
+ s->te.nel = 0;
+ if (s->context_str == NULL) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+
+ rc = avtab_read(&s->te, fp, policydb);
+ if (rc)
+ goto bad;
+
+ rc = sandbox_fix(policydb, symbols, &s);
+ if (rc)
+ goto bad;
+
+ entry = sidtab_search_entry(state->ss->sidtab, tsid);
+ if (!entry) {
+ rc = -EINVAL;
+ goto bad;
+ }
+
+ write_lock_irq(&state->ss->policy_rwlock);
+ rc = sandbox_attach(entry, s);
+ write_unlock_irq(&state->ss->policy_rwlock);
+ if (rc)
+ goto bad;
+
+exit:
+ kfree(context_str);
+ policydb_destroy_symbols(&symbols[SYM_COMMONS], SYM_COMMONS);
+ policydb_destroy_symbols(&symbols[SYM_CLASSES], SYM_CLASSES);
+ policydb_destroy_symbols(&symbols[SYM_TYPES], SYM_TYPES);
+ return rc;
+
+bad:
+ sandbox_destroy(s);
+ goto exit;
+}
+
+int sandbox_unload(struct selinux_state *state, u32 ssid, void *fp)
+{
+ u32 tsid;
+ int rc;
+ char *context_str = NULL;
+ size_t context_strlen = 0;
+ struct sidtab_entry *entry = NULL;
+
+ rc = context_read(&context_str, &context_strlen, fp);
+ if (rc)
+ return rc;
+
+ rc = security_context_to_sid(state, context_str, context_strlen,
+ &tsid, GFP_KERNEL);
+ kfree(context_str);
+ context_str = NULL;
+ context_strlen = 0;
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(state,
+ ssid, tsid,
+ SECCLASS_SANDBOX, SANDBOX__UNLOAD_POLICY, NULL);
+ if (rc)
+ return rc;
+
+ entry = sidtab_search_entry(state->ss->sidtab, tsid);
+ if (!entry)
+ return -EINVAL;
+
+ write_lock_irq(&state->ss->policy_rwlock);
+ sandbox_detach(entry);
+ write_unlock_irq(&state->ss->policy_rwlock);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _SS_SANDBOX_H_
+#define _SS_SANDBOX_H_
+
+#include "security.h"
+#include "symtab.h"
+#include "avtab.h"
+
+#define SANDBOX_MODE_ALLOW 0x00
+#define SANDBOX_MODE_DENY 0x01
+
+struct sandbox {
+ char *context_str;
+ u32 context_strlen;
+ u32 mode;
+ struct avtab te;
+};
+
+int sandbox_load(struct selinux_state *state, u32 ssid, void *fp);
+int sandbox_unload(struct selinux_state *state, u32 ssid, void *fp);
+void sandbox_destroy(struct sandbox *s);
+
+#endif /* _SS_SANDBOX_H_ */
@@ -101,7 +101,8 @@ static void context_struct_compute_av(struct policydb *policydb,
struct context *tcontext,
u16 tclass,
struct av_decision *avd,
- struct extended_perms *xperms);
+ struct extended_perms *xperms,
+ struct sandbox *sandbox);
static int selinux_set_mapping(struct policydb *pol,
struct security_class_mapping *map,
@@ -571,7 +572,7 @@ static void type_attribute_bounds_av(struct policydb *policydb,
tcontextp,
tclass,
&lo_avd,
- NULL);
+ NULL, NULL);
masked = ~lo_avd.allowed & avd->allowed;
@@ -620,7 +621,8 @@ static void context_struct_compute_av(struct policydb *policydb,
struct context *tcontext,
u16 tclass,
struct av_decision *avd,
- struct extended_perms *xperms)
+ struct extended_perms *xperms,
+ struct sandbox *sandbox)
{
struct constraint_node *constraint;
struct role_allow *ra;
@@ -629,6 +631,10 @@ static void context_struct_compute_av(struct policydb *policydb,
struct class_datum *tclass_datum;
struct ebitmap *sattr, *tattr;
struct ebitmap_node *snode, *tnode;
+ u32 sandbox_allowed = 0;
+ u32 sandbox_deny = 0;
+ u32 sandbox_auditallow = 0;
+ u32 sandbox_auditdeny = 0xffffffff;
unsigned int i, j;
avd->allowed = 0;
@@ -655,6 +661,7 @@ static void context_struct_compute_av(struct policydb *policydb,
avkey.specified = AVTAB_AV | AVTAB_XPERMS;
sattr = &policydb->type_attr_map_array[scontext->type - 1];
tattr = &policydb->type_attr_map_array[tcontext->type - 1];
+
ebitmap_for_each_positive_bit(sattr, snode, i) {
ebitmap_for_each_positive_bit(tattr, tnode, j) {
avkey.source_type = i + 1;
@@ -673,13 +680,45 @@ static void context_struct_compute_av(struct policydb *policydb,
services_compute_xperms_drivers(xperms, node);
}
+
/* Check conditional av table for additional permissions */
cond_compute_av(&policydb->te_cond_avtab, &avkey,
avd, xperms);
+ /* Compute sandbox policy */
+ if (likely(!sandbox))
+ continue;
+ for (node = avtab_search_node(&sandbox->te, &avkey);
+ node;
+ node = avtab_search_node_next(node,
+ avkey.specified)) {
+ if (node->key.specified == AVTAB_ALLOWED)
+ sandbox_allowed |= node->datum.u.data;
+ else if (node->key.specified ==
+ AVTAB_SANDBOXDENY)
+ sandbox_deny |= node->datum.u.data;
+ else if (node->key.specified ==
+ AVTAB_AUDITALLOW)
+ sandbox_auditallow |=
+ node->datum.u.data;
+ else if (node->key.specified ==
+ AVTAB_AUDITDENY)
+ sandbox_auditdeny &= node->datum.u.data;
+ /* TODO: add support for xperms */
+ }
}
}
+ if (sandbox != NULL) {
+ /* Apply sandbox restrictions */
+ if (sandbox->mode == SANDBOX_MODE_DENY)
+ avd->allowed &= (~sandbox_deny);
+ else
+ avd->allowed &= sandbox_allowed;
+ avd->auditallow &= sandbox_auditallow;
+ avd->auditdeny &= sandbox_auditdeny;
+ }
+
/*
* Remove any permissions prohibited by a constraint (this includes
* the MLS policy).
@@ -1106,7 +1145,9 @@ void security_compute_av(struct selinux_state *state,
struct policydb *policydb;
struct sidtab *sidtab;
u16 tclass;
+ struct sidtab_entry *entry = NULL;
struct context *scontext = NULL, *tcontext = NULL;
+ struct sandbox *sandbox = NULL;
read_lock(&state->ss->policy_rwlock);
avd_init(state, avd);
@@ -1117,13 +1158,18 @@ void security_compute_av(struct selinux_state *state,
policydb = &state->ss->policydb;
sidtab = state->ss->sidtab;
- scontext = sidtab_search(sidtab, ssid);
- if (!scontext) {
+ entry = sidtab_search_entry(sidtab, ssid);
+ if (!entry) {
pr_err("SELinux: %s: unrecognized SID %d\n",
__func__, ssid);
goto out;
}
+ sandbox = entry->sandbox;
+ scontext = &entry->context;
+ if (!scontext)
+ goto out;
+
/* permissive domain? */
if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
avd->flags |= AVD_FLAGS_PERMISSIVE;
@@ -1142,7 +1188,7 @@ void security_compute_av(struct selinux_state *state,
goto out;
}
context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
- xperms);
+ xperms, sandbox);
map_decision(&state->ss->map, orig_tclass, avd,
policydb->allow_unknown);
out:
@@ -1161,7 +1207,9 @@ void security_compute_av_user(struct selinux_state *state,
{
struct policydb *policydb;
struct sidtab *sidtab;
+ struct sidtab_entry *entry = NULL;
struct context *scontext = NULL, *tcontext = NULL;
+ struct sandbox *sandbox = NULL;
read_lock(&state->ss->policy_rwlock);
avd_init(state, avd);
@@ -1171,13 +1219,18 @@ void security_compute_av_user(struct selinux_state *state,
policydb = &state->ss->policydb;
sidtab = state->ss->sidtab;
- scontext = sidtab_search(sidtab, ssid);
- if (!scontext) {
+ entry = sidtab_search_entry(sidtab, ssid);
+ if (!entry) {
pr_err("SELinux: %s: unrecognized SID %d\n",
__func__, ssid);
goto out;
}
+ sandbox = entry->sandbox;
+ scontext = &entry->context;
+ if (!scontext)
+ goto out;
+
/* permissive domain? */
if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
avd->flags |= AVD_FLAGS_PERMISSIVE;
@@ -1196,7 +1249,7 @@ void security_compute_av_user(struct selinux_state *state,
}
context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
- NULL);
+ NULL, sandbox);
out:
read_unlock(&state->ss->policy_rwlock);
return;
@@ -3787,3 +3840,57 @@ int security_read_policy(struct selinux_state *state,
return 0;
}
+
+int security_sandbox_unload(struct selinux_state *state, u32 ssid,
+ void *data, size_t len)
+{
+ struct policy_file file = { data, len }, *fp = &file;
+ int rc = 0;
+ u32 seqno;
+
+ if (!state->initialized)
+ return -EINVAL;
+
+ /* unload */
+ rc = sandbox_unload(state, ssid, fp);
+ if (rc)
+ return rc;
+
+ write_lock_irq(&state->ss->policy_rwlock);
+ seqno = ++state->ss->latest_granting;
+ write_unlock_irq(&state->ss->policy_rwlock);
+
+ /* reset AVC - this will clear the cache */
+ avc_ss_reset(state->avc, seqno);
+ selnl_notify_policyload(seqno);
+ selinux_status_update_policyload(state, seqno);
+
+ return 0;
+}
+
+int security_sandbox_load(struct selinux_state *state, u32 ssid,
+ void *data, size_t len)
+{
+ struct policy_file file = { data, len }, *fp = &file;
+ int rc = 0;
+ u32 seqno;
+
+ if (!state->initialized)
+ return -EINVAL;
+
+ /* load */
+ rc = sandbox_load(state, ssid, fp);
+ if (rc)
+ return rc;
+
+ write_lock_irq(&state->ss->policy_rwlock);
+ seqno = ++state->ss->latest_granting;
+ write_unlock_irq(&state->ss->policy_rwlock);
+
+ /* reset AVC - this will clear the cache */
+ avc_ss_reset(state->avc, seqno);
+ selnl_notify_policyload(seqno);
+ selinux_status_update_policyload(state, seqno);
+
+ return 0;
+}
@@ -88,6 +88,7 @@ int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
isid->entry.cache = NULL;
#endif
+ isid->entry.sandbox = NULL;
isid->set = 1;
/*
@@ -292,6 +293,7 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
goto out_unlock;
dst->sid = index_to_sid(count);
+ dst->sandbox = NULL;
rc = context_cpy(&dst->context, context);
if (rc)
@@ -465,6 +467,7 @@ static void sidtab_destroy_entry(struct sidtab_entry *entry)
#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
kfree(rcu_dereference_raw(entry->cache));
#endif
+ sandbox_destroy(entry->sandbox);
}
static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
@@ -595,4 +598,28 @@ int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
return rc;
}
+int sidtab_sandbox_attach(struct sidtab_entry *entry,
+ struct sandbox *sandbox)
+{
+ if (!entry)
+ return -EINVAL;
+
+ entry->sandbox = sandbox;
+
+ return 0;
+}
+
+struct sandbox *sidtab_sandbox_detach(struct sidtab_entry *entry)
+{
+ struct sandbox *sandbox;
+
+ if (!entry)
+ return NULL;
+
+ sandbox = entry->sandbox;
+ entry->sandbox = NULL;
+
+ return sandbox;
+}
+
#endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */
@@ -16,6 +16,7 @@
#include <linux/hashtable.h>
#include "context.h"
+#include "sandbox.h"
struct sidtab_entry {
u32 sid;
@@ -24,6 +25,7 @@ struct sidtab_entry {
struct sidtab_str_cache __rcu *cache;
#endif
struct hlist_node list;
+ struct sandbox *sandbox;
};
union sidtab_entry_inner {
@@ -147,6 +149,10 @@ static inline int sidtab_sid2str_get(struct sidtab *s,
}
#endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */
+int sidtab_sandbox_attach(struct sidtab_entry *entry,
+ struct sandbox *sandbox);
+struct sandbox *sidtab_sandbox_detach(struct sidtab_entry *entry);
+
#endif /* _SS_SIDTAB_H_ */