@@ -290,9 +290,9 @@
#endif /* CONFIG_SECURITY_NETWORK */
/* The security server must be initialized before
any labeling or access decisions can be provided. */
-extern int ss_initialized;
+extern struct policydb *policydb;
/* The file system's label must be initialized prior to use. */
static char *labeling_behaviors[6] = {
@@ -506,16 +506,18 @@
down(&sbsec->sem);
if (sbsec->initialized)
goto out;
- if (!ss_initialized) {
+ rcu_read_lock();
+ if (!policydb) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
spin_lock(&sb_security_lock);
if (list_empty(&sbsec->list))
list_add(&sbsec->list, &superblock_security_head);
spin_unlock(&sb_security_lock);
+ rcu_read_unlock();
goto out;
}
/* Determine the labeling behavior to use for this filesystem type. */
@@ -4416,12 +4418,15 @@
{
extern void exit_sel_fs(void);
static int selinux_disabled = 0;
- if (ss_initialized) {
+ rcu_read_lock();
+ if (policydb) {
/* Not permitted after initial policy load. */
+ rcu_read_unlock();
return -EINVAL;
}
+ rcu_read_unlock();
if (selinux_disabled) {
/* Only do this once. */
return -EINVAL;
@@ -231,9 +231,9 @@
kmem_cache_free(avtab_node_cachep, temp);
}
h->htable[i] = NULL;
}
- vfree(h->htable);
+ kfree(h->htable);
h->htable = NULL;
}
@@ -264,9 +264,9 @@
int avtab_init(struct avtab *h)
{
int i;
- h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE);
+ h->htable = kmalloc(sizeof(*(h->htable)) * AVTAB_SIZE, GFP_KERNEL);
if (!h->htable)
return -ENOMEM;
for (i = 0; i < AVTAB_SIZE; i++)
h->htable[i] = NULL;
@@ -406,4 +406,38 @@
avtab_node_cachep = kmem_cache_create("avtab_node",
sizeof(struct avtab_node),
0, SLAB_PANIC, NULL, NULL);
}
+
+int avtab_duplicate(struct avtab *new, struct avtab *orig)
+{
+ int i,rc;
+ struct avtab_node *node, *tmp, *tail;
+
+ if (!new || !orig)
+ return -EFAULT;
+
+ rc = avtab_init(new);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < AVTAB_SIZE; i++) {
+ tail = NULL;
+ for (node = orig->htable[i]; node; node = node->next) {
+ tmp = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL);
+ if (!tmp)
+ goto error;
+ memcpy(tmp, node, sizeof(struct avtab_node));
+ if (!tail) {
+ new->htable[i] = tmp;
+ } else {
+ tail->next = tmp;
+ }
+ tail = tmp;
+ new->nel++;
+ }
+ }
+ return 0;
+ error:
+ avtab_destroy(new);
+ return -ENOMEM;
+}
@@ -79,9 +79,12 @@
struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
void avtab_cache_init(void);
-#define AVTAB_HASH_BITS 15
+int avtab_duplicate(struct avtab *new, struct avtab *orig);
+
+//#define AVTAB_HASH_BITS 15
+#define AVTAB_HASH_BITS 13
#define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS)
#define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1)
#define AVTAB_SIZE AVTAB_HASH_BUCKETS
@@ -12,9 +12,9 @@
#include <linux/string.h>
#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include <linux/slab.h>
-
+#include <linux/rcupdate.h>
#include "security.h"
#include "conditional.h"
/*
@@ -484,4 +484,213 @@
avd->auditallow |= avtab_auditallow(&node->datum);
}
return;
}
+/* ------------------------------------------------------------ */
+static struct cond_av_list *cond_dup_av_list(struct cond_av_list *orig, struct avtab *te_cond)
+{
+ struct avtab_datum *avdatum;
+ struct cond_av_list *cur, *tmp, *tail = NULL, *head = NULL;
+
+ for (cur = orig; cur; cur = cur->next) {
+ avdatum = avtab_search(te_cond, &cur->node->key, cur->node->datum.specified);
+ if (!avdatum)
+ goto error;
+
+ tmp = kmalloc(sizeof(struct cond_av_list), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+
+ tmp->node = container_of(avdatum, struct avtab_node, datum);
+ tmp->next = NULL;
+ if (!head) {
+ head = tail = tmp;
+ } else {
+ tail->next = tmp;
+ tail = tmp;
+ }
+ }
+ return head;
+ error:
+ for (cur=head; cur; cur = tmp) {
+ tmp = cur->next;
+ kfree(tmp);
+ }
+ return NULL;
+}
+
+static struct cond_expr *cond_dup_expr(struct cond_expr *orig)
+{
+ struct cond_expr *cur, *tmp, *tail, *head;
+
+ tail = head = NULL;
+ for (cur = orig; cur; cur = cur->next) {
+ tmp = kmalloc(sizeof(struct cond_expr), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ tmp->expr_type = cur->expr_type;
+ tmp->bool = cur->bool;
+ tmp->next = NULL;
+ if (!head) {
+ head = tail = tmp;
+ } else {
+ tail->next = tmp;
+ tail = tmp;
+ }
+ }
+ return head; /* success */
+
+ error:
+ for (cur = head; cur; cur = tmp) {
+ tmp = cur->next;
+ kfree(cur);
+ }
+ return NULL;
+}
+
+/* ------------------------------------------------------------ */
+int duplicate_policydb_cond_list(struct policydb *new, struct policydb *orig)
+{
+ int rc;
+ struct cond_node *cur, *tmp, *tail = NULL;
+
+ rc = avtab_duplicate(&new->te_cond_avtab, &orig->te_cond_avtab);
+ if (rc)
+ return rc;
+
+ new->cond_list = NULL;
+ for (cur = orig->cond_list; cur; cur = cur->next) {
+ tmp = kmalloc(sizeof(struct cond_node), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ memset(tmp, 0, sizeof(struct cond_node));
+ tmp->cur_state = cur->cur_state;
+
+ if (!tail) {
+ new->cond_list = tmp;
+ } else {
+ tail->next = tmp;
+ }
+ tail = tmp;
+
+ tmp->expr = cond_dup_expr(cur->expr);
+ if (cur->expr && !tmp->expr)
+ goto error;
+
+ tmp->true_list = cond_dup_av_list(cur->true_list, &new->te_cond_avtab);
+ if (cur->true_list && !tmp->true_list)
+ goto error;
+
+ tmp->false_list = cond_dup_av_list(cur->false_list, &new->te_cond_avtab);
+ if (cur->false_list && !tmp->false_list)
+ goto error;
+ }
+ return 0; /* success*/
+
+ error:
+ cond_list_destroy(new->cond_list);
+ return -ENOMEM;
+}
+
+static int cond_bools_destroy(void *key, void *datum, void *args)
+{
+ 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; /* Not need to duplicate */
+ 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)
+{
+ int len;
+ struct hashtab *newht;
+ struct cond_bool_datum **cond_bool_array;
+
+ if (!newdb || !orig)
+ return -EFAULT;
+
+ len = sizeof(struct cond_bool_datum *) * orig->p_bools.nprim;
+ cond_bool_array = kmalloc(len, GFP_KERNEL);
+ if (!cond_bool_array)
+ return -ENOMEM;
+ memset(cond_bool_array, 0, len);
+
+ newht = hashtab_duplicate(orig->p_bools.table, cond_bools_copy,
+ cond_bools_destroy, NULL);
+ if (!newht) {
+ kfree(cond_bool_array);
+ return -ENOMEM;
+ }
+ hashtab_map(newht, cond_bools_index, cond_bool_array);
+ newdb->bool_val_to_struct = cond_bool_array;
+ newdb->p_bools.table = newht;
+ newdb->p_bools.nprim = newht->nel;
+
+ return 0;
+}
+
+void cond_policydb_free_rcu(struct rcu_head *p)
+{
+ struct policydb *old_policy;
+
+ old_policy = container_of(p, struct policydb, rhead);
+
+ hashtab_map(old_policy->p_bools.table, cond_bools_destroy, NULL);
+ hashtab_destroy(old_policy->p_bools.table);
+ kfree(old_policy->bool_val_to_struct);
+
+ avtab_destroy(&old_policy->te_cond_avtab);
+ cond_list_destroy(old_policy->cond_list);
+
+ printk("olddb was kfree()'d at cond_policydb_free_rcu()\n");
+
+ kfree(old_policy);
+}
+
+struct policydb *cond_policydb_dup(struct policydb *orig)
+{
+ struct policydb *newdb;
+
+ newdb = kmalloc(sizeof(struct policydb), GFP_KERNEL);
+ if (!newdb)
+ return NULL;
+
+ memcpy(newdb, orig, sizeof(struct policydb));
+
+ if (duplicate_policydb_bools(newdb, orig)) {
+ kfree(newdb);
+ return NULL;
+ }
+
+ if (duplicate_policydb_cond_list(newdb, orig)) {
+ cond_policydb_destroy(newdb);
+ kfree(newdb);
+ return NULL;
+ }
+
+ return newdb; /* success */
+}
+/* ------------------------------------------------------------ */
@@ -73,5 +73,8 @@
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);
int evaluate_cond_node(struct policydb *p, struct cond_node *node);
+void cond_policydb_free_rcu(struct rcu_head *p);
+struct policydb *cond_policydb_dup(struct policydb *orig);
+
#endif /* _CONDITIONAL_H_ */
@@ -277,4 +277,56 @@
info->slots_used = slots_used;
info->max_chain_len = max_chain_len;
}
+
+struct hashtab *hashtab_duplicate(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)
+{
+ int i,rc;
+ struct hashtab *new;
+ struct hashtab_node *cur, *tmp, *tail;
+
+ if (!orig || !copy || !destroy)
+ return NULL;
+
+ new = hashtab_create(orig->hash_value, orig->keycmp, orig->size);
+ if (!new)
+ return NULL;
+
+ for (i = 0; i < orig->size; i++) {
+ tail = NULL;
+ for (cur = orig->htable[i]; cur; cur = cur->next) {
+ tmp = kmalloc(sizeof(struct hashtab_node), GFP_KERNEL);
+ if (!tmp)
+ goto error;
+ rc = copy(tmp, cur, args);
+ if (rc) {
+ kfree(tmp);
+ goto error;
+ }
+ tmp->next = NULL;
+ if (!tail) {
+ new->htable[i] = tmp;
+ } else {
+ tail->next = tmp;
+ }
+ tail = tmp;
+ new->nel++;
+ }
+ }
+ return new;
+ 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);
+ kfree(cur);
+ }
+ }
+ kfree(new);
+ return NULL;
+}
+
+
@@ -121,5 +121,12 @@
/* Fill info with some hash table statistics */
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
+
+struct hashtab *hashtab_duplicate(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);
+
+
#endif /* _SS_HASHTAB_H */
@@ -141,8 +141,13 @@
int i, rc;
memset(p, 0, sizeof(*p));
+ p->sidtab = kmalloc(sizeof(struct sidtab), GFP_KERNEL);
+ if (!p->sidtab)
+ return -ENOMEM;
+ sidtab_init(p->sidtab);
+
for (i = 0; i < SYM_NUM; i++) {
rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
if (rc)
goto out_free_symtab;
@@ -168,8 +173,9 @@
out_free_symtab:
for (i = 0; i < SYM_NUM; i++)
hashtab_destroy(p->symtab[i].table);
+ kfree(p->sidtab);
goto out;
}
/*
@@ -17,8 +17,9 @@
#ifndef _SS_POLICYDB_H_
#define _SS_POLICYDB_H_
+#include <linux/rcupdate.h>
#include "symtab.h"
#include "avtab.h"
#include "sidtab.h"
#include "context.h"
@@ -245,8 +246,10 @@
struct ebitmap trustedreaders;
struct ebitmap trustedwriters;
struct ebitmap trustedobjects;
#endif
+ struct rcu_head rhead;
+ struct sidtab *sidtab;
};
extern int policydb_init(struct policydb *p);
extern int policydb_index_classes(struct policydb *p);
@@ -41,21 +41,16 @@
extern void selnl_notify_policyload(u32 seqno);
extern int policydb_loaded_version;
-static rwlock_t policy_rwlock = RW_LOCK_UNLOCKED;
-#define POLICY_RDLOCK read_lock(&policy_rwlock)
-#define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
-#define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
-#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)
+#define POLICY_RDLOCK rcu_read_lock();
+#define POLICY_RDUNLOCK rcu_read_unlock();
static DECLARE_MUTEX(load_sem);
-#define LOAD_LOCK down(&load_sem)
-#define LOAD_UNLOCK up(&load_sem)
+#define LOAD_LOCK do{ down(&load_sem); rcu_read_lock(); } while(0)
+#define LOAD_UNLOCK do{ rcu_read_unlock(); up(&load_sem); } while(0)
-struct sidtab sidtab;
-struct policydb policydb;
-int ss_initialized = 0;
+struct policydb *policydb = NULL;
/*
* The largest sequence number that has been used when
* providing an access decision to the access vector cache.
@@ -68,9 +63,10 @@
* Return the boolean value of a constraint expression
* when it is applied to the specified source and target
* security contexts.
*/
-static int constraint_expr_eval(struct context *scontext,
+static int constraint_expr_eval(struct policydb *p,
+ struct context *scontext,
struct context *tcontext,
struct constraint_expr *cexpr)
{
u32 val1, val2;
@@ -110,10 +106,10 @@
break;
case CEXPR_ROLE:
val1 = scontext->role;
val2 = tcontext->role;
- r1 = policydb.role_val_to_struct[val1 - 1];
- r2 = policydb.role_val_to_struct[val2 - 1];
+ r1 = p->role_val_to_struct[val1 - 1];
+ r2 = p->role_val_to_struct[val2 - 1];
switch (e->op) {
case CEXPR_DOM:
s[++sp] = ebitmap_get_bit(&r1->dominates,
val2 - 1);
@@ -191,9 +187,10 @@
/*
* Compute access vectors based on a context structure pair for
* the permissions in a particular class.
*/
-static int context_struct_compute_av(struct context *scontext,
+static int context_struct_compute_av(struct policydb *p,
+ struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 requested,
struct av_decision *avd)
@@ -214,14 +211,14 @@
if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
tclass = SECCLASS_NETLINK_SOCKET;
- if (!tclass || tclass > policydb.p_classes.nprim) {
+ if (!tclass || tclass > p->p_classes.nprim) {
printk(KERN_ERR "security_compute_av: unrecognized class %d\n",
tclass);
return -EINVAL;
}
- tclass_datum = policydb.class_val_to_struct[tclass - 1];
+ tclass_datum = p->class_val_to_struct[tclass - 1];
/*
* Initialize the access vectors to the default values.
*/
@@ -237,9 +234,9 @@
*/
avkey.source_type = scontext->type;
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
- avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_AV);
+ avdatum = avtab_search(&p->te_avtab, &avkey, AVTAB_AV);
if (avdatum) {
if (avdatum->specified & AVTAB_ALLOWED)
avd->allowed = avtab_allowed(avdatum);
if (avdatum->specified & AVTAB_AUDITDENY)
@@ -248,9 +245,9 @@
avd->auditallow = avtab_auditallow(avdatum);
}
/* Check conditional av table for additional permissions */
- cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
+ cond_compute_av(&p->te_cond_avtab, &avkey, avd);
/*
* Remove any permissions prohibited by the MLS policy.
*/
@@ -261,9 +258,9 @@
*/
constraint = tclass_datum->constraints;
while (constraint) {
if ((constraint->permissions & (avd->allowed)) &&
- !constraint_expr_eval(scontext, tcontext,
+ !constraint_expr_eval(p, scontext, tcontext,
constraint->expr)) {
avd->allowed = (avd->allowed) & ~(constraint->permissions);
}
constraint = constraint->next;
@@ -276,9 +273,9 @@
*/
if (tclass == SECCLASS_PROCESS &&
(avd->allowed & PROCESS__TRANSITION) &&
scontext->role != tcontext->role) {
- for (ra = policydb.role_allow; ra; ra = ra->next) {
+ for (ra = p->role_allow; ra; ra = ra->next) {
if (scontext->role == ra->role &&
tcontext->role == ra->new_role)
break;
}
@@ -307,38 +304,40 @@
u16 tclass,
u32 requested,
struct av_decision *avd)
{
+ struct policydb *p;
struct context *scontext = NULL, *tcontext = NULL;
int rc = 0;
- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
avd->allowed = requested;
avd->decided = requested;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
- return 0;
+ goto out;
}
- POLICY_RDLOCK;
-
- scontext = sidtab_search(&sidtab, ssid);
+ scontext = sidtab_search(p->sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
ssid);
rc = -EINVAL;
goto out;
}
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(p->sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "security_compute_av: unrecognized SID %d\n",
tsid);
rc = -EINVAL;
goto out;
}
- rc = context_struct_compute_av(scontext, tcontext, tclass,
+ rc = context_struct_compute_av(p, scontext, tcontext, tclass,
requested, avd);
out:
POLICY_RDUNLOCK;
return rc;
@@ -350,19 +349,20 @@
* allocated string of the correct size. Set `*scontext'
* to point to this string and set `*scontext_len' to
* the length of the string.
*/
-int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
+int context_struct_to_string(struct policydb *p, struct context *context,
+ char **scontext, u32 *scontext_len)
{
char *scontextp;
*scontext = NULL;
*scontext_len = 0;
/* Compute the size of the context. */
- *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
- *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
- *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+ *scontext_len += strlen(p->p_user_val_to_name[context->user - 1]) + 1;
+ *scontext_len += strlen(p->p_role_val_to_name[context->role - 1]) + 1;
+ *scontext_len += strlen(p->p_type_val_to_name[context->type - 1]) + 1;
*scontext_len += mls_compute_context_len(context);
/* Allocate space for the context; caller must free this space. */
scontextp = kmalloc(*scontext_len+1,GFP_ATOMIC);
@@ -374,14 +374,14 @@
/*
* Copy the user name, role name and type name into the context.
*/
sprintf(scontextp, "%s:%s:%s:",
- policydb.p_user_val_to_name[context->user - 1],
- policydb.p_role_val_to_name[context->role - 1],
- policydb.p_type_val_to_name[context->type - 1]);
- scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
- 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
- 1 + strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+ p->p_user_val_to_name[context->user - 1],
+ p->p_role_val_to_name[context->role - 1],
+ p->p_type_val_to_name[context->type - 1]);
+ scontextp += strlen(p->p_user_val_to_name[context->user - 1]) +
+ 1 + strlen(p->p_role_val_to_name[context->role - 1]) +
+ 1 + strlen(p->p_type_val_to_name[context->type - 1]) + 1;
mls_sid_to_context(context, &scontextp);
scontextp--;
@@ -403,12 +403,16 @@
* to point to this string and set @scontext_len to the length of the string.
*/
int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
{
+ struct policydb *p;
struct context *context;
int rc = 0;
- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
if (sid <= SECINITSID_NUM) {
char *scontextp;
*scontext_len = strlen(initial_sid_to_string[sid]) + 1;
@@ -421,20 +425,19 @@
"load_policy on unknown SID %d\n", sid);
rc = -EINVAL;
goto out;
}
- POLICY_RDLOCK;
- context = sidtab_search(&sidtab, sid);
+
+ context = sidtab_search(p->sidtab, sid);
if (!context) {
printk(KERN_ERR "security_sid_to_context: unrecognized SID "
"%d\n", sid);
rc = -EINVAL;
- goto out_unlock;
+ goto out;
}
- rc = context_struct_to_string(context, scontext, scontext_len);
-out_unlock:
- POLICY_RDUNLOCK;
+ rc = context_struct_to_string(p, context, scontext, scontext_len);
out:
+ POLICY_RDUNLOCK;
return rc;
}
@@ -451,47 +454,46 @@
*/
int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid)
{
char *scontext2;
+ struct policydb *pol;
struct context context;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *scontextp, *p, oldc;
int rc = 0;
- if (!ss_initialized) {
+ /* Copy the string so that we can modify the copy as we parse it.
+ The string should already by null terminated, but we append a
+ null suffix to the copy to avoid problems with the existing
+ attr package, which doesn't view the null terminator as part
+ of the attribute value. */
+ scontext2 = kmalloc(scontext_len+1,GFP_KERNEL);
+ if (!scontext2)
+ return -ENOMEM;
+ memcpy(scontext2, scontext, scontext_len);
+ scontext2[scontext_len] = 0;
+
+ POLICY_RDLOCK;
+ pol = policydb;
+
+ if (!pol) {
int i;
for (i = 1; i < SECINITSID_NUM; i++) {
- if (!strcmp(initial_sid_to_string[i], scontext)) {
+ if (!strcmp(initial_sid_to_string[i], scontext2)) {
*sid = i;
goto out;
}
}
*sid = SECINITSID_KERNEL;
goto out;
}
- *sid = SECSID_NULL;
-
- /* Copy the string so that we can modify the copy as we parse it.
- The string should already by null terminated, but we append a
- null suffix to the copy to avoid problems with the existing
- attr package, which doesn't view the null terminator as part
- of the attribute value. */
- scontext2 = kmalloc(scontext_len+1,GFP_KERNEL);
- if (!scontext2) {
- rc = -ENOMEM;
- goto out;
- }
- memcpy(scontext2, scontext, scontext_len);
- scontext2[scontext_len] = 0;
context_init(&context);
*sid = SECSID_NULL;
- POLICY_RDLOCK;
-
/* Parse the security context. */
rc = -EINVAL;
scontextp = (char *) scontext2;
@@ -505,9 +507,9 @@
goto out_unlock;
*p++ = 0;
- usrdatum = hashtab_search(policydb.p_users.table, scontextp);
+ usrdatum = hashtab_search(pol->p_users.table, scontextp);
if (!usrdatum)
goto out_unlock;
context.user = usrdatum->value;
@@ -521,9 +523,9 @@
goto out_unlock;
*p++ = 0;
- role = hashtab_search(policydb.p_roles.table, scontextp);
+ role = hashtab_search(pol->p_roles.table, scontextp);
if (!role)
goto out_unlock;
context.role = role->value;
@@ -533,9 +535,9 @@
p++;
oldc = *p;
*p++ = 0;
- typdatum = hashtab_search(policydb.p_types.table, scontextp);
+ typdatum = hashtab_search(pol->p_types.table, scontextp);
if (!typdatum)
goto out_unlock;
context.type = typdatum->value;
@@ -549,43 +551,44 @@
goto out_unlock;
}
/* Check the validity of the new context. */
- if (!policydb_context_isvalid(&policydb, &context)) {
+ if (!policydb_context_isvalid(pol, &context)) {
rc = -EINVAL;
goto out_unlock;
}
/* Obtain the new sid. */
- rc = sidtab_context_to_sid(&sidtab, &context, sid);
+ rc = sidtab_context_to_sid(pol->sidtab, &context, sid);
out_unlock:
POLICY_RDUNLOCK;
context_destroy(&context);
- kfree(scontext2);
out:
+ kfree(scontext2);
return rc;
}
static int compute_sid_handle_invalid_context(
+ struct policydb *p,
struct context *scontext,
struct context *tcontext,
u16 tclass,
struct context *newcontext)
{
char *s = NULL, *t = NULL, *n = NULL;
u32 slen, tlen, nlen;
- if (context_struct_to_string(scontext, &s, &slen) < 0)
+ if (context_struct_to_string(p, scontext, &s, &slen) < 0)
goto out;
- if (context_struct_to_string(tcontext, &t, &tlen) < 0)
+ if (context_struct_to_string(p, tcontext, &t, &tlen) < 0)
goto out;
- if (context_struct_to_string(newcontext, &n, &nlen) < 0)
+ if (context_struct_to_string(p, newcontext, &n, &nlen) < 0)
goto out;
audit_log(current->audit_context,
"security_compute_sid: invalid context %s"
" for scontext=%s"
" tcontext=%s"
" tclass=%s",
- n, s, t, policydb.p_class_val_to_name[tclass-1]);
+ n, s, t, p->p_class_val_to_name[tclass-1]);
out:
kfree(s);
kfree(t);
kfree(n);
@@ -599,17 +602,21 @@
u16 tclass,
u32 specified,
u32 *out_sid)
{
+ struct policydb *p;
struct context *scontext = NULL, *tcontext = NULL, newcontext;
struct role_trans *roletr = NULL;
struct avtab_key avkey;
struct avtab_datum *avdatum;
struct avtab_node *node;
unsigned int type_change = 0;
int rc = 0;
- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
switch (tclass) {
case SECCLASS_PROCESS:
*out_sid = ssid;
break;
@@ -619,18 +626,16 @@
}
goto out;
}
- POLICY_RDLOCK;
-
- scontext = sidtab_search(&sidtab, ssid);
+ scontext = sidtab_search(p->sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
ssid);
rc = -EINVAL;
goto out_unlock;
}
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(p->sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n",
tsid);
rc = -EINVAL;
@@ -669,13 +674,13 @@
/* Look for a type transition/member/change rule. */
avkey.source_type = scontext->type;
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
- avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_TYPE);
+ avdatum = avtab_search(&p->te_avtab, &avkey, AVTAB_TYPE);
/* If no permanent rule, also check for enabled conditional rules */
if(!avdatum) {
- node = avtab_search_node(&policydb.te_cond_avtab, &avkey, specified);
+ node = avtab_search_node(&p->te_cond_avtab, &avkey, specified);
for (; node != NULL; node = avtab_search_node_next(node, specified)) {
if (node->datum.specified & AVTAB_ENABLED) {
avdatum = &node->datum;
break;
@@ -703,9 +708,9 @@
switch (tclass) {
case SECCLASS_PROCESS:
if (specified & AVTAB_TRANSITION) {
/* Look for a role transition rule. */
- for (roletr = policydb.role_tr; roletr;
+ for (roletr = p->role_tr; roletr;
roletr = roletr->next) {
if (roletr->role == scontext->role &&
roletr->type == tcontext->type) {
/* Use the role transition rule. */
@@ -740,18 +745,16 @@
if (rc)
goto out_unlock;
/* Check the validity of the context. */
- if (!policydb_context_isvalid(&policydb, &newcontext)) {
- rc = compute_sid_handle_invalid_context(scontext,
- tcontext,
- tclass,
- &newcontext);
+ if (!policydb_context_isvalid(p, &newcontext)) {
+ rc = compute_sid_handle_invalid_context(p, scontext, tcontext,
+ tclass, &newcontext);
if (rc)
goto out_unlock;
}
/* Obtain the sid for the context. */
- rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
+ rc = sidtab_context_to_sid(p->sidtab, &newcontext, out_sid);
out_unlock:
POLICY_RDUNLOCK;
context_destroy(&newcontext);
out:
@@ -913,9 +916,9 @@
return sidtab_insert(s, sid, context);
}
-static inline int convert_context_handle_invalid_context(struct context *context)
+static inline int convert_context_handle_invalid_context(struct policydb *p, struct context *context)
{
int rc = 0;
if (selinux_enforcing) {
@@ -923,9 +926,9 @@
} else {
char *s;
u32 len;
- context_struct_to_string(context, &s, &len);
+ context_struct_to_string(p, context, &s, &len);
printk(KERN_ERR "security: context %s is invalid\n", s);
kfree(s);
}
return rc;
@@ -993,26 +996,37 @@
goto bad;
/* Check the validity of the new context. */
if (!policydb_context_isvalid(args->newp, c)) {
- rc = convert_context_handle_invalid_context(&oldc);
+ rc = convert_context_handle_invalid_context(args->oldp, &oldc);
if (rc)
goto bad;
}
context_destroy(&oldc);
out:
return rc;
bad:
- context_struct_to_string(&oldc, &s, &len);
+ context_struct_to_string(args->oldp, &oldc, &s, &len);
context_destroy(&oldc);
printk(KERN_ERR "security: invalidating context %s\n", s);
kfree(s);
goto out;
}
extern void selinux_complete_init(void);
+void policydb_destroy_rcu(struct rcu_head *p)
+{
+ struct policydb *olddb;
+ olddb = container_of(p, struct policydb, rhead);
+
+ sidtab_destroy(olddb->sidtab);
+ policydb_destroy(olddb);
+ kfree(olddb);
+ printk("kfree(olddb) in policydb_destroy_rcu() preempt()=0x%x\n",preempt_count());
+}
+
/**
* security_load_policy - Load a security policy configuration.
* @data: binary policy data
* @len: length of data in bytes
@@ -1023,29 +1037,34 @@
* loading the new policy.
*/
int security_load_policy(void *data, size_t len)
{
- struct policydb oldpolicydb, newpolicydb;
- struct sidtab oldsidtab, newsidtab;
+ struct policydb *newdb, *olddb;
struct convert_context_args args;
u32 seqno;
int rc = 0;
struct policy_file file = { data, len }, *fp = &file;
+ newdb = kmalloc(sizeof(struct policydb), GFP_KERNEL);
+ if (!newdb)
+ return -ENOMEM;
+
LOAD_LOCK;
+ olddb = policydb;
- if (!ss_initialized) {
+ if (!olddb) {
avtab_cache_init();
- if (policydb_read(&policydb, fp)) {
+ if (policydb_read(newdb, fp)) {
LOAD_UNLOCK;
return -EINVAL;
}
- if (policydb_load_isids(&policydb, &sidtab)) {
+ if (policydb_load_isids(newdb, newdb->sidtab)) {
LOAD_UNLOCK;
- policydb_destroy(&policydb);
+ policydb_destroy(newdb);
return -EINVAL;
}
- ss_initialized = 1;
+ smp_wmb();
+ policydb = newdb;
LOAD_UNLOCK;
selinux_complete_init();
return 0;
@@ -1054,62 +1073,52 @@
#if 0
sidtab_hash_eval(&sidtab, "sids");
#endif
- if (policydb_read(&newpolicydb, fp)) {
+ if (policydb_read(newdb, fp)) {
LOAD_UNLOCK;
return -EINVAL;
}
- sidtab_init(&newsidtab);
-
/* Verify that the existing classes did not change. */
- if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) {
+ if (hashtab_map(olddb->p_classes.table, validate_class, newdb)) {
printk(KERN_ERR "security: the definition of an existing "
"class changed\n");
rc = -EINVAL;
goto err;
}
-
+
/* Clone the SID table. */
- sidtab_shutdown(&sidtab);
- if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
+ sidtab_shutdown(olddb->sidtab);
+ if (sidtab_map(olddb->sidtab, clone_sid, newdb->sidtab)) {
rc = -ENOMEM;
goto err;
}
/* Convert the internal representations of contexts
in the new SID table and remove invalid SIDs. */
- args.oldp = &policydb;
- args.newp = &newpolicydb;
- sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
-
- /* Save the old policydb and SID table to free later. */
- memcpy(&oldpolicydb, &policydb, sizeof policydb);
- sidtab_set(&oldsidtab, &sidtab);
-
- /* Install the new policydb and SID table. */
- POLICY_WRLOCK;
- memcpy(&policydb, &newpolicydb, sizeof policydb);
- sidtab_set(&sidtab, &newsidtab);
+ args.oldp = olddb;
+ args.newp = newdb;
+ sidtab_map_remove_on_error(newdb->sidtab, convert_context, &args);
+
+ /* update policydb atomically */
+ smp_wmb();
+ policydb = newdb;
seqno = ++latest_granting;
-
- POLICY_WRUNLOCK;
+
LOAD_UNLOCK;
- /* Free the old policydb and SID table. */
- policydb_destroy(&oldpolicydb);
- sidtab_destroy(&oldsidtab);
+ call_rcu(&olddb->rhead, policydb_destroy_rcu);
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
return 0;
err:
LOAD_UNLOCK;
- sidtab_destroy(&newsidtab);
- policydb_destroy(&newpolicydb);
+ sidtab_destroy(newdb->sidtab);
+ policydb_destroy(newdb);
return rc;
}
@@ -1126,14 +1135,21 @@
u8 protocol,
u16 port,
u32 *out_sid)
{
+ struct policydb *p;
struct ocontext *c;
int rc = 0;
POLICY_RDLOCK;
+ p = policydb;
- c = policydb.ocontexts[OCON_PORT];
+ if (!p) {
+ *out_sid = SECINITSID_PORT;
+ goto out;
+ }
+
+ c = p->ocontexts[OCON_PORT];
while (c) {
if (c->u.port.protocol == protocol &&
c->u.port.low_port <= port &&
c->u.port.high_port >= port)
@@ -1142,9 +1158,9 @@
}
if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1169,27 +1185,35 @@
u32 *if_sid,
u32 *msg_sid)
{
int rc = 0;
+ struct policydb *p;
struct ocontext *c;
POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
+ *if_sid = SECINITSID_NETIF;
+ *msg_sid = SECINITSID_NETMSG;
+ goto out;
+ }
- c = policydb.ocontexts[OCON_NETIF];
+ c = p->ocontexts[OCON_NETIF];
while (c) {
if (strcmp(name, c->u.name) == 0)
break;
c = c->next;
}
if (c) {
if (!c->sid[0] || !c->sid[1]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[1],
&c->sid[1]);
if (rc)
goto out;
@@ -1231,11 +1255,18 @@
u32 addrlen,
u32 *out_sid)
{
int rc = 0;
+ struct policydb *p;
struct ocontext *c;
POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
+ *out_sid = SECINITSID_NODE;
+ goto out;
+ }
switch (domain) {
case AF_INET: {
u32 addr;
@@ -1246,9 +1277,9 @@
}
addr = *((u32 *)addrp);
- c = policydb.ocontexts[OCON_NODE];
+ c = p->ocontexts[OCON_NODE];
while (c) {
if (c->u.node.addr == (addr & c->u.node.mask))
break;
c = c->next;
@@ -1260,9 +1291,9 @@
if (addrlen != sizeof(u64) * 2) {
rc = -EINVAL;
goto out;
}
- c = policydb.ocontexts[OCON_NODE6];
+ c = p->ocontexts[OCON_NODE6];
while (c) {
if (match_ipv6_addrmask(addrp, c->u.node6.addr,
c->u.node6.mask))
break;
@@ -1276,9 +1307,9 @@
}
if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1313,64 +1344,66 @@
char *username,
u32 **sids,
u32 *nel)
{
+ struct policydb *p;
struct context *fromcon, usercon;
u32 *mysids, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL;
struct user_datum *user;
struct role_datum *role;
struct av_decision avd;
int rc = 0, i, j;
- if (!ss_initialized) {
+ POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
*sids = NULL;
*nel = 0;
goto out;
}
- POLICY_RDLOCK;
-
- fromcon = sidtab_search(&sidtab, fromsid);
+ fromcon = sidtab_search(p->sidtab, fromsid);
if (!fromcon) {
rc = -EINVAL;
- goto out_unlock;
+ goto out;
}
- user = hashtab_search(policydb.p_users.table, username);
+ user = hashtab_search(p->p_users.table, username);
if (!user) {
rc = -EINVAL;
- goto out_unlock;
+ goto out;
}
usercon.user = user->value;
mysids = kmalloc(maxnel*sizeof(*mysids), GFP_ATOMIC);
if (!mysids) {
rc = -ENOMEM;
- goto out_unlock;
+ goto out;
}
memset(mysids, 0, maxnel*sizeof(*mysids));
for (i = ebitmap_startbit(&user->roles); i < ebitmap_length(&user->roles); i++) {
if (!ebitmap_get_bit(&user->roles, i))
continue;
- role = policydb.role_val_to_struct[i];
+ role = p->role_val_to_struct[i];
usercon.role = i+1;
for (j = ebitmap_startbit(&role->types); j < ebitmap_length(&role->types); j++) {
if (!ebitmap_get_bit(&role->types, j))
continue;
usercon.type = j+1;
mls_for_user_ranges(user,usercon) {
- rc = context_struct_compute_av(fromcon, &usercon,
+ rc = context_struct_compute_av(p, fromcon, &usercon,
SECCLASS_PROCESS,
PROCESS__TRANSITION,
&avd);
if (rc || !(avd.allowed & PROCESS__TRANSITION))
continue;
- rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
+ rc = sidtab_context_to_sid(p->sidtab, &usercon, &sid);
if (rc) {
kfree(mysids);
- goto out_unlock;
+ goto out;
}
if (mynel < maxnel) {
mysids[mynel++] = sid;
} else {
@@ -1378,9 +1411,9 @@
mysids2 = kmalloc(maxnel*sizeof(*mysids2), GFP_ATOMIC);
if (!mysids2) {
rc = -ENOMEM;
kfree(mysids);
- goto out_unlock;
+ goto out;
}
memset(mysids2, 0, maxnel*sizeof(*mysids2));
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
kfree(mysids);
@@ -1393,12 +1426,10 @@
}
*sids = mysids;
*nel = mynel;
-
-out_unlock:
- POLICY_RDUNLOCK;
out:
+ POLICY_RDUNLOCK;
return rc;
}
/**
@@ -1417,15 +1448,23 @@
u16 sclass,
u32 *sid)
{
int len;
+ struct policydb *p;
struct genfs *genfs;
struct ocontext *c;
int rc = 0, cmp = 0;
POLICY_RDLOCK;
+ p = policydb;
- for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
+ if (!p) {
+ *sid = SECINITSID_UNLABELED;
+ rc = -ENOENT;
+ goto out;
+ }
+
+ for (genfs = p->genfs; genfs; genfs = genfs->next) {
cmp = strcmp(fstype, genfs->fstype);
if (cmp <= 0)
break;
}
@@ -1449,9 +1488,9 @@
goto out;
}
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1474,13 +1513,21 @@
unsigned int *behavior,
u32 *sid)
{
int rc = 0;
+ struct policydb *p;
struct ocontext *c;
POLICY_RDLOCK;
+ p = policydb;
+
+ if (!p) {
+ *sid = SECINITSID_UNLABELED;
+ *behavior = SECURITY_FS_USE_NONE;
+ goto out;
+ }
- c = policydb.ocontexts[OCON_FSUSE];
+ c = p->ocontexts[OCON_FSUSE];
while (c) {
if (strcmp(fstype, c->u.name) == 0)
break;
c = c->next;
@@ -1488,9 +1535,9 @@
if (c) {
*behavior = c->v.behavior;
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(p->sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
@@ -1513,14 +1560,25 @@
int security_get_bools(int *len, char ***names, int **values)
{
int i, rc = -ENOMEM;
+ struct policydb *p;
POLICY_RDLOCK;
+ p = policydb;
+ if (!p) {
+ *len = 0;
+ *names = NULL;
+ *values = NULL;
+ rc = 0;
+ goto out;
+ }
+
+
*names = NULL;
*values = NULL;
- *len = policydb.p_bools.nprim;
+ *len = p->p_bools.nprim;
if (!*len) {
rc = 0;
goto out;
}
@@ -1535,14 +1593,14 @@
goto err;
for (i = 0; i < *len; i++) {
size_t name_len;
- (*values)[i] = policydb.bool_val_to_struct[i]->state;
- name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
+ (*values)[i] = p->bool_val_to_struct[i]->state;
+ name_len = strlen(p->p_bool_val_to_name[i]) + 1;
(*names)[i] = (char*)kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
if (!(*names)[i])
goto err;
- strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
+ strncpy((*names)[i], p->p_bool_val_to_name[i], name_len);
(*names)[i][name_len - 1] = 0;
}
rc = 0;
out:
@@ -1563,42 +1621,53 @@
int security_set_bools(int len, int *values)
{
int i, rc = 0;
int lenp, seqno = 0;
+ struct policydb *newdb, *olddb;
struct cond_node *cur;
- POLICY_WRLOCK;
+ LOAD_LOCK;
+ olddb = policydb;
- lenp = policydb.p_bools.nprim;
+ lenp = olddb->p_bools.nprim;
if (len != lenp) {
rc = -EFAULT;
goto out;
}
+ newdb = cond_policydb_dup(olddb);
+ if (!newdb) {
+ rc = -ENOMEM;
+ goto out;
+ }
printk(KERN_INFO "security: committed booleans { ");
for (i = 0; i < len; i++) {
if (values[i]) {
- policydb.bool_val_to_struct[i]->state = 1;
+ newdb->bool_val_to_struct[i]->state = 1;
} else {
- policydb.bool_val_to_struct[i]->state = 0;
+ newdb->bool_val_to_struct[i]->state = 0;
}
if (i != 0)
printk(", ");
- printk("%s:%d", policydb.p_bool_val_to_name[i],
- policydb.bool_val_to_struct[i]->state);
+ printk("%s:%d", newdb->p_bool_val_to_name[i],
+ newdb->bool_val_to_struct[i]->state);
}
printk(" }\n");
- for (cur = policydb.cond_list; cur != NULL; cur = cur->next) {
- rc = evaluate_cond_node(&policydb, cur);
- if (rc)
+ for (cur = newdb->cond_list; cur != NULL; cur = cur->next) {
+ rc = evaluate_cond_node(newdb, cur);
+ if (rc) {
+ call_rcu(&newdb->rhead, cond_policydb_free_rcu);
goto out;
+ }
}
-
+ smp_wmb();
+ policydb = newdb;
seqno = ++latest_granting;
+ call_rcu(&olddb->rhead, cond_policydb_free_rcu);
out:
- POLICY_WRUNLOCK;
+ LOAD_UNLOCK;
if (!rc) {
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
}
@@ -1608,18 +1677,25 @@
int security_get_bool_value(int bool)
{
int rc = 0;
int len;
+ struct policydb *p;
POLICY_RDLOCK;
+ p = policydb;
+ if (!p) {
+ rc = -EFAULT;
+ goto out;
+ }
+
- len = policydb.p_bools.nprim;
+ len = p->p_bools.nprim;
if (bool >= len) {
rc = -EFAULT;
goto out;
}
- rc = policydb.bool_val_to_struct[bool]->state;
+ rc = p->bool_val_to_struct[bool]->state;
out:
POLICY_RDUNLOCK;
return rc;
}
@@ -13,9 +13,9 @@
* The security server uses two global data structures
* when providing its services: the SID table (sidtab)
* and the policy database (policydb).
*/
-extern struct sidtab sidtab;
-extern struct policydb policydb;
+//extern struct sidtab sidtab;
+extern struct policydb *policydb;
#endif /* _SS_SERVICES_H_ */