1071 lines
29 KiB
C
1071 lines
29 KiB
C
#include <linux/gfp.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/version.h>
|
|
|
|
#include "sepolicy.h"
|
|
#include "../klog.h" // IWYU pragma: keep
|
|
#include "ss/symtab.h"
|
|
#include "../kernel_compat.h" // Add check Huawei Device
|
|
|
|
#define KSU_SUPPORT_ADD_TYPE
|
|
|
|
//////////////////////////////////////////////////////
|
|
// Declaration
|
|
//////////////////////////////////////////////////////
|
|
|
|
static struct avtab_node *get_avtab_node(struct policydb *db,
|
|
struct avtab_key *key,
|
|
struct avtab_extended_perms *xperms);
|
|
|
|
static bool add_rule(struct policydb *db, const char *s, const char *t,
|
|
const char *c, const char *p, int effect, bool invert);
|
|
|
|
static void add_rule_raw(struct policydb *db, struct type_datum *src,
|
|
struct type_datum *tgt, struct class_datum *cls,
|
|
struct perm_datum *perm, int effect, bool invert);
|
|
|
|
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
|
struct type_datum *tgt, struct class_datum *cls,
|
|
uint16_t low, uint16_t high, int effect,
|
|
bool invert);
|
|
static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
|
|
const char *c, const char *range, int effect,
|
|
bool invert);
|
|
|
|
static bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
|
const char *c, const char *d, int effect);
|
|
|
|
static bool add_filename_trans(struct policydb *db, const char *s,
|
|
const char *t, const char *c, const char *d,
|
|
const char *o);
|
|
|
|
static bool add_genfscon(struct policydb *db, const char *fs_name,
|
|
const char *path, const char *context);
|
|
|
|
static bool add_type(struct policydb *db, const char *type_name, bool attr);
|
|
|
|
static bool set_type_state(struct policydb *db, const char *type_name,
|
|
bool permissive);
|
|
|
|
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
|
struct type_datum *attr);
|
|
|
|
static bool add_typeattribute(struct policydb *db, const char *type,
|
|
const char *attr);
|
|
|
|
//////////////////////////////////////////////////////
|
|
// Implementation
|
|
//////////////////////////////////////////////////////
|
|
|
|
// Invert is adding rules for auditdeny; in other cases, invert is removing
|
|
// rules
|
|
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
|
|
|
|
#define ksu_hash_for_each(node_ptr, n_slot, cur) \
|
|
int i; \
|
|
for (i = 0; i < n_slot; ++i) \
|
|
for (cur = node_ptr[i]; cur; cur = cur->next)
|
|
|
|
// htable is a struct instead of pointer above 5.8.0:
|
|
// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
|
#define ksu_hashtab_for_each(htab, cur) \
|
|
ksu_hash_for_each(htab.htable, htab.size, cur)
|
|
#else
|
|
#define ksu_hashtab_for_each(htab, cur) \
|
|
ksu_hash_for_each(htab->htable, htab->size, cur)
|
|
#endif
|
|
|
|
// symtab_search is introduced on 5.9.0:
|
|
// https://elixir.bootlin.com/linux/v5.9-rc1/source/security/selinux/ss/symtab.h
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
|
|
#define symtab_search(s, name) hashtab_search((s)->table, name)
|
|
#define symtab_insert(s, name, datum) hashtab_insert((s)->table, name, datum)
|
|
#endif
|
|
|
|
#define avtab_for_each(avtab, cur) \
|
|
ksu_hash_for_each(avtab.htable, avtab.nslot, cur);
|
|
|
|
static struct avtab_node *get_avtab_node(struct policydb *db,
|
|
struct avtab_key *key,
|
|
struct avtab_extended_perms *xperms)
|
|
{
|
|
struct avtab_node *node;
|
|
|
|
/* AVTAB_XPERMS entries are not necessarily unique */
|
|
if (key->specified & AVTAB_XPERMS) {
|
|
bool match = false;
|
|
node = avtab_search_node(&db->te_avtab, key);
|
|
while (node) {
|
|
if ((node->datum.u.xperms->specified ==
|
|
xperms->specified) &&
|
|
(node->datum.u.xperms->driver == xperms->driver)) {
|
|
match = true;
|
|
break;
|
|
}
|
|
node = avtab_search_node_next(node, key->specified);
|
|
}
|
|
if (!match)
|
|
node = NULL;
|
|
} else {
|
|
node = avtab_search_node(&db->te_avtab, key);
|
|
}
|
|
|
|
if (!node) {
|
|
struct avtab_datum avdatum = {};
|
|
/*
|
|
* AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for
|
|
* others. Initialize the data accordingly.
|
|
*/
|
|
if (key->specified & AVTAB_XPERMS) {
|
|
avdatum.u.xperms = xperms;
|
|
} else {
|
|
avdatum.u.data =
|
|
key->specified == AVTAB_AUDITDENY ? ~0U : 0U;
|
|
}
|
|
/* this is used to get the node - insertion is actually unique */
|
|
node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum);
|
|
|
|
int grow_size = sizeof(struct avtab_key);
|
|
grow_size += sizeof(struct avtab_datum);
|
|
if (key->specified & AVTAB_XPERMS) {
|
|
grow_size += sizeof(u8);
|
|
grow_size += sizeof(u8);
|
|
grow_size += sizeof(u32) *
|
|
ARRAY_SIZE(avdatum.u.xperms->perms.p);
|
|
}
|
|
db->len += grow_size;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static bool add_rule(struct policydb *db, const char *s, const char *t,
|
|
const char *c, const char *p, int effect, bool invert)
|
|
{
|
|
struct type_datum *src = NULL, *tgt = NULL;
|
|
struct class_datum *cls = NULL;
|
|
struct perm_datum *perm = NULL;
|
|
|
|
if (s) {
|
|
src = symtab_search(&db->p_types, s);
|
|
if (src == NULL) {
|
|
pr_info("source type %s does not exist\n", s);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (t) {
|
|
tgt = symtab_search(&db->p_types, t);
|
|
if (tgt == NULL) {
|
|
pr_info("target type %s does not exist\n", t);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (c) {
|
|
cls = symtab_search(&db->p_classes, c);
|
|
if (cls == NULL) {
|
|
pr_info("class %s does not exist\n", c);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (p) {
|
|
if (c == NULL) {
|
|
pr_info("No class is specified, cannot add perm [%s] \n",
|
|
p);
|
|
return false;
|
|
}
|
|
|
|
perm = symtab_search(&cls->permissions, p);
|
|
if (perm == NULL && cls->comdatum != NULL) {
|
|
perm = symtab_search(&cls->comdatum->permissions, p);
|
|
}
|
|
if (perm == NULL) {
|
|
pr_info("perm %s does not exist in class %s\n", p, c);
|
|
return false;
|
|
}
|
|
}
|
|
add_rule_raw(db, src, tgt, cls, perm, effect, invert);
|
|
return true;
|
|
}
|
|
|
|
static void add_rule_raw(struct policydb *db, struct type_datum *src,
|
|
struct type_datum *tgt, struct class_datum *cls,
|
|
struct perm_datum *perm, int effect, bool invert)
|
|
{
|
|
if (src == NULL) {
|
|
struct hashtab_node *node;
|
|
if (strip_av(effect, invert)) {
|
|
ksu_hashtab_for_each(db->p_types.table, node)
|
|
{
|
|
add_rule_raw(db,
|
|
(struct type_datum *)node->datum,
|
|
tgt, cls, perm, effect, invert);
|
|
};
|
|
} else {
|
|
ksu_hashtab_for_each(db->p_types.table, node)
|
|
{
|
|
struct type_datum *type =
|
|
(struct type_datum *)(node->datum);
|
|
if (type->attribute) {
|
|
add_rule_raw(db, type, tgt, cls, perm,
|
|
effect, invert);
|
|
}
|
|
};
|
|
}
|
|
} else if (tgt == NULL) {
|
|
struct hashtab_node *node;
|
|
if (strip_av(effect, invert)) {
|
|
ksu_hashtab_for_each(db->p_types.table, node)
|
|
{
|
|
add_rule_raw(db, src,
|
|
(struct type_datum *)node->datum,
|
|
cls, perm, effect, invert);
|
|
};
|
|
} else {
|
|
ksu_hashtab_for_each(db->p_types.table, node)
|
|
{
|
|
struct type_datum *type =
|
|
(struct type_datum *)(node->datum);
|
|
if (type->attribute) {
|
|
add_rule_raw(db, src, type, cls, perm,
|
|
effect, invert);
|
|
}
|
|
};
|
|
}
|
|
} else if (cls == NULL) {
|
|
struct hashtab_node *node;
|
|
ksu_hashtab_for_each(db->p_classes.table, node)
|
|
{
|
|
add_rule_raw(db, src, tgt,
|
|
(struct class_datum *)node->datum, perm,
|
|
effect, invert);
|
|
}
|
|
} else {
|
|
struct avtab_key key;
|
|
key.source_type = src->value;
|
|
key.target_type = tgt->value;
|
|
key.target_class = cls->value;
|
|
key.specified = effect;
|
|
|
|
struct avtab_node *node = get_avtab_node(db, &key, NULL);
|
|
if (invert) {
|
|
if (perm)
|
|
node->datum.u.data &=
|
|
~(1U << (perm->value - 1));
|
|
else
|
|
node->datum.u.data = 0U;
|
|
} else {
|
|
if (perm)
|
|
node->datum.u.data |= 1U << (perm->value - 1);
|
|
else
|
|
node->datum.u.data = ~0U;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ioctl_driver(x) (x >> 8 & 0xFF)
|
|
#define ioctl_func(x) (x & 0xFF)
|
|
|
|
#define xperm_test(x, p) (1 & (p[x >> 5] >> (x & 0x1f)))
|
|
#define xperm_set(x, p) (p[x >> 5] |= (1 << (x & 0x1f)))
|
|
#define xperm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f)))
|
|
|
|
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
|
struct type_datum *tgt, struct class_datum *cls,
|
|
uint16_t low, uint16_t high, int effect,
|
|
bool invert)
|
|
{
|
|
if (src == NULL) {
|
|
struct hashtab_node *node;
|
|
ksu_hashtab_for_each(db->p_types.table, node)
|
|
{
|
|
struct type_datum *type =
|
|
(struct type_datum *)(node->datum);
|
|
if (type->attribute) {
|
|
add_xperm_rule_raw(db, type, tgt, cls, low,
|
|
high, effect, invert);
|
|
}
|
|
};
|
|
} else if (tgt == NULL) {
|
|
struct hashtab_node *node;
|
|
ksu_hashtab_for_each(db->p_types.table, node)
|
|
{
|
|
struct type_datum *type =
|
|
(struct type_datum *)(node->datum);
|
|
if (type->attribute) {
|
|
add_xperm_rule_raw(db, src, type, cls, low,
|
|
high, effect, invert);
|
|
}
|
|
};
|
|
} else if (cls == NULL) {
|
|
struct hashtab_node *node;
|
|
ksu_hashtab_for_each(db->p_classes.table, node)
|
|
{
|
|
add_xperm_rule_raw(db, src, tgt,
|
|
(struct class_datum *)(node->datum),
|
|
low, high, effect, invert);
|
|
};
|
|
} else {
|
|
struct avtab_key key;
|
|
key.source_type = src->value;
|
|
key.target_type = tgt->value;
|
|
key.target_class = cls->value;
|
|
key.specified = effect;
|
|
|
|
struct avtab_datum *datum;
|
|
struct avtab_node *node;
|
|
struct avtab_extended_perms xperms;
|
|
|
|
memset(&xperms, 0, sizeof(xperms));
|
|
if (ioctl_driver(low) != ioctl_driver(high)) {
|
|
xperms.specified = AVTAB_XPERMS_IOCTLDRIVER;
|
|
xperms.driver = 0;
|
|
} else {
|
|
xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION;
|
|
xperms.driver = ioctl_driver(low);
|
|
}
|
|
int i;
|
|
if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) {
|
|
for (i = ioctl_driver(low); i <= ioctl_driver(high);
|
|
++i) {
|
|
if (invert)
|
|
xperm_clear(i, xperms.perms.p);
|
|
else
|
|
xperm_set(i, xperms.perms.p);
|
|
}
|
|
} else {
|
|
for (i = ioctl_func(low); i <= ioctl_func(high); ++i) {
|
|
if (invert)
|
|
xperm_clear(i, xperms.perms.p);
|
|
else
|
|
xperm_set(i, xperms.perms.p);
|
|
}
|
|
}
|
|
|
|
node = get_avtab_node(db, &key, &xperms);
|
|
if (!node) {
|
|
pr_warn("add_xperm_rule_raw cannot found node!\n");
|
|
return;
|
|
}
|
|
datum = &node->datum;
|
|
|
|
if (datum->u.xperms == NULL) {
|
|
datum->u.xperms =
|
|
(struct avtab_extended_perms *)(kmalloc(
|
|
sizeof(xperms), GFP_KERNEL));
|
|
if (!datum->u.xperms) {
|
|
pr_err("alloc xperms failed\n");
|
|
return;
|
|
}
|
|
memcpy(datum->u.xperms, &xperms, sizeof(xperms));
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
|
|
const char *c, const char *range, int effect,
|
|
bool invert)
|
|
{
|
|
struct type_datum *src = NULL, *tgt = NULL;
|
|
struct class_datum *cls = NULL;
|
|
|
|
if (s) {
|
|
src = symtab_search(&db->p_types, s);
|
|
if (src == NULL) {
|
|
pr_info("source type %s does not exist\n", s);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (t) {
|
|
tgt = symtab_search(&db->p_types, t);
|
|
if (tgt == NULL) {
|
|
pr_info("target type %s does not exist\n", t);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (c) {
|
|
cls = symtab_search(&db->p_classes, c);
|
|
if (cls == NULL) {
|
|
pr_info("class %s does not exist\n", c);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
u16 low, high;
|
|
|
|
if (range) {
|
|
if (strchr(range, '-')) {
|
|
sscanf(range, "%hx-%hx", &low, &high);
|
|
} else {
|
|
sscanf(range, "%hx", &low);
|
|
high = low;
|
|
}
|
|
} else {
|
|
low = 0;
|
|
high = 0xFFFF;
|
|
}
|
|
|
|
add_xperm_rule_raw(db, src, tgt, cls, low, high, effect, invert);
|
|
return true;
|
|
}
|
|
|
|
static bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
|
const char *c, const char *d, int effect)
|
|
{
|
|
struct type_datum *src, *tgt, *def;
|
|
struct class_datum *cls;
|
|
|
|
src = symtab_search(&db->p_types, s);
|
|
if (src == NULL) {
|
|
pr_info("source type %s does not exist\n", s);
|
|
return false;
|
|
}
|
|
tgt = symtab_search(&db->p_types, t);
|
|
if (tgt == NULL) {
|
|
pr_info("target type %s does not exist\n", t);
|
|
return false;
|
|
}
|
|
cls = symtab_search(&db->p_classes, c);
|
|
if (cls == NULL) {
|
|
pr_info("class %s does not exist\n", c);
|
|
return false;
|
|
}
|
|
def = symtab_search(&db->p_types, d);
|
|
if (def == NULL) {
|
|
pr_info("default type %s does not exist\n", d);
|
|
return false;
|
|
}
|
|
|
|
struct avtab_key key;
|
|
key.source_type = src->value;
|
|
key.target_type = tgt->value;
|
|
key.target_class = cls->value;
|
|
key.specified = effect;
|
|
|
|
struct avtab_node *node = get_avtab_node(db, &key, NULL);
|
|
node->datum.u.data = def->value;
|
|
|
|
return true;
|
|
}
|
|
|
|
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void
|
|
// *datum, struct hashtab_key_params key_params) 5.8.0: int
|
|
// hashtab_insert(struct hashtab *h, void *k, void *d);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
|
static u32 filenametr_hash(const void *k)
|
|
{
|
|
const struct filename_trans_key *ft = k;
|
|
unsigned long hash;
|
|
unsigned int byte_num;
|
|
unsigned char focus;
|
|
|
|
hash = ft->ttype ^ ft->tclass;
|
|
|
|
byte_num = 0;
|
|
while ((focus = ft->name[byte_num++]))
|
|
hash = partial_name_hash(focus, hash);
|
|
return hash;
|
|
}
|
|
|
|
static int filenametr_cmp(const void *k1, const void *k2)
|
|
{
|
|
const struct filename_trans_key *ft1 = k1;
|
|
const struct filename_trans_key *ft2 = k2;
|
|
int v;
|
|
|
|
v = ft1->ttype - ft2->ttype;
|
|
if (v)
|
|
return v;
|
|
|
|
v = ft1->tclass - ft2->tclass;
|
|
if (v)
|
|
return v;
|
|
|
|
return strcmp(ft1->name, ft2->name);
|
|
}
|
|
|
|
static const struct hashtab_key_params filenametr_key_params = {
|
|
.hash = filenametr_hash,
|
|
.cmp = filenametr_cmp,
|
|
};
|
|
#endif
|
|
|
|
static bool add_filename_trans(struct policydb *db, const char *s,
|
|
const char *t, const char *c, const char *d,
|
|
const char *o)
|
|
{
|
|
struct type_datum *src, *tgt, *def;
|
|
struct class_datum *cls;
|
|
|
|
src = symtab_search(&db->p_types, s);
|
|
if (src == NULL) {
|
|
pr_warn("source type %s does not exist\n", s);
|
|
return false;
|
|
}
|
|
tgt = symtab_search(&db->p_types, t);
|
|
if (tgt == NULL) {
|
|
pr_warn("target type %s does not exist\n", t);
|
|
return false;
|
|
}
|
|
cls = symtab_search(&db->p_classes, c);
|
|
if (cls == NULL) {
|
|
pr_warn("class %s does not exist\n", c);
|
|
return false;
|
|
}
|
|
def = symtab_search(&db->p_types, d);
|
|
if (def == NULL) {
|
|
pr_warn("default type %s does not exist\n", d);
|
|
return false;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
|
|
struct filename_trans_key key;
|
|
key.ttype = tgt->value;
|
|
key.tclass = cls->value;
|
|
key.name = (char *)o;
|
|
|
|
struct filename_trans_datum *last = NULL;
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
|
struct filename_trans_datum *trans =
|
|
policydb_filenametr_search(db, &key);
|
|
#else
|
|
struct filename_trans_datum *trans =
|
|
hashtab_search(&db->filename_trans, &key);
|
|
#endif
|
|
while (trans) {
|
|
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
|
|
// Duplicate, overwrite existing data and return
|
|
trans->otype = def->value;
|
|
return true;
|
|
}
|
|
if (trans->otype == def->value)
|
|
break;
|
|
last = trans;
|
|
trans = trans->next;
|
|
}
|
|
|
|
if (trans == NULL) {
|
|
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
|
1, GFP_ATOMIC);
|
|
struct filename_trans_key *new_key =
|
|
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
|
|
GFP_ATOMIC);
|
|
*new_key = key;
|
|
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
|
trans->next = last;
|
|
trans->otype = def->value;
|
|
hashtab_insert(&db->filename_trans, new_key, trans,
|
|
filenametr_key_params);
|
|
}
|
|
|
|
db->compat_filename_trans_count++;
|
|
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
|
|
#else // < 5.7.0, has no filename_trans_key, but struct filename_trans
|
|
|
|
struct filename_trans key;
|
|
key.ttype = tgt->value;
|
|
key.tclass = cls->value;
|
|
key.name = (char *)o;
|
|
|
|
struct filename_trans_datum *trans =
|
|
hashtab_search(db->filename_trans, &key);
|
|
|
|
if (trans == NULL) {
|
|
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
|
1, GFP_ATOMIC);
|
|
if (!trans) {
|
|
pr_err("add_filename_trans: Failed to alloc datum\n");
|
|
return false;
|
|
}
|
|
struct filename_trans *new_key =
|
|
(struct filename_trans *)kmalloc(sizeof(*new_key),
|
|
GFP_ATOMIC);
|
|
if (!new_key) {
|
|
pr_err("add_filename_trans: Failed to alloc new_key\n");
|
|
return false;
|
|
}
|
|
*new_key = key;
|
|
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
|
trans->otype = def->value;
|
|
hashtab_insert(db->filename_trans, new_key, trans);
|
|
}
|
|
|
|
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) ==
|
|
0;
|
|
#endif
|
|
}
|
|
|
|
static bool add_genfscon(struct policydb *db, const char *fs_name,
|
|
const char *path, const char *context)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void *ksu_realloc(void *old, size_t new_size, size_t old_size)
|
|
{
|
|
// we can't use krealloc, because it may be read-only
|
|
void *new = kzalloc(new_size, GFP_ATOMIC);
|
|
if (!new) {
|
|
return NULL;
|
|
}
|
|
if (old_size) {
|
|
memcpy(new, old, old_size);
|
|
}
|
|
// we can't use kfree, because it may be read-only
|
|
// there maybe some leaks, maybe we can check ptr_write, but it's not a big deal
|
|
// kfree(old);
|
|
return new;
|
|
}
|
|
|
|
static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
|
{
|
|
#ifdef KSU_SUPPORT_ADD_TYPE
|
|
struct type_datum *type = symtab_search(&db->p_types, type_name);
|
|
if (type) {
|
|
pr_warn("Type %s already exists\n", type_name);
|
|
return true;
|
|
}
|
|
|
|
u32 value = ++db->p_types.nprim;
|
|
type = (struct type_datum *)kzalloc(sizeof(struct type_datum),
|
|
GFP_ATOMIC);
|
|
if (!type) {
|
|
pr_err("add_type: alloc type_datum failed.\n");
|
|
return false;
|
|
}
|
|
|
|
type->primary = 1;
|
|
type->value = value;
|
|
type->attribute = attr;
|
|
|
|
char *key = kstrdup(type_name, GFP_ATOMIC);
|
|
if (!key) {
|
|
pr_err("add_type: alloc key failed.\n");
|
|
return false;
|
|
}
|
|
|
|
if (symtab_insert(&db->p_types, key, type)) {
|
|
pr_err("add_type: insert symtab failed.\n");
|
|
return false;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
|
struct ebitmap *new_type_attr_map_array =
|
|
ksu_realloc(db->type_attr_map_array,
|
|
value * sizeof(struct ebitmap),
|
|
(value - 1) * sizeof(struct ebitmap));
|
|
|
|
if (!new_type_attr_map_array) {
|
|
pr_err("add_type: alloc type_attr_map_array failed\n");
|
|
return false;
|
|
}
|
|
|
|
struct type_datum **new_type_val_to_struct =
|
|
ksu_realloc(db->type_val_to_struct,
|
|
sizeof(*db->type_val_to_struct) * value,
|
|
sizeof(*db->type_val_to_struct) * (value - 1));
|
|
|
|
if (!new_type_val_to_struct) {
|
|
pr_err("add_type: alloc type_val_to_struct failed\n");
|
|
return false;
|
|
}
|
|
|
|
char **new_val_to_name_types =
|
|
ksu_realloc(db->sym_val_to_name[SYM_TYPES],
|
|
sizeof(char *) * value,
|
|
sizeof(char *) * (value - 1));
|
|
if (!new_val_to_name_types) {
|
|
pr_err("add_type: alloc val_to_name failed\n");
|
|
return false;
|
|
}
|
|
|
|
db->type_attr_map_array = new_type_attr_map_array;
|
|
ebitmap_init(&db->type_attr_map_array[value - 1]);
|
|
ebitmap_set_bit(&db->type_attr_map_array[value - 1], value - 1, 1);
|
|
|
|
db->type_val_to_struct = new_type_val_to_struct;
|
|
db->type_val_to_struct[value - 1] = type;
|
|
|
|
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
|
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
|
|
|
|
int i;
|
|
for (i = 0; i < db->p_roles.nprim; ++i) {
|
|
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
|
1);
|
|
}
|
|
|
|
return true;
|
|
#elif defined(CONFIG_IS_HW_HISI)
|
|
/*
|
|
* Huawei use type_attr_map and type_val_to_struct.
|
|
* And use ebitmap not flex_array.
|
|
*/
|
|
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
|
|
struct ebitmap *new_type_attr_map =
|
|
(krealloc(db->type_attr_map, new_size, GFP_ATOMIC));
|
|
|
|
struct type_datum **new_type_val_to_struct =
|
|
krealloc(db->type_val_to_struct,
|
|
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
|
|
GFP_ATOMIC);
|
|
|
|
if (!new_type_attr_map) {
|
|
pr_err("add_type: alloc type_attr_map failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!new_type_val_to_struct) {
|
|
pr_err("add_type: alloc type_val_to_struct failed\n");
|
|
return false;
|
|
}
|
|
|
|
char **new_val_to_name_types =
|
|
krealloc(db->sym_val_to_name[SYM_TYPES],
|
|
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
|
|
GFP_KERNEL);
|
|
if (!new_val_to_name_types) {
|
|
pr_err("add_type: alloc val_to_name failed\n");
|
|
return false;
|
|
}
|
|
|
|
db->type_attr_map = new_type_attr_map;
|
|
ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO);
|
|
ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1);
|
|
|
|
db->type_val_to_struct = new_type_val_to_struct;
|
|
db->type_val_to_struct[value - 1] = type;
|
|
|
|
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
|
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
|
|
|
|
int i;
|
|
for (i = 0; i < db->p_roles.nprim; ++i) {
|
|
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
|
1);
|
|
}
|
|
|
|
return true;
|
|
#else
|
|
// flex_array is not extensible, we need to create a new bigger one instead
|
|
struct flex_array *new_type_attr_map_array =
|
|
flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
|
|
struct flex_array *new_type_val_to_struct =
|
|
flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
|
|
struct flex_array *new_val_to_name_types =
|
|
flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
|
|
if (!new_type_attr_map_array) {
|
|
pr_err("add_type: alloc type_attr_map_array failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!new_type_val_to_struct) {
|
|
pr_err("add_type: alloc type_val_to_struct failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!new_val_to_name_types) {
|
|
pr_err("add_type: alloc val_to_name failed\n");
|
|
return false;
|
|
}
|
|
|
|
// preallocate so we don't have to worry about the put ever failing
|
|
if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim,
|
|
GFP_ATOMIC | __GFP_ZERO)) {
|
|
pr_err("add_type: prealloc type_attr_map_array failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim,
|
|
GFP_ATOMIC | __GFP_ZERO)) {
|
|
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (flex_array_prealloc(new_val_to_name_types, 0,
|
|
db->symtab[SYM_TYPES].nprim,
|
|
GFP_ATOMIC | __GFP_ZERO)) {
|
|
pr_err("add_type: prealloc val_to_name_types failed\n");
|
|
return false;
|
|
}
|
|
|
|
int j;
|
|
void *old_elem;
|
|
// copy the old data or pointers to new flex arrays
|
|
for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) {
|
|
old_elem = flex_array_get(db->type_attr_map_array, j);
|
|
if (old_elem)
|
|
flex_array_put(new_type_attr_map_array, j, old_elem,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
}
|
|
|
|
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
|
|
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
|
|
if (old_elem)
|
|
flex_array_put_ptr(new_type_val_to_struct, j, old_elem,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
}
|
|
|
|
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
|
|
old_elem =
|
|
flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
|
|
if (old_elem)
|
|
flex_array_put_ptr(new_val_to_name_types, j, old_elem,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
}
|
|
|
|
// store the pointer of old flex arrays first, when assigning new ones we
|
|
// should free it
|
|
struct flex_array *old_fa;
|
|
|
|
old_fa = db->type_attr_map_array;
|
|
db->type_attr_map_array = new_type_attr_map_array;
|
|
if (old_fa) {
|
|
flex_array_free(old_fa);
|
|
}
|
|
|
|
ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1));
|
|
ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1),
|
|
value - 1, 1);
|
|
|
|
old_fa = db->type_val_to_struct_array;
|
|
db->type_val_to_struct_array = new_type_val_to_struct;
|
|
if (old_fa) {
|
|
flex_array_free(old_fa);
|
|
}
|
|
flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
|
|
old_fa = db->sym_val_to_name[SYM_TYPES];
|
|
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
|
if (old_fa) {
|
|
flex_array_free(old_fa);
|
|
}
|
|
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key,
|
|
GFP_ATOMIC | __GFP_ZERO);
|
|
|
|
int i;
|
|
for (i = 0; i < db->p_roles.nprim; ++i) {
|
|
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
|
1);
|
|
}
|
|
return true;
|
|
#endif
|
|
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static bool set_type_state(struct policydb *db, const char *type_name,
|
|
bool permissive)
|
|
{
|
|
struct type_datum *type;
|
|
if (type_name == NULL) {
|
|
struct hashtab_node *node;
|
|
ksu_hashtab_for_each(db->p_types.table, node)
|
|
{
|
|
type = (struct type_datum *)(node->datum);
|
|
if (ebitmap_set_bit(&db->permissive_map, type->value,
|
|
permissive))
|
|
pr_info("Could not set bit in permissive map\n");
|
|
};
|
|
} else {
|
|
type = (struct type_datum *)symtab_search(&db->p_types,
|
|
type_name);
|
|
if (type == NULL) {
|
|
pr_info("type %s does not exist\n", type_name);
|
|
return false;
|
|
}
|
|
if (ebitmap_set_bit(&db->permissive_map, type->value,
|
|
permissive)) {
|
|
pr_info("Could not set bit in permissive map\n");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
|
struct type_datum *attr)
|
|
{
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
|
struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
|
|
#elif defined(CONFIG_IS_HW_HISI)
|
|
/*
|
|
* HISI_SELINUX_EBITMAP_RO is Huawei's unique features.
|
|
*/
|
|
struct ebitmap *sattr = &db->type_attr_map[type->value - 1],
|
|
HISI_SELINUX_EBITMAP_RO;
|
|
#else
|
|
struct ebitmap *sattr =
|
|
flex_array_get(db->type_attr_map_array, type->value - 1);
|
|
#endif
|
|
ebitmap_set_bit(sattr, attr->value - 1, 1);
|
|
|
|
struct hashtab_node *node;
|
|
struct constraint_node *n;
|
|
struct constraint_expr *e;
|
|
ksu_hashtab_for_each(db->p_classes.table, node)
|
|
{
|
|
struct class_datum *cls = (struct class_datum *)(node->datum);
|
|
for (n = cls->constraints; n; n = n->next) {
|
|
for (e = n->expr; e; e = e->next) {
|
|
if (e->expr_type == CEXPR_NAMES &&
|
|
ebitmap_get_bit(&e->type_names->types,
|
|
attr->value - 1)) {
|
|
ebitmap_set_bit(&e->names,
|
|
type->value - 1, 1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
static bool add_typeattribute(struct policydb *db, const char *type,
|
|
const char *attr)
|
|
{
|
|
struct type_datum *type_d = symtab_search(&db->p_types, type);
|
|
if (type_d == NULL) {
|
|
pr_info("type %s does not exist\n", type);
|
|
return false;
|
|
} else if (type_d->attribute) {
|
|
pr_info("type %s is an attribute\n", attr);
|
|
return false;
|
|
}
|
|
|
|
struct type_datum *attr_d = symtab_search(&db->p_types, attr);
|
|
if (attr_d == NULL) {
|
|
pr_info("attribute %s does not exist\n", type);
|
|
return false;
|
|
} else if (!attr_d->attribute) {
|
|
pr_info("type %s is not an attribute \n", attr);
|
|
return false;
|
|
}
|
|
|
|
add_typeattribute_raw(db, type_d, attr_d);
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Operation on types
|
|
bool ksu_type(struct policydb *db, const char *name, const char *attr)
|
|
{
|
|
return add_type(db, name, false) && add_typeattribute(db, name, attr);
|
|
}
|
|
|
|
bool ksu_attribute(struct policydb *db, const char *name)
|
|
{
|
|
return add_type(db, name, true);
|
|
}
|
|
|
|
bool ksu_permissive(struct policydb *db, const char *type)
|
|
{
|
|
return set_type_state(db, type, true);
|
|
}
|
|
|
|
bool ksu_enforce(struct policydb *db, const char *type)
|
|
{
|
|
return set_type_state(db, type, false);
|
|
}
|
|
|
|
bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr)
|
|
{
|
|
return add_typeattribute(db, type, attr);
|
|
}
|
|
|
|
bool ksu_exists(struct policydb *db, const char *type)
|
|
{
|
|
return symtab_search(&db->p_types, type) != NULL;
|
|
}
|
|
|
|
// Access vector rules
|
|
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *perm)
|
|
{
|
|
return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, false);
|
|
}
|
|
|
|
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *perm)
|
|
{
|
|
return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, true);
|
|
}
|
|
|
|
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *perm)
|
|
{
|
|
return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITALLOW, false);
|
|
}
|
|
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *perm)
|
|
{
|
|
return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITDENY, true);
|
|
}
|
|
|
|
// Extended permissions access vector rules
|
|
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *range)
|
|
{
|
|
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED,
|
|
false);
|
|
}
|
|
|
|
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *range)
|
|
{
|
|
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW,
|
|
false);
|
|
}
|
|
|
|
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *range)
|
|
{
|
|
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT,
|
|
false);
|
|
}
|
|
|
|
// Type rules
|
|
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *def, const char *obj)
|
|
{
|
|
if (obj) {
|
|
return add_filename_trans(db, src, tgt, cls, def, obj);
|
|
} else {
|
|
return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION);
|
|
}
|
|
}
|
|
|
|
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *def)
|
|
{
|
|
return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE);
|
|
}
|
|
|
|
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
|
const char *cls, const char *def)
|
|
{
|
|
return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER);
|
|
}
|
|
|
|
// File system labeling
|
|
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
|
const char *ctx)
|
|
{
|
|
return add_genfscon(db, fs_name, path, ctx);
|
|
}
|