Joshua Brindle wrote: > Stephen Smalley wrote: >> On Fri, 2008-07-25 at 22:03 +0900, KaiGai Kohei wrote: >>> [1/3] thread-context-kernel.1.patch >>> This patch enables to assign a thread a "weaker" hierarchical >>> domain, only if the destinated domain is a child of the current >>> domain. Hierachy relationships are defined in the policy version >>> 24. This patch also enables to read the new version of policy. >> >> If you are going to take type hierarchy support into the kernel, then >> it seems like it should be completely taken into the kernel, i.e. the >> hierarchy checking should be applied by the kernel rather than by the >> toolchain. That is what the Flask security server did for its >> extensible policy mechanism. >> > > If we are going to do this we also might as well implement > the explicit hierarchy support and get away from name based hierarchy. > Oops, I missed the patch that does this, disregard. >> And I think both the neverallow checking and the type hierarchy >> checking needs to move away from needing to do a full expansion in >> order to check; it is just too expensive these days. >> > > Do you think it will be faster to do attribute based lookups? > It will certainly use significantly less memory. > >>> >>> >>> Signed-off-by: KaiGai Kohei <kaigai@xxxxxxxxxxxxx> >>> ---- >>> security/selinux/hooks.c | 11 +++++- >>> security/selinux/include/security.h | 5 ++- >>> security/selinux/ss/policydb.c | 75 >> ++++++++++++++++++++++++++++++++-- >>> security/selinux/ss/policydb.h | 2 + >>> security/selinux/ss/services.c | 60 >>> ++++++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 7 >>> deletions(-) >>> >>> diff --git a/security/selinux/hooks.c >> b/security/selinux/hooks.c index >>> bc1c3ae..d4c1c5c 100644 >>> --- a/security/selinux/hooks.c >>> +++ b/security/selinux/hooks.c >>> @@ -5181,7 +5181,12 @@ static int >> selinux_setprocattr(struct task_struct *p, >>> if (sid == 0) >>> return -EINVAL; >>> >>> - /* Only allow single threaded processes to change context */ >>> + /* + * SELinux allows to change context in the following case >>> only. + * - Single threaded processes. >>> + * - Multi threaded processes intend to change its context into >>> + * lower or same domain in hierarchy relationship. + */ >>> if (atomic_read(&p->mm->mm_users) != 1) { >>> struct task_struct *g, *t; >>> struct mm_struct *mm = p->mm; >>> @@ -5189,11 +5194,15 @@ static int >> selinux_setprocattr(struct task_struct *p, >>> do_each_thread(g, t) { >>> if (t->mm == mm && t != p) { >>> read_unlock(&tasklist_lock); >>> + >>> + if >> (!security_check_hierarchy(tsec->sid, sid)) >>> + goto hierarchy_ok; >>> return -EPERM; >>> } >>> } while_each_thread(g, t); >>> read_unlock(&tasklist_lock); >>> } >>> +hierarchy_ok: >>> >>> /* Check permissions for the transition. */ >>> error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, diff --git >>> a/security/selinux/include/security.h >>> b/security/selinux/include/security.h >>> index ad30ac4..97fa9bb 100644 >>> --- a/security/selinux/include/security.h >>> +++ b/security/selinux/include/security.h >>> @@ -27,13 +27,14 @@ >>> #define POLICYDB_VERSION_RANGETRANS 21 >>> #define POLICYDB_VERSION_POLCAP 22 >>> #define POLICYDB_VERSION_PERMISSIVE 23 >>> +#define POLICYDB_VERSION_HIERARCHY 24 >>> >>> /* Range of policy versions we understand*/ >>> #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE >>> #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX >>> #define POLICYDB_VERSION_MAX >> CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE >>> #else >>> -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE >>> +#define POLICYDB_VERSION_MAX > POLICYDB_VERSION_HIERARCHY #endif >>> >>> #define CONTEXT_MNT 0x01 >>> @@ -118,6 +119,8 @@ int security_net_peersid_resolve(u32 nlbl_sid, >>> u32 nlbl_type, > u32 xfrm_sid, u32 *peer_sid); >>> >>> +int security_check_hierarchy(u32 old_sid, u32 new_sid); + >>> int security_get_classes(char ***classes, int *nclasses); int >>> security_get_permissions(char *class, char ***perms, int *nperms); >>> int security_get_reject_unknown(void); >>> diff --git a/security/selinux/ss/policydb.c >>> b/security/selinux/ss/policydb.c index 84f8cc7..e388f7a 100644 >>> --- a/security/selinux/ss/policydb.c >>> +++ b/security/selinux/ss/policydb.c >>> @@ -116,7 +116,12 @@ static struct policydb_compat_info >>> policydb_compat[] = { .version = POLICYDB_VERSION_PERMISSIVE, >>> .sym_num = SYM_NUM, .ocon_num = OCON_NUM, >>> - } >>> + }, >>> + { >>> + .version = POLICYDB_VERSION_HIERARCHY, >>> + .sym_num = SYM_NUM, >>> + .ocon_num = OCON_NUM, >>> + }, >>> }; >>> >>> static struct policydb_compat_info *policydb_lookup_compat(int >>> version) @@ -270,9 +275,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->parent > 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; >>> @@ -397,6 +405,46 @@ static void symtab_hash_eval(struct symtab *s) >>> } #endif >>> >>> +static int type_hierarchy_sanity_checks(struct policydb *p) { >>> + struct type_datum *type; + struct ebitmap e; >>> + int rc = 0, i; >>> + >>> + if (p->policyvers < POLICYDB_VERSION_HIERARCHY) >>> + return 0; >>> + >>> + for (i = 0; i < p->p_types.nprim; i++) { >>> + type = p->type_val_to_struct[i]; >>> + >>> + if (!type || !type->parent) >>> + continue; >>> + >>> + ebitmap_init(&e); >>> + while (type) { >>> + if (ebitmap_get_bit(&e, type->value - 1)) { >>> + printk(KERN_ERR "Hierarchy type >> looped at %s\n", >>> + >> p->p_type_val_to_name[type->value - 1]); >>> + rc = -EINVAL; >>> + break; >>> + } >>> + >>> + rc = ebitmap_set_bit(&e, type->value - 1, 1); >>> + if (rc) >>> + break; >>> + >>> + if (!type->parent) >>> + break; >>> + >>> + type = > p->type_val_to_struct[type->parent - 1]; + } >>> + ebitmap_destroy(&e); >>> + if (rc) >>> + return rc; >>> + } >>> + return 0; >>> +} >>> + >>> /* >>> * Define the other val_to_name and val_to_struct arrays >>> * in a policy database structure. >>> @@ -438,6 +486,14 @@ static int >> policydb_index_others(struct policydb *p) >>> goto out; >>> } >>> >>> + p->type_val_to_struct = >>> + kzalloc(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; >>> @@ -455,6 +511,7 @@ static int > policydb_index_others(struct policydb >>> *p) goto out; } >>> >>> + rc = type_hierarchy_sanity_checks(p); >>> out: >>> return rc; >>> } >>> @@ -625,6 +682,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); >>> >>> @@ -1236,8 +1294,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 to_read, rc; >>> + __le32 buf[4]; >>> u32 len; >>> >>> typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); @@ -1246,13 >>> +1304,20 @@ 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_HIERARCHY) >>> + to_read = sizeof(u32) * 3; >>> + else >>> + to_read = sizeof(u32) * 4; >>> + >>> + rc = next_entry(buf, fp, 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_HIERARCHY) >>> + typdatum->parent = le32_to_cpu(buf[3]); >>> >>> key = kmalloc(len + 1, GFP_KERNEL); >>> if (!key) { >>> diff --git a/security/selinux/ss/policydb.h >>> b/security/selinux/ss/policydb.h index 4253370..f3484d4 100644 >>> --- a/security/selinux/ss/policydb.h >>> +++ b/security/selinux/ss/policydb.h >>> @@ -81,6 +81,7 @@ struct role_allow { >>> /* Type attributes */ >>> struct type_datum { >>> u32 value; /* internal type value */ >>> + u32 parent; /* parent type, if hierarchy related */ >>> unsigned char primary; /* primary name? */ >>> }; >>> >>> @@ -209,6 +210,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 dcc2e1c..3ecd793 100644 >>> --- a/security/selinux/ss/services.c >>> +++ b/security/selinux/ss/services.c >>> @@ -2160,6 +2160,66 @@ out_slowpath: >>> return rc; >>> } >>> >>> +/* >>> + * security_check_hierarchy >>> + * >>> + * It returns 0, if @old_sid is same or upper type of @new_sid in >>> +hierarchy + * relationship directly/indirectly. 1 means @old_sid is >>> not a child +type + * of @new_sid, and negative returns are error. >>> + */ +int security_check_hierarchy(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; >>> + } >>> + >>> + /* domain unchanged */ >>> + if (old_context->type == new_context->type) { >>> + rc = 0; >>> + goto out; >>> + } >>> + >>> + /* check domain hierarchy */ >>> + index = new_context->type; >>> + while (true) { >>> + type = policydb.type_val_to_struct[index - 1]; >>> + if (!type) >>> + break; >>> + >>> + /* No hierarchy relationship */ >>> + if (type->parent == 0) { >>> + rc = 1; >>> + break; >>> + } >>> + if (type->parent == old_context->type) { >>> + rc = 0; >>> + break; >>> + } >>> + index = type->parent; >>> + } >>> +out: >>> + read_unlock(&policy_rwlock); >>> + >>> + return rc; >>> +} >>> + >>> static int get_classes_callback(void *k, void *d, void *args) { >>> struct class_datum *datum = d; -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with the words "unsubscribe selinux" without quotes as the message.