diff -rNU4 linux-2.6.9.selinux/security/selinux/hooks.c linux-2.6.9.selinux-policydb/security/selinux/hooks.c --- linux-2.6.9.selinux/security/selinux/hooks.c 2004-11-09 19:27:09.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/hooks.c 2004-11-09 19:27:07.000000000 +0900 @@ -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; diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/avtab.c linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.c --- linux-2.6.9.selinux/security/selinux/ss/avtab.c 2004-11-09 19:27:08.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.c 2004-11-10 11:43:39.000000000 +0900 @@ -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; +} diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/avtab.h linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.h --- linux-2.6.9.selinux/security/selinux/ss/avtab.h 2004-11-09 19:27:07.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/avtab.h 2004-11-10 11:44:09.000000000 +0900 @@ -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 diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/conditional.c linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.c --- linux-2.6.9.selinux/security/selinux/ss/conditional.c 2004-11-09 19:27:07.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.c 2004-11-10 21:29:08.000000000 +0900 @@ -12,9 +12,9 @@ #include #include #include #include - +#include #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 */ +} +/* ------------------------------------------------------------ */ diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/conditional.h linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.h --- linux-2.6.9.selinux/security/selinux/ss/conditional.h 2004-11-09 19:27:08.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/conditional.h 2004-11-09 19:27:05.000000000 +0900 @@ -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_ */ diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/hashtab.c linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.c --- linux-2.6.9.selinux/security/selinux/ss/hashtab.c 2004-11-09 19:27:08.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.c 2004-11-10 21:32:56.000000000 +0900 @@ -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; +} + + diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/hashtab.h linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.h --- linux-2.6.9.selinux/security/selinux/ss/hashtab.h 2004-11-09 19:27:08.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/hashtab.h 2004-11-09 19:27:05.000000000 +0900 @@ -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 */ diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/policydb.c linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.c --- linux-2.6.9.selinux/security/selinux/ss/policydb.c 2004-11-09 19:27:08.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.c 2004-11-11 10:27:54.889230773 +0900 @@ -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; } /* diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/policydb.h linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.h --- linux-2.6.9.selinux/security/selinux/ss/policydb.h 2004-11-09 19:27:08.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/policydb.h 2004-11-09 19:27:04.000000000 +0900 @@ -17,8 +17,9 @@ #ifndef _SS_POLICYDB_H_ #define _SS_POLICYDB_H_ +#include #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); diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/services.c linux-2.6.9.selinux-policydb/security/selinux/ss/services.c --- linux-2.6.9.selinux/security/selinux/ss/services.c 2004-11-09 20:14:15.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/services.c 2004-11-10 21:29:15.000000000 +0900 @@ -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; } diff -rNU4 linux-2.6.9.selinux/security/selinux/ss/services.h linux-2.6.9.selinux-policydb/security/selinux/ss/services.h --- linux-2.6.9.selinux/security/selinux/ss/services.h 2004-11-09 19:27:07.000000000 +0900 +++ linux-2.6.9.selinux-policydb/security/selinux/ss/services.h 2004-11-09 19:27:05.000000000 +0900 @@ -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_ */