diff options
author | KaiGai Kohei <kaigai@ak.jp.nec.com> | 2008-08-28 16:35:57 +0900 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2008-08-29 00:33:33 +1000 |
commit | d9250dea3f89fe808a525f08888016b495240ed4 (patch) | |
tree | c4b039ce0b29714e8f4c3bbc6d407adc361cc122 /security/selinux/ss | |
parent | da31894ed7b654e2e1741e7ac4ef6c15be0dd14b (diff) |
SELinux: add boundary support and thread context assignment
The purpose of this patch is to assign per-thread security context
under a constraint. It enables multi-threaded server application
to kick a request handler with its fair security context, and
helps some of userspace object managers to handle user's request.
When we assign a per-thread security context, it must not have wider
permissions than the original one. Because a multi-threaded process
shares a single local memory, an arbitary per-thread security context
also means another thread can easily refer violated information.
The constraint on a per-thread security context requires a new domain
has to be equal or weaker than its original one, when it tries to assign
a per-thread security context.
Bounds relationship between two types is a way to ensure a domain can
never have wider permission than its bounds. We can define it in two
explicit or implicit ways.
The first way is using new TYPEBOUNDS statement. It enables to define
a boundary of types explicitly. The other one expand the concept of
existing named based hierarchy. If we defines a type with "." separated
name like "httpd_t.php", toolchain implicitly set its bounds on "httpd_t".
This feature requires a new policy version.
The 24th version (POLICYDB_VERSION_BOUNDARY) enables to ship them into
kernel space, and the following patch enables to handle it.
Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/ss')
-rw-r--r-- | security/selinux/ss/policydb.c | 205 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 5 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 172 |
3 files changed, 367 insertions, 15 deletions
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 26646305dc0e..72e4a54973aa 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/audit.h> #include "security.h" #include "policydb.h" @@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, .sym_num = SYM_NUM, .ocon_num = OCON_NUM, - } + }, + { + .version = POLICYDB_VERSION_BOUNDARY, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) role = datum; p = datap; - if (!role->value || role->value > p->p_roles.nprim) + if (!role->value + || role->value > p->p_roles.nprim + || role->bounds > p->p_roles.nprim) return -EINVAL; p->p_role_val_to_name[role->value - 1] = key; p->role_val_to_struct[role->value - 1] = role; @@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) p = datap; if (typdatum->primary) { - if (!typdatum->value || typdatum->value > p->p_types.nprim) + if (!typdatum->value + || typdatum->value > p->p_types.nprim + || typdatum->bounds > p->p_types.nprim) return -EINVAL; p->p_type_val_to_name[typdatum->value - 1] = key; + p->type_val_to_struct[typdatum->value - 1] = typdatum; } return 0; @@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) usrdatum = datum; p = datap; - if (!usrdatum->value || usrdatum->value > p->p_users.nprim) + if (!usrdatum->value + || usrdatum->value > p->p_users.nprim + || usrdatum->bounds > p->p_users.nprim) return -EINVAL; p->p_user_val_to_name[usrdatum->value - 1] = key; p->user_val_to_struct[usrdatum->value - 1] = usrdatum; @@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) goto out; } + p->type_val_to_struct = + kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), + GFP_KERNEL); + if (!p->type_val_to_struct) { + rc = -ENOMEM; + goto out; + } + if (cond_init_bool_indexes(p)) { rc = -ENOMEM; goto out; @@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) kfree(p->class_val_to_struct); kfree(p->role_val_to_struct); kfree(p->user_val_to_struct); + kfree(p->type_val_to_struct); avtab_destroy(&p->te_avtab); @@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct role_datum *role; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; role = kzalloc(sizeof(*role), GFP_KERNEL); @@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); role->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + role->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct type_datum *typdatum; - int rc; - __le32 buf[3]; + int rc, to_read = 3; + __le32 buf[4]; u32 len; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) return rc; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 4; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); typdatum->value = le32_to_cpu(buf[1]); - typdatum->primary = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { + u32 prop = le32_to_cpu(buf[2]); + + if (prop & TYPEDATUM_PROPERTY_PRIMARY) + typdatum->primary = 1; + if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) + typdatum->attribute = 1; + + typdatum->bounds = le32_to_cpu(buf[3]); + } else { + typdatum->primary = le32_to_cpu(buf[2]); + } key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) { char *key = NULL; struct user_datum *usrdatum; - int rc; - __le32 buf[2]; + int rc, to_read = 2; + __le32 buf[3]; u32 len; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); @@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) goto out; } - rc = next_entry(buf, fp, sizeof buf); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + to_read = 3; + + rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); if (rc < 0) goto bad; len = le32_to_cpu(buf[0]); usrdatum->value = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) + usrdatum->bounds = le32_to_cpu(buf[2]); key = kmalloc(len + 1, GFP_KERNEL); if (!key) { @@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) cat_read, }; +static int user_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct user_datum *upper, *user; + struct policydb *p = datap; + int depth = 0; + + upper = user = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: user %s: " + "too deep or looped boundary", + (char *) key); + return -EINVAL; + } + + upper = p->user_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&user->roles, node, bit) { + if (ebitmap_get_bit(&upper->roles, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "user=%s role=%s bounds=%s\n", + p->p_user_val_to_name[user->value - 1], + p->p_role_val_to_name[bit], + p->p_user_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int role_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct role_datum *upper, *role; + struct policydb *p = datap; + int depth = 0; + + upper = role = datum; + while (upper->bounds) { + struct ebitmap_node *node; + unsigned long bit; + + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: role %s: " + "too deep or looped bounds\n", + (char *) key); + return -EINVAL; + } + + upper = p->role_val_to_struct[upper->bounds - 1]; + ebitmap_for_each_positive_bit(&role->types, node, bit) { + if (ebitmap_get_bit(&upper->types, bit)) + continue; + + printk(KERN_ERR + "SELinux: boundary violated policy: " + "role=%s type=%s bounds=%s\n", + p->p_role_val_to_name[role->value - 1], + p->p_type_val_to_name[bit], + p->p_role_val_to_name[upper->value - 1]); + + return -EINVAL; + } + } + + return 0; +} + +static int type_bounds_sanity_check(void *key, void *datum, void *datap) +{ + struct type_datum *upper, *type; + struct policydb *p = datap; + int depth = 0; + + upper = type = datum; + while (upper->bounds) { + if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { + printk(KERN_ERR "SELinux: type %s: " + "too deep or looped boundary\n", + (char *) key); + return -EINVAL; + } + + upper = p->type_val_to_struct[upper->bounds - 1]; + if (upper->attribute) { + printk(KERN_ERR "SELinux: type %s: " + "bounded by attribute %s", + (char *) key, + p->p_type_val_to_name[upper->value - 1]); + return -EINVAL; + } + } + + return 0; +} + +static int policydb_bounds_sanity_check(struct policydb *p) +{ + int rc; + + if (p->policyvers < POLICYDB_VERSION_BOUNDARY) + return 0; + + rc = hashtab_map(p->p_users.table, + user_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_roles.table, + role_bounds_sanity_check, p); + if (rc) + return rc; + + rc = hashtab_map(p->p_types.table, + type_bounds_sanity_check, p); + if (rc) + return rc; + + return 0; +} + extern int ss_initialized; /* @@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp) goto bad; } + rc = policydb_bounds_sanity_check(p); + if (rc) + goto bad; + rc = 0; out: return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370fda6a..55152d498b53 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -61,6 +61,7 @@ struct class_datum { /* Role attributes */ struct role_datum { u32 value; /* internal role value */ + u32 bounds; /* boundary of role */ struct ebitmap dominates; /* set of roles dominated by this role */ struct ebitmap types; /* set of authorized types for role */ }; @@ -81,12 +82,15 @@ struct role_allow { /* Type attributes */ struct type_datum { u32 value; /* internal type value */ + u32 bounds; /* boundary of type */ unsigned char primary; /* primary name? */ + unsigned char attribute;/* attribute ?*/ }; /* User attributes */ struct user_datum { u32 value; /* internal user value */ + u32 bounds; /* bounds of user */ struct ebitmap roles; /* set of authorized roles for user */ struct mls_range range; /* MLS range (min - max) for user */ struct mls_level dfltlevel; /* default login MLS level for user */ @@ -209,6 +213,7 @@ struct policydb { struct class_datum **class_val_to_struct; struct role_datum **role_val_to_struct; struct user_datum **user_val_to_struct; + struct type_datum **type_val_to_struct; /* type enforcement access vectors and transitions */ struct avtab te_avtab; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 5a0536bddc63..4f233d9960e7 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -88,6 +88,11 @@ static u32 latest_granting; static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len); +static int context_struct_compute_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd); /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -274,6 +279,100 @@ mls_ops: } /* + * security_boundary_permission - drops violated permissions + * on boundary constraint. + */ +static void type_attribute_bounds_av(struct context *scontext, + struct context *tcontext, + u16 tclass, + u32 requested, + struct av_decision *avd) +{ + struct context lo_scontext; + struct context lo_tcontext; + struct av_decision lo_avd; + struct type_datum *source + = policydb.type_val_to_struct[scontext->type - 1]; + struct type_datum *target + = policydb.type_val_to_struct[tcontext->type - 1]; + u32 masked = 0; + + if (source->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); + lo_scontext.type = source->bounds; + + context_struct_compute_av(&lo_scontext, + tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); + lo_tcontext.type = target->bounds; + + context_struct_compute_av(scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (source->bounds && target->bounds) { + memset(&lo_avd, 0, sizeof(lo_avd)); + /* + * lo_scontext and lo_tcontext are already + * set up. + */ + + context_struct_compute_av(&lo_scontext, + &lo_tcontext, + tclass, + requested, + &lo_avd); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; + } + + if (masked) { + struct audit_buffer *ab; + char *stype_name + = policydb.p_type_val_to_name[source->value - 1]; + char *ttype_name + = policydb.p_type_val_to_name[target->value - 1]; + char *tclass_name + = policydb.p_class_val_to_name[tclass - 1]; + + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* notice to userspace via audit message */ + ab = audit_log_start(current->audit_context, + GFP_ATOMIC, AUDIT_SELINUX_ERR); + if (!ab) + return; + + audit_log_format(ab, "av boundary violation: " + "source=%s target=%s tclass=%s", + stype_name, ttype_name, tclass_name); + avc_dump_av(ab, tclass, masked); + audit_log_end(ab); + } +} + +/* * Compute access vectors based on a context structure pair for * the permissions in a particular class. */ @@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext, PROCESS__DYNTRANSITION); } + /* + * If the given source and target types have boundary + * constraint, lazy checks have to mask any violated + * permission and notice it to userspace via audit. + */ + type_attribute_bounds_av(scontext, tcontext, + tclass, requested, avd); + return 0; inval_class: @@ -549,6 +656,69 @@ out: return rc; } +/* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. + * It returns 0, if @newsid is bounded by @oldsid. + * Otherwise, it returns error code. + * + * @oldsid : current security identifier + * @newsid : destinated security identifier + */ +int security_bounded_transition(u32 old_sid, u32 new_sid) +{ + struct context *old_context, *new_context; + struct type_datum *type; + int index; + int rc = -EINVAL; + + read_lock(&policy_rwlock); + + old_context = sidtab_search(&sidtab, old_sid); + if (!old_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, old_sid); + goto out; + } + + new_context = sidtab_search(&sidtab, new_sid); + if (!new_context) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", + __func__, new_sid); + goto out; + } + + /* type/domain unchaned */ + if (old_context->type == new_context->type) { + rc = 0; + goto out; + } + + index = new_context->type; + while (true) { + type = policydb.type_val_to_struct[index - 1]; + BUG_ON(!type); + + /* not bounded anymore */ + if (!type->bounds) { + rc = -EPERM; + break; + } + + /* @newsid is bounded by @oldsid */ + if (type->bounds == old_context->type) { + rc = 0; + break; + } + index = type->bounds; + } +out: + read_unlock(&policy_rwlock); + + return rc; +} + + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier @@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol, *p++ = 0; typdatum = hashtab_search(pol->p_types.table, scontextp); - if (!typdatum) + if (!typdatum || typdatum->attribute) goto out; ctx->type = typdatum->value; |