forked from luck/tmp_suning_uos_patched
selinux: refactor changing booleans
Refactor the logic for changing SELinux policy booleans in a similar manner to the refactoring of policy load, thereby reducing the size of the critical section when the policy write-lock is held and making it easier to convert the policy rwlock to RCU in the future. Instead of directly modifying the policydb in place, modify a copy and then swap it into place through a single pointer update. Only fully copy the portions of the policydb that are affected by boolean changes to avoid the full cost of a deep policydb copy. Introduce another level of indirection for the sidtab since changing booleans does not require updating the sidtab, unlike policy load. While we are here, create a common helper for notifying other kernel components and userspace of a policy change and call it from both security_set_bools() and selinux_policy_commit(). Based on an old (2004) patch by Kaigai Kohei [1] to convert the policy rwlock to RCU that was deferred at the time since it did not significantly improve performance and introduced complexity. Peter Enderborg later submitted a patch series to convert to RCU [2] that would have made changing booleans a much more expensive operation by requiring a full policydb_write();policydb_read(); sequence to deep copy the entire policydb and also had concerns regarding atomic allocations. This change is now simplified by the earlier work to encapsulate policy state in the selinux_policy struct and to refactor policy load. After this change, the last major obstacle to converting the policy rwlock to RCU is likely the sidtab live convert support. [1] https://lore.kernel.org/selinux/6e2f9128-e191-ebb3-0e87-74bfccb0767f@tycho.nsa.gov/ [2] https://lore.kernel.org/selinux/20180530141104.28569-1-peter.enderborg@sony.com/ Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
parent
02a52c5c8c
commit
c7c556f1e8
|
@ -301,7 +301,6 @@ void avtab_destroy(struct avtab *h)
|
||||||
|
|
||||||
void avtab_init(struct avtab *h)
|
void avtab_init(struct avtab *h)
|
||||||
{
|
{
|
||||||
kvfree(h->htable);
|
|
||||||
h->htable = NULL;
|
h->htable = NULL;
|
||||||
h->nel = 0;
|
h->nel = 0;
|
||||||
}
|
}
|
||||||
|
@ -340,6 +339,54 @@ int avtab_alloc(struct avtab *h, u32 nrules)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int avtab_duplicate(struct avtab *new, struct avtab *orig)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct avtab_node *node, *tmp, *tail;
|
||||||
|
|
||||||
|
memset(new, 0, sizeof(*new));
|
||||||
|
|
||||||
|
new->htable = kvcalloc(orig->nslot, sizeof(void *), GFP_KERNEL);
|
||||||
|
if (!new->htable)
|
||||||
|
return -ENOMEM;
|
||||||
|
new->nslot = orig->nslot;
|
||||||
|
new->mask = orig->mask;
|
||||||
|
|
||||||
|
for (i = 0; i < orig->nslot; i++) {
|
||||||
|
tail = NULL;
|
||||||
|
for (node = orig->htable[i]; node; node = node->next) {
|
||||||
|
tmp = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
|
||||||
|
if (!tmp)
|
||||||
|
goto error;
|
||||||
|
tmp->key = node->key;
|
||||||
|
if (tmp->key.specified & AVTAB_XPERMS) {
|
||||||
|
tmp->datum.u.xperms =
|
||||||
|
kmem_cache_zalloc(avtab_xperms_cachep,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!tmp->datum.u.xperms) {
|
||||||
|
kmem_cache_free(avtab_node_cachep, tmp);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
tmp->datum.u.xperms = node->datum.u.xperms;
|
||||||
|
} else
|
||||||
|
tmp->datum.u.data = node->datum.u.data;
|
||||||
|
|
||||||
|
if (tail)
|
||||||
|
tail->next = tmp;
|
||||||
|
else
|
||||||
|
new->htable[i] = tmp;
|
||||||
|
|
||||||
|
tail = tmp;
|
||||||
|
new->nel++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
avtab_destroy(new);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
void avtab_hash_eval(struct avtab *h, char *tag)
|
void avtab_hash_eval(struct avtab *h, char *tag)
|
||||||
{
|
{
|
||||||
int i, chain_len, slots_used, max_chain_len;
|
int i, chain_len, slots_used, max_chain_len;
|
||||||
|
|
|
@ -89,6 +89,7 @@ struct avtab {
|
||||||
|
|
||||||
void avtab_init(struct avtab *h);
|
void avtab_init(struct avtab *h);
|
||||||
int avtab_alloc(struct avtab *, u32);
|
int avtab_alloc(struct avtab *, u32);
|
||||||
|
int avtab_duplicate(struct avtab *new, struct avtab *orig);
|
||||||
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
|
struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
|
||||||
void avtab_destroy(struct avtab *h);
|
void avtab_destroy(struct avtab *h);
|
||||||
void avtab_hash_eval(struct avtab *h, char *tag);
|
void avtab_hash_eval(struct avtab *h, char *tag);
|
||||||
|
|
|
@ -600,3 +600,159 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||||
services_compute_xperms_drivers(xperms, node);
|
services_compute_xperms_drivers(xperms, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cond_dup_av_list(struct cond_av_list *new,
|
||||||
|
struct cond_av_list *orig,
|
||||||
|
struct avtab *avtab)
|
||||||
|
{
|
||||||
|
struct avtab_node *avnode;
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
memset(new, 0, sizeof(*new));
|
||||||
|
|
||||||
|
new->nodes = kcalloc(orig->len, sizeof(*new->nodes), GFP_KERNEL);
|
||||||
|
if (!new->nodes)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < orig->len; i++) {
|
||||||
|
avnode = avtab_search_node(avtab, &orig->nodes[i]->key);
|
||||||
|
if (WARN_ON(!avnode))
|
||||||
|
return -EINVAL;
|
||||||
|
new->nodes[i] = avnode;
|
||||||
|
new->len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int duplicate_policydb_cond_list(struct policydb *newp,
|
||||||
|
struct policydb *origp)
|
||||||
|
{
|
||||||
|
int rc, i, j;
|
||||||
|
|
||||||
|
rc = avtab_duplicate(&newp->te_cond_avtab, &origp->te_cond_avtab);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
newp->cond_list_len = 0;
|
||||||
|
newp->cond_list = kcalloc(origp->cond_list_len,
|
||||||
|
sizeof(*newp->cond_list),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!newp->cond_list)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
for (i = 0; i < origp->cond_list_len; i++) {
|
||||||
|
struct cond_node *newn = &newp->cond_list[i];
|
||||||
|
struct cond_node *orign = &origp->cond_list[i];
|
||||||
|
|
||||||
|
newp->cond_list_len++;
|
||||||
|
|
||||||
|
newn->cur_state = orign->cur_state;
|
||||||
|
newn->expr.nodes = kcalloc(orign->expr.len,
|
||||||
|
sizeof(*newn->expr.nodes), GFP_KERNEL);
|
||||||
|
if (!newn->expr.nodes)
|
||||||
|
goto error;
|
||||||
|
for (j = 0; j < orign->expr.len; j++)
|
||||||
|
newn->expr.nodes[j] = orign->expr.nodes[j];
|
||||||
|
newn->expr.len = orign->expr.len;
|
||||||
|
|
||||||
|
rc = cond_dup_av_list(&newn->true_list, &orign->true_list,
|
||||||
|
&newp->te_cond_avtab);
|
||||||
|
if (rc)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
rc = cond_dup_av_list(&newn->false_list, &orign->false_list,
|
||||||
|
&newp->te_cond_avtab);
|
||||||
|
if (rc)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
avtab_destroy(&newp->te_cond_avtab);
|
||||||
|
cond_list_destroy(newp);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cond_bools_destroy(void *key, void *datum, void *args)
|
||||||
|
{
|
||||||
|
/* key was not copied so no need to free here */
|
||||||
|
kfree(datum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, void *args)
|
||||||
|
{
|
||||||
|
struct cond_bool_datum *datum;
|
||||||
|
|
||||||
|
datum = kmalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
|
||||||
|
if (!datum)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memcpy(datum, orig->datum, sizeof(struct cond_bool_datum));
|
||||||
|
|
||||||
|
new->key = orig->key; /* No need to copy, never modified */
|
||||||
|
new->datum = datum;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cond_bools_index(void *key, void *datum, void *args)
|
||||||
|
{
|
||||||
|
struct cond_bool_datum *booldatum, **cond_bool_array;
|
||||||
|
|
||||||
|
booldatum = datum;
|
||||||
|
cond_bool_array = args;
|
||||||
|
cond_bool_array[booldatum->value - 1] = booldatum;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int duplicate_policydb_bools(struct policydb *newdb,
|
||||||
|
struct policydb *orig)
|
||||||
|
{
|
||||||
|
struct cond_bool_datum **cond_bool_array;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
cond_bool_array = kmalloc_array(orig->p_bools.nprim,
|
||||||
|
sizeof(*orig->bool_val_to_struct),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!cond_bool_array)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rc = hashtab_duplicate(&newdb->p_bools.table, &orig->p_bools.table,
|
||||||
|
cond_bools_copy, cond_bools_destroy, NULL);
|
||||||
|
if (rc) {
|
||||||
|
kfree(cond_bool_array);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtab_map(&newdb->p_bools.table, cond_bools_index, cond_bool_array);
|
||||||
|
newdb->bool_val_to_struct = cond_bool_array;
|
||||||
|
|
||||||
|
newdb->p_bools.nprim = orig->p_bools.nprim;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cond_policydb_destroy_dup(struct policydb *p)
|
||||||
|
{
|
||||||
|
hashtab_map(&p->p_bools.table, cond_bools_destroy, NULL);
|
||||||
|
hashtab_destroy(&p->p_bools.table);
|
||||||
|
cond_policydb_destroy(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cond_policydb_dup(struct policydb *new, struct policydb *orig)
|
||||||
|
{
|
||||||
|
cond_policydb_init(new);
|
||||||
|
|
||||||
|
if (duplicate_policydb_bools(new, orig))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (duplicate_policydb_cond_list(new, orig)) {
|
||||||
|
cond_policydb_destroy_dup(new);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -79,5 +79,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||||
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
|
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
|
||||||
struct extended_perms_decision *xpermd);
|
struct extended_perms_decision *xpermd);
|
||||||
void evaluate_cond_nodes(struct policydb *p);
|
void evaluate_cond_nodes(struct policydb *p);
|
||||||
|
void cond_policydb_destroy_dup(struct policydb *p);
|
||||||
|
int cond_policydb_dup(struct policydb *new, struct policydb *orig);
|
||||||
|
|
||||||
#endif /* _CONDITIONAL_H_ */
|
#endif /* _CONDITIONAL_H_ */
|
||||||
|
|
|
@ -122,6 +122,59 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
|
||||||
info->max_chain_len = max_chain_len;
|
info->max_chain_len = max_chain_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
|
||||||
|
int (*copy)(struct hashtab_node *new,
|
||||||
|
struct hashtab_node *orig, void *args),
|
||||||
|
int (*destroy)(void *k, void *d, void *args),
|
||||||
|
void *args)
|
||||||
|
{
|
||||||
|
struct hashtab_node *cur, *tmp, *tail;
|
||||||
|
int i, rc;
|
||||||
|
|
||||||
|
memset(new, 0, sizeof(*new));
|
||||||
|
|
||||||
|
new->htable = kcalloc(orig->size, sizeof(*new->htable), GFP_KERNEL);
|
||||||
|
if (!new->htable)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
new->size = orig->size;
|
||||||
|
|
||||||
|
for (i = 0; i < orig->size; i++) {
|
||||||
|
tail = NULL;
|
||||||
|
for (cur = orig->htable[i]; cur; cur = cur->next) {
|
||||||
|
tmp = kmem_cache_zalloc(hashtab_node_cachep,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!tmp)
|
||||||
|
goto error;
|
||||||
|
rc = copy(tmp, cur, args);
|
||||||
|
if (rc) {
|
||||||
|
kmem_cache_free(hashtab_node_cachep, tmp);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
tmp->next = NULL;
|
||||||
|
if (!tail)
|
||||||
|
new->htable[i] = tmp;
|
||||||
|
else
|
||||||
|
tail->next = tmp;
|
||||||
|
tail = tmp;
|
||||||
|
new->nel++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
for (i = 0; i < new->size; i++) {
|
||||||
|
for (cur = new->htable[i]; cur; cur = tmp) {
|
||||||
|
tmp = cur->next;
|
||||||
|
destroy(cur->key, cur->datum, args);
|
||||||
|
kmem_cache_free(hashtab_node_cachep, cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kmem_cache_free(hashtab_node_cachep, new);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
void __init hashtab_cache_init(void)
|
void __init hashtab_cache_init(void)
|
||||||
{
|
{
|
||||||
hashtab_node_cachep = kmem_cache_create("hashtab_node",
|
hashtab_node_cachep = kmem_cache_create("hashtab_node",
|
||||||
|
|
|
@ -136,6 +136,12 @@ int hashtab_map(struct hashtab *h,
|
||||||
int (*apply)(void *k, void *d, void *args),
|
int (*apply)(void *k, void *d, void *args),
|
||||||
void *args);
|
void *args);
|
||||||
|
|
||||||
|
int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
|
||||||
|
int (*copy)(struct hashtab_node *new,
|
||||||
|
struct hashtab_node *orig, void *args),
|
||||||
|
int (*destroy)(void *k, void *d, void *args),
|
||||||
|
void *args);
|
||||||
|
|
||||||
/* Fill info with some hash table statistics */
|
/* Fill info with some hash table statistics */
|
||||||
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
|
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
|
||||||
|
|
||||||
|
|
|
@ -723,7 +723,7 @@ static int security_validtrans_handle_fail(struct selinux_state *state,
|
||||||
u16 tclass)
|
u16 tclass)
|
||||||
{
|
{
|
||||||
struct policydb *p = &state->ss->policy->policydb;
|
struct policydb *p = &state->ss->policy->policydb;
|
||||||
struct sidtab *sidtab = &state->ss->policy->sidtab;
|
struct sidtab *sidtab = state->ss->policy->sidtab;
|
||||||
char *o = NULL, *n = NULL, *t = NULL;
|
char *o = NULL, *n = NULL, *t = NULL;
|
||||||
u32 olen, nlen, tlen;
|
u32 olen, nlen, tlen;
|
||||||
|
|
||||||
|
@ -768,7 +768,7 @@ static int security_compute_validatetrans(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
tclass = unmap_class(&state->ss->policy->map, orig_tclass);
|
tclass = unmap_class(&state->ss->policy->map, orig_tclass);
|
||||||
|
@ -869,7 +869,7 @@ int security_bounded_transition(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
old_entry = sidtab_search_entry(sidtab, old_sid);
|
old_entry = sidtab_search_entry(sidtab, old_sid);
|
||||||
|
@ -1026,7 +1026,7 @@ void security_compute_xperms_decision(struct selinux_state *state,
|
||||||
goto allow;
|
goto allow;
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
scontext = sidtab_search(sidtab, ssid);
|
scontext = sidtab_search(sidtab, ssid);
|
||||||
if (!scontext) {
|
if (!scontext) {
|
||||||
|
@ -1111,7 +1111,7 @@ void security_compute_av(struct selinux_state *state,
|
||||||
goto allow;
|
goto allow;
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
scontext = sidtab_search(sidtab, ssid);
|
scontext = sidtab_search(sidtab, ssid);
|
||||||
if (!scontext) {
|
if (!scontext) {
|
||||||
|
@ -1165,7 +1165,7 @@ void security_compute_av_user(struct selinux_state *state,
|
||||||
goto allow;
|
goto allow;
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
scontext = sidtab_search(sidtab, ssid);
|
scontext = sidtab_search(sidtab, ssid);
|
||||||
if (!scontext) {
|
if (!scontext) {
|
||||||
|
@ -1288,7 +1288,7 @@ int security_sidtab_hash_stats(struct selinux_state *state, char *page)
|
||||||
}
|
}
|
||||||
|
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
rc = sidtab_hash_stats(&state->ss->policy->sidtab, page);
|
rc = sidtab_hash_stats(state->ss->policy->sidtab, page);
|
||||||
read_unlock(&state->ss->policy_rwlock);
|
read_unlock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1337,7 +1337,7 @@ static int security_sid_to_context_core(struct selinux_state *state,
|
||||||
}
|
}
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
if (force)
|
if (force)
|
||||||
entry = sidtab_search_entry_force(sidtab, sid);
|
entry = sidtab_search_entry_force(sidtab, sid);
|
||||||
|
@ -1531,7 +1531,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
|
||||||
}
|
}
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
rc = string_to_context_struct(policydb, sidtab, scontext2,
|
rc = string_to_context_struct(policydb, sidtab, scontext2,
|
||||||
&context, def_sid);
|
&context, def_sid);
|
||||||
if (rc == -EINVAL && force) {
|
if (rc == -EINVAL && force) {
|
||||||
|
@ -1619,7 +1619,7 @@ static int compute_sid_handle_invalid_context(
|
||||||
struct context *newcontext)
|
struct context *newcontext)
|
||||||
{
|
{
|
||||||
struct policydb *policydb = &state->ss->policy->policydb;
|
struct policydb *policydb = &state->ss->policy->policydb;
|
||||||
struct sidtab *sidtab = &state->ss->policy->sidtab;
|
struct sidtab *sidtab = state->ss->policy->sidtab;
|
||||||
char *s = NULL, *t = NULL, *n = NULL;
|
char *s = NULL, *t = NULL, *n = NULL;
|
||||||
u32 slen, tlen, nlen;
|
u32 slen, tlen, nlen;
|
||||||
struct audit_buffer *ab;
|
struct audit_buffer *ab;
|
||||||
|
@ -1724,7 +1724,7 @@ static int security_compute_sid(struct selinux_state *state,
|
||||||
}
|
}
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
sentry = sidtab_search_entry(sidtab, ssid);
|
sentry = sidtab_search_entry(sidtab, ssid);
|
||||||
if (!sentry) {
|
if (!sentry) {
|
||||||
|
@ -2128,7 +2128,8 @@ static void selinux_policy_free(struct selinux_policy *policy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
policydb_destroy(&policy->policydb);
|
policydb_destroy(&policy->policydb);
|
||||||
sidtab_destroy(&policy->sidtab);
|
sidtab_destroy(policy->sidtab);
|
||||||
|
kfree(policy->sidtab);
|
||||||
kfree(policy->map.mapping);
|
kfree(policy->map.mapping);
|
||||||
kfree(policy);
|
kfree(policy);
|
||||||
}
|
}
|
||||||
|
@ -2136,11 +2137,21 @@ static void selinux_policy_free(struct selinux_policy *policy)
|
||||||
void selinux_policy_cancel(struct selinux_state *state,
|
void selinux_policy_cancel(struct selinux_state *state,
|
||||||
struct selinux_policy *policy)
|
struct selinux_policy *policy)
|
||||||
{
|
{
|
||||||
|
sidtab_cancel_convert(state->ss->policy->sidtab);
|
||||||
sidtab_cancel_convert(&state->ss->policy->sidtab);
|
|
||||||
selinux_policy_free(policy);
|
selinux_policy_free(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void selinux_notify_policy_change(struct selinux_state *state,
|
||||||
|
u32 seqno)
|
||||||
|
{
|
||||||
|
/* Flush external caches and notify userspace of policy load */
|
||||||
|
avc_ss_reset(state->avc, seqno);
|
||||||
|
selnl_notify_policyload(seqno);
|
||||||
|
selinux_status_update_policyload(state, seqno);
|
||||||
|
selinux_netlbl_cache_invalidate();
|
||||||
|
selinux_xfrm_notify_policyload();
|
||||||
|
}
|
||||||
|
|
||||||
void selinux_policy_commit(struct selinux_state *state,
|
void selinux_policy_commit(struct selinux_state *state,
|
||||||
struct selinux_policy *newpolicy)
|
struct selinux_policy *newpolicy)
|
||||||
{
|
{
|
||||||
|
@ -2185,12 +2196,8 @@ void selinux_policy_commit(struct selinux_state *state,
|
||||||
/* Free the old policy */
|
/* Free the old policy */
|
||||||
selinux_policy_free(oldpolicy);
|
selinux_policy_free(oldpolicy);
|
||||||
|
|
||||||
/* Flush external caches and notify userspace of policy load */
|
/* Notify others of the policy change */
|
||||||
avc_ss_reset(state->avc, seqno);
|
selinux_notify_policy_change(state, seqno);
|
||||||
selnl_notify_policyload(seqno);
|
|
||||||
selinux_status_update_policyload(state, seqno);
|
|
||||||
selinux_netlbl_cache_invalidate();
|
|
||||||
selinux_xfrm_notify_policyload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2216,6 +2223,10 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
|
||||||
if (!newpolicy)
|
if (!newpolicy)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
newpolicy->sidtab = kzalloc(sizeof(*newpolicy->sidtab), GFP_KERNEL);
|
||||||
|
if (!newpolicy)
|
||||||
|
goto err;
|
||||||
|
|
||||||
rc = policydb_read(&newpolicy->policydb, fp);
|
rc = policydb_read(&newpolicy->policydb, fp);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -2226,7 +2237,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
|
||||||
if (rc)
|
if (rc)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
rc = policydb_load_isids(&newpolicy->policydb, &newpolicy->sidtab);
|
rc = policydb_load_isids(&newpolicy->policydb, newpolicy->sidtab);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("SELinux: unable to load the initial SIDs\n");
|
pr_err("SELinux: unable to load the initial SIDs\n");
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -2261,9 +2272,9 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
|
||||||
|
|
||||||
convert_params.func = convert_context;
|
convert_params.func = convert_context;
|
||||||
convert_params.args = &args;
|
convert_params.args = &args;
|
||||||
convert_params.target = &newpolicy->sidtab;
|
convert_params.target = newpolicy->sidtab;
|
||||||
|
|
||||||
rc = sidtab_convert(&state->ss->policy->sidtab, &convert_params);
|
rc = sidtab_convert(state->ss->policy->sidtab, &convert_params);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("SELinux: unable to convert the internal"
|
pr_err("SELinux: unable to convert the internal"
|
||||||
" representation of contexts in the new SID"
|
" representation of contexts in the new SID"
|
||||||
|
@ -2306,7 +2317,7 @@ int security_port_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_PORT];
|
c = policydb->ocontexts[OCON_PORT];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2351,7 +2362,7 @@ int security_ib_pkey_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_IBPKEY];
|
c = policydb->ocontexts[OCON_IBPKEY];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2397,7 +2408,7 @@ int security_ib_endport_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_IBENDPORT];
|
c = policydb->ocontexts[OCON_IBENDPORT];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2442,7 +2453,7 @@ int security_netif_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_NETIF];
|
c = policydb->ocontexts[OCON_NETIF];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2505,7 +2516,7 @@ int security_node_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case AF_INET: {
|
case AF_INET: {
|
||||||
|
@ -2605,7 +2616,7 @@ int security_get_user_sids(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
context_init(&usercon);
|
context_init(&usercon);
|
||||||
|
|
||||||
|
@ -2705,7 +2716,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
|
||||||
u32 *sid)
|
u32 *sid)
|
||||||
{
|
{
|
||||||
struct policydb *policydb = &policy->policydb;
|
struct policydb *policydb = &policy->policydb;
|
||||||
struct sidtab *sidtab = &policy->sidtab;
|
struct sidtab *sidtab = policy->sidtab;
|
||||||
int len;
|
int len;
|
||||||
u16 sclass;
|
u16 sclass;
|
||||||
struct genfs *genfs;
|
struct genfs *genfs;
|
||||||
|
@ -2802,7 +2813,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_FSUSE];
|
c = policydb->ocontexts[OCON_FSUSE];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2891,49 +2902,77 @@ int security_get_bools(struct selinux_policy *policy,
|
||||||
|
|
||||||
int security_set_bools(struct selinux_state *state, u32 len, int *values)
|
int security_set_bools(struct selinux_state *state, u32 len, int *values)
|
||||||
{
|
{
|
||||||
struct policydb *policydb;
|
struct selinux_policy *newpolicy, *oldpolicy;
|
||||||
int rc;
|
int rc;
|
||||||
u32 i, lenp, seqno = 0;
|
u32 i, seqno = 0;
|
||||||
|
|
||||||
write_lock_irq(&state->ss->policy_rwlock);
|
/*
|
||||||
|
* NOTE: We do not need to take the policy read-lock
|
||||||
|
* around the code below because other policy-modifying
|
||||||
|
* operations are already excluded by selinuxfs via
|
||||||
|
* fsi->mutex.
|
||||||
|
*/
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
/* Consistency check on number of booleans, should never fail */
|
||||||
|
if (WARN_ON(len != state->ss->policy->policydb.p_bools.nprim))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
rc = -EFAULT;
|
newpolicy = kmemdup(state->ss->policy, sizeof(*newpolicy),
|
||||||
lenp = policydb->p_bools.nprim;
|
GFP_KERNEL);
|
||||||
if (len != lenp)
|
if (!newpolicy)
|
||||||
goto out;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
oldpolicy = state->ss->policy;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deep copy only the parts of the policydb that might be
|
||||||
|
* modified as a result of changing booleans.
|
||||||
|
*/
|
||||||
|
rc = cond_policydb_dup(&newpolicy->policydb, &oldpolicy->policydb);
|
||||||
|
if (rc) {
|
||||||
|
kfree(newpolicy);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the boolean states in the copy */
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
if (!!values[i] != policydb->bool_val_to_struct[i]->state) {
|
int new_state = !!values[i];
|
||||||
|
int old_state = newpolicy->policydb.bool_val_to_struct[i]->state;
|
||||||
|
|
||||||
|
if (new_state != old_state) {
|
||||||
audit_log(audit_context(), GFP_ATOMIC,
|
audit_log(audit_context(), GFP_ATOMIC,
|
||||||
AUDIT_MAC_CONFIG_CHANGE,
|
AUDIT_MAC_CONFIG_CHANGE,
|
||||||
"bool=%s val=%d old_val=%d auid=%u ses=%u",
|
"bool=%s val=%d old_val=%d auid=%u ses=%u",
|
||||||
sym_name(policydb, SYM_BOOLS, i),
|
sym_name(&newpolicy->policydb, SYM_BOOLS, i),
|
||||||
!!values[i],
|
new_state,
|
||||||
policydb->bool_val_to_struct[i]->state,
|
old_state,
|
||||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||||
audit_get_sessionid(current));
|
audit_get_sessionid(current));
|
||||||
|
newpolicy->policydb.bool_val_to_struct[i]->state = new_state;
|
||||||
}
|
}
|
||||||
if (values[i])
|
|
||||||
policydb->bool_val_to_struct[i]->state = 1;
|
|
||||||
else
|
|
||||||
policydb->bool_val_to_struct[i]->state = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate_cond_nodes(policydb);
|
/* Re-evaluate the conditional rules in the copy */
|
||||||
|
evaluate_cond_nodes(&newpolicy->policydb);
|
||||||
|
|
||||||
|
/* Install the new policy */
|
||||||
|
write_lock_irq(&state->ss->policy_rwlock);
|
||||||
|
state->ss->policy = newpolicy;
|
||||||
seqno = ++state->ss->latest_granting;
|
seqno = ++state->ss->latest_granting;
|
||||||
rc = 0;
|
|
||||||
out:
|
|
||||||
write_unlock_irq(&state->ss->policy_rwlock);
|
write_unlock_irq(&state->ss->policy_rwlock);
|
||||||
if (!rc) {
|
|
||||||
avc_ss_reset(state->avc, seqno);
|
/*
|
||||||
selnl_notify_policyload(seqno);
|
* Free the conditional portions of the old policydb
|
||||||
selinux_status_update_policyload(state, seqno);
|
* that were copied for the new policy.
|
||||||
selinux_xfrm_notify_policyload();
|
*/
|
||||||
}
|
cond_policydb_destroy_dup(&oldpolicy->policydb);
|
||||||
return rc;
|
|
||||||
|
/* Free the old policy structure but not what it references. */
|
||||||
|
kfree(oldpolicy);
|
||||||
|
|
||||||
|
/* Notify others of the policy change */
|
||||||
|
selinux_notify_policy_change(state, seqno);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int security_get_bool_value(struct selinux_state *state,
|
int security_get_bool_value(struct selinux_state *state,
|
||||||
|
@ -3015,7 +3054,7 @@ int security_sid_mls_copy(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
if (!policydb->mls_enabled) {
|
if (!policydb->mls_enabled) {
|
||||||
*new_sid = sid;
|
*new_sid = sid;
|
||||||
|
@ -3125,7 +3164,7 @@ int security_net_peersid_resolve(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't need to check initialized here since the only way both
|
* We don't need to check initialized here since the only way both
|
||||||
|
@ -3467,7 +3506,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxt = sidtab_search(&state->ss->policy->sidtab, sid);
|
ctxt = sidtab_search(state->ss->policy->sidtab, sid);
|
||||||
if (unlikely(!ctxt)) {
|
if (unlikely(!ctxt)) {
|
||||||
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
|
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
|
||||||
sid);
|
sid);
|
||||||
|
@ -3643,7 +3682,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
sidtab = &state->ss->policy->sidtab;
|
sidtab = state->ss->policy->sidtab;
|
||||||
|
|
||||||
if (secattr->flags & NETLBL_SECATTR_CACHE)
|
if (secattr->flags & NETLBL_SECATTR_CACHE)
|
||||||
*sid = *(u32 *)secattr->cache->data;
|
*sid = *(u32 *)secattr->cache->data;
|
||||||
|
@ -3713,7 +3752,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state,
|
||||||
policydb = &state->ss->policy->policydb;
|
policydb = &state->ss->policy->policydb;
|
||||||
|
|
||||||
rc = -ENOENT;
|
rc = -ENOENT;
|
||||||
ctx = sidtab_search(&state->ss->policy->sidtab, sid);
|
ctx = sidtab_search(state->ss->policy->sidtab, sid);
|
||||||
if (ctx == NULL)
|
if (ctx == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ struct selinux_map {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct selinux_policy {
|
struct selinux_policy {
|
||||||
struct sidtab sidtab;
|
struct sidtab *sidtab;
|
||||||
struct policydb policydb;
|
struct policydb policydb;
|
||||||
struct selinux_map map;
|
struct selinux_map map;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user