From 8023976cf4627d9f1d82ad468ec40e32eb87d211 Mon Sep 17 00:00:00 2001 From: Harry Ciao Date: Fri, 25 Mar 2011 13:51:56 +0800 Subject: SELinux: Add class support to the role_trans structure If kernel policy version is >= 26, then the binary representation of the role_trans structure supports specifying the class for the current subject or the newly created object. If kernel policy version is < 26, then the class field would be default to the process class. Signed-off-by: Harry Ciao Acked-by: Stephen Smalley Signed-off-by: Eric Paris --- security/selinux/ss/policydb.c | 14 ++++++++++++++ security/selinux/ss/policydb.h | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index e7b850ad57ee..fd62c50d6e7d 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -128,6 +128,11 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_ROLETRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -2302,8 +2307,17 @@ int policydb_read(struct policydb *p, void *fp) tr->role = le32_to_cpu(buf[0]); tr->type = le32_to_cpu(buf[1]); tr->new_role = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto bad; + tr->tclass = le32_to_cpu(buf[0]); + } else + tr->tclass = p->process_class; + if (!policydb_role_isvalid(p, tr->role) || !policydb_type_isvalid(p, tr->type) || + !policydb_class_isvalid(p, tr->tclass) || !policydb_role_isvalid(p, tr->new_role)) goto bad; ltr = tr; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 732ea4a68682..801175f79cf9 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -72,7 +72,8 @@ struct role_datum { struct role_trans { u32 role; /* current role */ - u32 type; /* program executable type */ + u32 type; /* program executable type, or new object type */ + u32 tclass; /* process class, or new object class */ u32 new_role; /* new role */ struct role_trans *next; }; -- cgit v1.2.3 From 63a312ca55d09a3f6526919df495fff1073c88f4 Mon Sep 17 00:00:00 2001 From: Harry Ciao Date: Fri, 25 Mar 2011 13:51:58 +0800 Subject: SELinux: Compute role in newcontext for all classes Apply role_transition rules for all kinds of classes. Signed-off-by: Harry Ciao Acked-by: Stephen Smalley Signed-off-by: Eric Paris --- security/selinux/ss/services.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 3e7544d2a07b..03f7a4748ee8 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1484,17 +1484,15 @@ static int security_compute_sid(u32 ssid, tcontext->type, tclass, qstr); /* Check for class-specific changes. */ - if (tclass == policydb.process_class) { - if (specified & AVTAB_TRANSITION) { - /* Look for a role transition rule. */ - for (roletr = policydb.role_tr; roletr; - roletr = roletr->next) { - if (roletr->role == scontext->role && - roletr->type == tcontext->type) { - /* Use the role transition rule. */ - newcontext.role = roletr->new_role; - break; - } + if (specified & AVTAB_TRANSITION) { + /* Look for a role transition rule. */ + for (roletr = policydb.role_tr; roletr; roletr = roletr->next) { + if ((roletr->role == scontext->role) && + (roletr->type == tcontext->type) && + (roletr->tclass == tclass)) { + /* Use the role transition rule. */ + newcontext.role = roletr->new_role; + break; } } } -- cgit v1.2.3 From c900ff323d761753a56d8d6a67b034ceee277b6e Mon Sep 17 00:00:00 2001 From: Harry Ciao Date: Fri, 25 Mar 2011 13:52:00 +0800 Subject: SELinux: Write class field in role_trans_write. If kernel policy version is >= 26, then write the class field of the role_trans structure into the binary reprensentation. Signed-off-by: Harry Ciao Acked-by: Stephen Smalley Signed-off-by: Eric Paris --- security/selinux/ss/policydb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index fd62c50d6e7d..a493eae24e0a 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -2535,8 +2535,9 @@ static int cat_write(void *vkey, void *datum, void *ptr) return 0; } -static int role_trans_write(struct role_trans *r, void *fp) +static int role_trans_write(struct policydb *p, void *fp) { + struct role_trans *r = p->role_tr; struct role_trans *tr; u32 buf[3]; size_t nel; @@ -2556,6 +2557,12 @@ static int role_trans_write(struct role_trans *r, void *fp) rc = put_entry(buf, sizeof(u32), 3, fp); if (rc) return rc; + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + buf[0] = cpu_to_le32(tr->tclass); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + } } return 0; @@ -3267,7 +3274,7 @@ int policydb_write(struct policydb *p, void *fp) if (rc) return rc; - rc = role_trans_write(p->role_tr, fp); + rc = role_trans_write(p, fp); if (rc) return rc; -- cgit v1.2.3 From f50a3ec961f90e38c0311411179d5dfee1412192 Mon Sep 17 00:00:00 2001 From: Kohei Kaigai Date: Fri, 1 Apr 2011 15:39:26 +0100 Subject: selinux: add type_transition with name extension support for selinuxfs The attached patch allows /selinux/create takes optional 4th argument to support TYPE_TRANSITION with name extension for userspace object managers. If 4th argument is not supplied, it shall perform as existing kernel. In fact, the regression test of SE-PostgreSQL works well on the patched kernel. Thanks, Signed-off-by: KaiGai Kohei [manually verify fuzz was not an issue, and it wasn't: eparis] Signed-off-by: Eric Paris --- security/selinux/include/security.h | 4 ++-- security/selinux/selinuxfs.c | 16 ++++++++++++++-- security/selinux/ss/services.c | 17 +++++++++-------- 3 files changed, 25 insertions(+), 12 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index bfc5218d5840..2cf670864147 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -112,8 +112,8 @@ void security_compute_av_user(u32 ssid, u32 tsid, int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, const struct qstr *qstr, u32 *out_sid); -int security_transition_sid_user(u32 ssid, u32 tsid, - u16 tclass, u32 *out_sid); +int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, + const char *objname, u32 *out_sid); int security_member_sid(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index ea39cb742ae5..973f5a4a6fce 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -753,11 +753,13 @@ out: static ssize_t sel_write_create(struct file *file, char *buf, size_t size) { char *scon = NULL, *tcon = NULL; + char *namebuf = NULL, *objname = NULL; u32 ssid, tsid, newsid; u16 tclass; ssize_t length; char *newcon = NULL; u32 len; + int nargs; length = task_has_security(current, SECURITY__COMPUTE_CREATE); if (length) @@ -773,9 +775,17 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) if (!tcon) goto out; + length = -ENOMEM; + namebuf = kzalloc(size + 1, GFP_KERNEL); + if (!namebuf) + goto out; + length = -EINVAL; - if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) + nargs = sscanf(buf, "%s %s %hu %s", scon, tcon, &tclass, namebuf); + if (nargs < 3 || nargs > 4) goto out; + if (nargs == 4) + objname = namebuf; length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); if (length) @@ -785,7 +795,8 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) if (length) goto out; - length = security_transition_sid_user(ssid, tsid, tclass, &newsid); + length = security_transition_sid_user(ssid, tsid, tclass, + objname, &newsid); if (length) goto out; @@ -804,6 +815,7 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) length = len; out: kfree(newcon); + kfree(namebuf); kfree(tcon); kfree(scon); return length; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 03f7a4748ee8..39d732145abe 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1360,14 +1360,14 @@ out: static void filename_compute_type(struct policydb *p, struct context *newcontext, u32 scon, u32 tcon, u16 tclass, - const struct qstr *qstr) + const char *objname) { struct filename_trans *ft; for (ft = p->filename_trans; ft; ft = ft->next) { if (ft->stype == scon && ft->ttype == tcon && ft->tclass == tclass && - !strcmp(ft->name, qstr->name)) { + !strcmp(ft->name, objname)) { newcontext->type = ft->otype; return; } @@ -1378,7 +1378,7 @@ static int security_compute_sid(u32 ssid, u32 tsid, u16 orig_tclass, u32 specified, - const struct qstr *qstr, + const char *objname, u32 *out_sid, bool kern) { @@ -1479,9 +1479,9 @@ static int security_compute_sid(u32 ssid, } /* if we have a qstr this is a file trans check so check those rules */ - if (qstr) + if (objname) filename_compute_type(&policydb, &newcontext, scontext->type, - tcontext->type, tclass, qstr); + tcontext->type, tclass, objname); /* Check for class-specific changes. */ if (specified & AVTAB_TRANSITION) { @@ -1539,13 +1539,14 @@ int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, const struct qstr *qstr, u32 *out_sid) { return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, - qstr, out_sid, true); + qstr ? qstr->name : NULL, out_sid, true); } -int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid) +int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, + const char *objname, u32 *out_sid) { return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, - NULL, out_sid, false); + objname, out_sid, false); } /** -- cgit v1.2.3 From eba71de2cb7c02c5ae4f2ad3656343da71bc4661 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Fri, 25 Mar 2011 10:13:43 -0400 Subject: selinux: Fix regression for Xorg Commit 6f5317e730505d5cbc851c435a2dfe3d5a21d343 introduced a bug in the handling of userspace object classes that is causing breakage for Xorg when XSELinux is enabled. Fix the bug by changing map_class() to return SECCLASS_NULL when the class cannot be mapped to a kernel object class. Reported-by: "Justin P. Mattock" Signed-off-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/services.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 39d732145abe..f3f5dca81006 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -213,7 +213,7 @@ static u16 map_class(u16 pol_value) return i; } - return pol_value; + return SECCLASS_NULL; } static void map_decision(u16 tclass, struct av_decision *avd, -- cgit v1.2.3 From 1214eac73f798bccabc6adb55e7b2d787527c13c Mon Sep 17 00:00:00 2001 From: Harry Ciao Date: Thu, 7 Apr 2011 14:12:57 +0800 Subject: Initialize policydb.process_class eariler. Initialize policydb.process_class once all symtabs read from policy image, so that it could be used to setup the role_trans.tclass field when a lower version policy.X is loaded. Signed-off-by: Harry Ciao Signed-off-by: Eric Paris --- security/selinux/ss/policydb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index a493eae24e0a..82373eb2dc97 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -2275,6 +2275,11 @@ int policydb_read(struct policydb *p, void *fp) p->symtab[i].nprim = nprim; } + rc = -EINVAL; + p->process_class = string_to_security_class(p, "process"); + if (!p->process_class) + goto bad; + rc = avtab_read(&p->te_avtab, fp, p); if (rc) goto bad; @@ -2358,11 +2363,6 @@ int policydb_read(struct policydb *p, void *fp) if (rc) goto bad; - rc = -EINVAL; - p->process_class = string_to_security_class(p, "process"); - if (!p->process_class) - goto bad; - rc = -EINVAL; p->process_trans_perms = string_to_av_perm(p, p->process_class, "transition"); p->process_trans_perms |= string_to_av_perm(p, p->process_class, "dyntransition"); -- cgit v1.2.3 From 425b473de5372cad6fffc6b98a758ed8e3fc70ce Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 7 Apr 2011 14:46:59 -0400 Subject: SELinux: delete debugging printks from filename_trans rule processing The filename_trans rule processing has some printk(KERN_ERR ) messages which were intended as debug aids in creating the code but weren't removed before it was submitted. Remove them. Signed-off-by: Eric Paris --- security/selinux/ss/policydb.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 82373eb2dc97..5591e422256a 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1824,8 +1824,6 @@ static int filename_trans_read(struct policydb *p, void *fp) goto out; nel = le32_to_cpu(buf[0]); - printk(KERN_ERR "%s: nel=%d\n", __func__, nel); - last = p->filename_trans; while (last && last->next) last = last->next; @@ -1862,8 +1860,6 @@ static int filename_trans_read(struct policydb *p, void *fp) goto out; name[len] = 0; - printk(KERN_ERR "%s: ft=%p ft->name=%p ft->name=%s\n", __func__, ft, ft->name, ft->name); - rc = next_entry(buf, fp, sizeof(u32) * 4); if (rc) goto out; -- cgit v1.2.3 From 6b697323a78bed254ee372f71b1a6a2901bb4b7a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 20 Apr 2011 10:21:28 -0400 Subject: SELinux: security_read_policy should take a size_t not ssize_t The len should be an size_t but is a ssize_t. Easy enough fix to silence build warnings. We have no need for signed-ness. Signed-off-by: Eric Paris Reviewed-by: James Morris --- security/selinux/include/security.h | 2 +- security/selinux/ss/services.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 2cf670864147..3ba4feba048a 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -86,7 +86,7 @@ extern int selinux_policycap_openperm; int security_mls_enabled(void); int security_load_policy(void *data, size_t len); -int security_read_policy(void **data, ssize_t *len); +int security_read_policy(void **data, size_t *len); size_t security_policydb_len(void); int security_policycap_supported(unsigned int req_cap); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index f3f5dca81006..211c0ada594c 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -3189,7 +3189,7 @@ out: * @len: length of data in bytes * */ -int security_read_policy(void **data, ssize_t *len) +int security_read_policy(void **data, size_t *len) { int rc; struct policy_file fp; -- cgit v1.2.3 From 4742600cf536c0c115b6f769eda82ee377d199c9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:11:20 -0400 Subject: SELinux: fix comment to state filename_compute_type takes an objname not a qstr filename_compute_type used to take a qstr, but it now takes just a name. Fix the comments to indicate it is an objname, not a qstr. Signed-off-by: Eric Paris --- security/selinux/ss/services.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 211c0ada594c..3e1ae85c0130 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1478,7 +1478,7 @@ static int security_compute_sid(u32 ssid, newcontext.type = avdatum->data; } - /* if we have a qstr this is a file trans check so check those rules */ + /* if we have a objname this is a file trans check so check those rules */ if (objname) filename_compute_type(&policydb, &newcontext, scontext->type, tcontext->type, tclass, objname); -- cgit v1.2.3 From 2667991f60e67d28c495b8967aaabf84b4ccd560 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:11:20 -0400 Subject: SELinux: rename filename_compute_type argument to *type instead of *con filename_compute_type() takes as arguments the numeric value of the type of the subject and target. It does not take a context. Thus the names are misleading. Fix the argument names. Signed-off-by: Eric Paris Reviewed-by: James Morris --- security/selinux/ss/services.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 3e1ae85c0130..78bb8100b02e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1359,13 +1359,13 @@ out: } static void filename_compute_type(struct policydb *p, struct context *newcontext, - u32 scon, u32 tcon, u16 tclass, + u32 stype, u32 ttype, u16 tclass, const char *objname) { struct filename_trans *ft; for (ft = p->filename_trans; ft; ft = ft->next) { - if (ft->stype == scon && - ft->ttype == tcon && + if (ft->stype == stype && + ft->ttype == ttype && ft->tclass == tclass && !strcmp(ft->name, objname)) { newcontext->type = ft->otype; -- cgit v1.2.3 From 03a4c0182a156547edd5f2717c1702590fe36bbf Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:11:21 -0400 Subject: SELinux: skip filename trans rules if ttype does not match parent dir Right now we walk to filename trans rule list for every inode that is created. First passes at policy using this facility creates around 5000 filename trans rules. Running a list of 5000 entries every time is a bad idea. This patch adds a new ebitmap to policy which has a bit set for each ttype that has at least 1 filename trans rule. Thus when an inode is created we can quickly determine if any rules exist for this parent directory type and can skip the list if we know there is definitely no relevant entry. Signed-off-by: Eric Paris Reviewed-by: James Morris --- security/selinux/ss/policydb.c | 6 ++++++ security/selinux/ss/policydb.h | 2 ++ security/selinux/ss/services.c | 9 +++++++++ 3 files changed, 17 insertions(+) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 5591e422256a..4c1811972b8b 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -240,6 +240,7 @@ static int policydb_init(struct policydb *p) if (!p->range_tr) goto out; + ebitmap_init(&p->filename_trans_ttypes); ebitmap_init(&p->policycaps); ebitmap_init(&p->permissive_map); @@ -801,6 +802,7 @@ void policydb_destroy(struct policydb *p) ft = nft; } + ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); @@ -1868,6 +1870,10 @@ static int filename_trans_read(struct policydb *p, void *fp) ft->ttype = le32_to_cpu(buf[1]); ft->tclass = le32_to_cpu(buf[2]); ft->otype = le32_to_cpu(buf[3]); + + rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1); + if (rc) + goto out; } rc = 0; out: diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 801175f79cf9..f054a9d4d114 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -227,6 +227,8 @@ struct policydb { /* role transitions */ struct role_trans *role_tr; + /* quickly exclude lookups when parent ttype has no rules */ + struct ebitmap filename_trans_ttypes; /* file transitions with the last path component */ struct filename_trans *filename_trans; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 78bb8100b02e..6a22eaebf3b7 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1363,6 +1363,15 @@ static void filename_compute_type(struct policydb *p, struct context *newcontext const char *objname) { struct filename_trans *ft; + + /* + * Most filename trans rules are going to live in specific directories + * like /dev or /var/run. This bitmap will quickly skip rule searches + * if the ttype does not contain any rules. + */ + if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype)) + return; + for (ft = p->filename_trans; ft; ft = ft->next) { if (ft->stype == stype && ft->ttype == ttype && -- cgit v1.2.3 From be30b16d43f4781406de0c08c96501dae4cc5a77 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:11:21 -0400 Subject: SELinux: calculate and print hashtab stats with a generic function We have custom debug functions like rangetr_hash_eval and symtab_hash_eval which do the same thing. Just create a generic function that takes the name of the hash table as an argument instead of having custom functions. Signed-off-by: Eric Paris Reviewed-by: James Morris --- security/selinux/ss/policydb.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 4c1811972b8b..70c863bf715e 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -423,32 +423,26 @@ static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) = }; #ifdef DEBUG_HASHES -static void symtab_hash_eval(struct symtab *s) +static void hash_eval(struct hashtab *h, const char *hash_name) { - int i; - - for (i = 0; i < SYM_NUM; i++) { - struct hashtab *h = s[i].table; - struct hashtab_info info; + struct hashtab_info info; - hashtab_stat(h, &info); - printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " - "longest chain length %d\n", symtab_name[i], h->nel, - info.slots_used, h->size, info.max_chain_len); - } + hashtab_stat(h, &info); + printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " + "longest chain length %d\n", hash_name, h->nel, + info.slots_used, h->size, info.max_chain_len); } -static void rangetr_hash_eval(struct hashtab *h) +static void symtab_hash_eval(struct symtab *s) { - struct hashtab_info info; + int i; - hashtab_stat(h, &info); - printk(KERN_DEBUG "SELinux: rangetr: %d entries and %d/%d buckets used, " - "longest chain length %d\n", h->nel, - info.slots_used, h->size, info.max_chain_len); + for (i = 0; i < SYM_NUM; i++) + hash_eval(s[i].table, symtab_name[i]); } + #else -static inline void rangetr_hash_eval(struct hashtab *h) +static inline void hash_eval(struct hashtab *h, char *hash_name) { } #endif @@ -1802,7 +1796,7 @@ static int range_read(struct policydb *p, void *fp) rt = NULL; r = NULL; } - rangetr_hash_eval(p->range_tr); + hash_eval(p->range_tr, "rangetr"); rc = 0; out: kfree(rt); -- cgit v1.2.3 From 3f058ef7787e1b48720622346de9a5317aeb749a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:11:21 -0400 Subject: SELinux: generic hashtab entry counter Instead of a hashtab entry counter function only useful for range transition rules make a function generic for any hashtable to use. Signed-off-by: Eric Paris Reviewed-by: James Morris --- security/selinux/ss/policydb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 70c863bf715e..ca7a7231b5a2 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -3066,7 +3066,7 @@ static int genfs_write(struct policydb *p, void *fp) return 0; } -static int range_count(void *key, void *data, void *ptr) +static int hashtab_cnt(void *key, void *data, void *ptr) { int *cnt = ptr; *cnt = *cnt + 1; @@ -3114,7 +3114,7 @@ static int range_write(struct policydb *p, void *fp) /* count the number of entries in the hashtab */ nel = 0; - rc = hashtab_map(p->range_tr, range_count, &nel); + rc = hashtab_map(p->range_tr, hashtab_cnt, &nel); if (rc) return rc; -- cgit v1.2.3 From 2463c26d50adc282d19317013ba0ff473823ca47 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:11:21 -0400 Subject: SELinux: put name based create rules in a hashtable To shorten the list we need to run if filename trans rules exist for the type of the given parent directory I put them in a hashtable. Given the policy we are expecting to use in Fedora this takes the worst case list run from about 5,000 entries to 17. Signed-off-by: Eric Paris Reviewed-by: James Morris --- security/selinux/ss/policydb.c | 167 +++++++++++++++++++++++++++++------------ security/selinux/ss/policydb.h | 9 ++- security/selinux/ss/services.c | 20 ++--- 3 files changed, 135 insertions(+), 61 deletions(-) (limited to 'security/selinux/ss') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index ca7a7231b5a2..549120c56edd 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -184,6 +184,43 @@ out: return rc; } +static u32 filenametr_hash(struct hashtab *h, const void *k) +{ + const struct filename_trans *ft = k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; + + hash = ft->stype ^ ft->ttype ^ ft->tclass; + + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash & (h->size - 1); +} + +static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ + const struct filename_trans *ft1 = k1; + const struct filename_trans *ft2 = k2; + int v; + + v = ft1->stype - ft2->stype; + if (v) + return v; + + v = ft1->ttype - ft2->ttype; + if (v) + return v; + + v = ft1->tclass - ft2->tclass; + if (v) + return v; + + return strcmp(ft1->name, ft2->name); + +} + static u32 rangetr_hash(struct hashtab *h, const void *k) { const struct range_trans *key = k; @@ -236,6 +273,10 @@ static int policydb_init(struct policydb *p) if (rc) goto out; + p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10)); + if (!p->filename_trans) + goto out; + p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); if (!p->range_tr) goto out; @@ -246,6 +287,8 @@ static int policydb_init(struct policydb *p) return 0; out: + hashtab_destroy(p->filename_trans); + hashtab_destroy(p->range_tr); for (i = 0; i < SYM_NUM; i++) hashtab_destroy(p->symtab[i].table); return rc; @@ -675,6 +718,16 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = cat_destroy, }; +static int filenametr_destroy(void *key, void *datum, void *p) +{ + struct filename_trans *ft = key; + kfree(ft->name); + kfree(key); + kfree(datum); + cond_resched(); + return 0; +} + static int range_tr_destroy(void *key, void *datum, void *p) { struct mls_range *rt = datum; @@ -709,7 +762,6 @@ void policydb_destroy(struct policydb *p) int i; struct role_allow *ra, *lra = NULL; struct role_trans *tr, *ltr = NULL; - struct filename_trans *ft, *nft; for (i = 0; i < SYM_NUM; i++) { cond_resched(); @@ -773,6 +825,9 @@ void policydb_destroy(struct policydb *p) } kfree(lra); + hashtab_map(p->filename_trans, filenametr_destroy, NULL); + hashtab_destroy(p->filename_trans); + hashtab_map(p->range_tr, range_tr_destroy, NULL); hashtab_destroy(p->range_tr); @@ -788,14 +843,6 @@ void policydb_destroy(struct policydb *p) flex_array_free(p->type_attr_map_array); } - ft = p->filename_trans; - while (ft) { - nft = ft->next; - kfree(ft->name); - kfree(ft); - ft = nft; - } - ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); @@ -1806,9 +1853,10 @@ out: static int filename_trans_read(struct policydb *p, void *fp) { - struct filename_trans *ft, *last; - u32 nel, len; + struct filename_trans *ft; + struct filename_trans_datum *otype; char *name; + u32 nel, len; __le32 buf[4]; int rc, i; @@ -1817,25 +1865,23 @@ static int filename_trans_read(struct policydb *p, void *fp) rc = next_entry(buf, fp, sizeof(u32)); if (rc) - goto out; + return rc; nel = le32_to_cpu(buf[0]); - last = p->filename_trans; - while (last && last->next) - last = last->next; - for (i = 0; i < nel; i++) { + ft = NULL; + otype = NULL; + name = NULL; + rc = -ENOMEM; ft = kzalloc(sizeof(*ft), GFP_KERNEL); if (!ft) goto out; - /* add it to the tail of the list */ - if (!last) - p->filename_trans = ft; - else - last->next = ft; - last = ft; + rc = -ENOMEM; + otype = kmalloc(sizeof(*otype), GFP_KERNEL); + if (!otype) + goto out; /* length of the path component string */ rc = next_entry(buf, fp, sizeof(u32)); @@ -1863,14 +1909,22 @@ static int filename_trans_read(struct policydb *p, void *fp) ft->stype = le32_to_cpu(buf[0]); ft->ttype = le32_to_cpu(buf[1]); ft->tclass = le32_to_cpu(buf[2]); - ft->otype = le32_to_cpu(buf[3]); + + otype->otype = le32_to_cpu(buf[3]); rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1); if (rc) goto out; + + hashtab_insert(p->filename_trans, ft, otype); } - rc = 0; + hash_eval(p->filename_trans, "filenametr"); + return 0; out: + kfree(ft); + kfree(name); + kfree(otype); + return rc; } @@ -3131,43 +3185,60 @@ static int range_write(struct policydb *p, void *fp) return 0; } -static int filename_trans_write(struct policydb *p, void *fp) +static int filename_write_helper(void *key, void *data, void *ptr) { - struct filename_trans *ft; - u32 len, nel = 0; __le32 buf[4]; + struct filename_trans *ft = key; + struct filename_trans_datum *otype = data; + void *fp = ptr; int rc; + u32 len; - for (ft = p->filename_trans; ft; ft = ft->next) - nel++; - - buf[0] = cpu_to_le32(nel); + len = strlen(ft->name); + buf[0] = cpu_to_le32(len); rc = put_entry(buf, sizeof(u32), 1, fp); if (rc) return rc; - for (ft = p->filename_trans; ft; ft = ft->next) { - len = strlen(ft->name); - buf[0] = cpu_to_le32(len); - rc = put_entry(buf, sizeof(u32), 1, fp); - if (rc) - return rc; + rc = put_entry(ft->name, sizeof(char), len, fp); + if (rc) + return rc; - rc = put_entry(ft->name, sizeof(char), len, fp); - if (rc) - return rc; + buf[0] = ft->stype; + buf[1] = ft->ttype; + buf[2] = ft->tclass; + buf[3] = otype->otype; - buf[0] = ft->stype; - buf[1] = ft->ttype; - buf[2] = ft->tclass; - buf[3] = ft->otype; + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; - rc = put_entry(buf, sizeof(u32), 4, fp); - if (rc) - return rc; - } return 0; } + +static int filename_trans_write(struct policydb *p, void *fp) +{ + u32 nel; + __le32 buf[1]; + int rc; + + nel = 0; + rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel); + if (rc) + return rc; + + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = hashtab_map(p->filename_trans, filename_write_helper, fp); + if (rc) + return rc; + + return 0; +} + /* * Write the configuration data in a policy database * structure to a policy database binary representation diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index f054a9d4d114..b846c0387180 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -79,11 +79,13 @@ struct role_trans { }; struct filename_trans { - struct filename_trans *next; u32 stype; /* current process */ u32 ttype; /* parent dir context */ u16 tclass; /* class of new object */ const char *name; /* last path component */ +}; + +struct filename_trans_datum { u32 otype; /* expected of new object */ }; @@ -227,10 +229,11 @@ struct policydb { /* role transitions */ struct role_trans *role_tr; + /* file transitions with the last path component */ /* quickly exclude lookups when parent ttype has no rules */ struct ebitmap filename_trans_ttypes; - /* file transitions with the last path component */ - struct filename_trans *filename_trans; + /* actual set of filename_trans rules */ + struct hashtab *filename_trans; /* bools indexed by (value - 1) */ struct cond_bool_datum **bool_val_to_struct; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 6a22eaebf3b7..e11b4b038f4a 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1362,7 +1362,8 @@ static void filename_compute_type(struct policydb *p, struct context *newcontext u32 stype, u32 ttype, u16 tclass, const char *objname) { - struct filename_trans *ft; + struct filename_trans ft; + struct filename_trans_datum *otype; /* * Most filename trans rules are going to live in specific directories @@ -1372,15 +1373,14 @@ static void filename_compute_type(struct policydb *p, struct context *newcontext if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype)) return; - for (ft = p->filename_trans; ft; ft = ft->next) { - if (ft->stype == stype && - ft->ttype == ttype && - ft->tclass == tclass && - !strcmp(ft->name, objname)) { - newcontext->type = ft->otype; - return; - } - } + ft.stype = stype; + ft.ttype = ttype; + ft.tclass = tclass; + ft.name = objname; + + otype = hashtab_search(p->filename_trans, &ft); + if (otype) + newcontext->type = otype->otype; } static int security_compute_sid(u32 ssid, -- cgit v1.2.3 From 5a3ea8782c63d3501cb764c176f153c0d9a400e1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:55:52 -0400 Subject: flex_array: flex_array_prealloc takes a number of elements, not an end Change flex_array_prealloc to take the number of elements for which space should be allocated instead of the last (inclusive) element. Users and documentation are updated accordingly. flex_arrays got introduced before they had users. When folks started using it, they ended up needing a different API than was coded up originally. This swaps over to the API that folks apparently need. Based-on-patch-by: Steffen Klassert Signed-off-by: Eric Paris Tested-by: Chris Richards Acked-by: Dave Hansen Cc: stable@kernel.org [2.6.38+] --- Documentation/flexible-arrays.txt | 4 ++-- include/linux/flex_array.h | 2 +- lib/flex_array.c | 13 ++++++++----- security/selinux/ss/policydb.c | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) (limited to 'security/selinux/ss') diff --git a/Documentation/flexible-arrays.txt b/Documentation/flexible-arrays.txt index cb8a3a00cc92..df904aec9904 100644 --- a/Documentation/flexible-arrays.txt +++ b/Documentation/flexible-arrays.txt @@ -66,10 +66,10 @@ trick is to ensure that any needed memory allocations are done before entering atomic context, using: int flex_array_prealloc(struct flex_array *array, unsigned int start, - unsigned int end, gfp_t flags); + unsigned int nr_elements, gfp_t flags); This function will ensure that memory for the elements indexed in the range -defined by start and end has been allocated. Thereafter, a +defined by start and nr_elements has been allocated. Thereafter, a flex_array_put() call on an element in that range is guaranteed not to block. diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h index 70e4efabe0fb..ebeb2f3ad068 100644 --- a/include/linux/flex_array.h +++ b/include/linux/flex_array.h @@ -61,7 +61,7 @@ struct flex_array { struct flex_array *flex_array_alloc(int element_size, unsigned int total, gfp_t flags); int flex_array_prealloc(struct flex_array *fa, unsigned int start, - unsigned int end, gfp_t flags); + unsigned int nr_elements, gfp_t flags); void flex_array_free(struct flex_array *fa); void flex_array_free_parts(struct flex_array *fa); int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src, diff --git a/lib/flex_array.c b/lib/flex_array.c index c0ea40ba2082..0c33b24498ba 100644 --- a/lib/flex_array.c +++ b/lib/flex_array.c @@ -232,10 +232,10 @@ EXPORT_SYMBOL(flex_array_clear); /** * flex_array_prealloc - guarantee that array space exists - * @fa: the flex array for which to preallocate parts - * @start: index of first array element for which space is allocated - * @end: index of last (inclusive) element for which space is allocated - * @flags: page allocation flags + * @fa: the flex array for which to preallocate parts + * @start: index of first array element for which space is allocated + * @nr_elements: number of elements for which space is allocated + * @flags: page allocation flags * * This will guarantee that no future calls to flex_array_put() * will allocate memory. It can be used if you are expecting to @@ -245,13 +245,16 @@ EXPORT_SYMBOL(flex_array_clear); * Locking must be provided by the caller. */ int flex_array_prealloc(struct flex_array *fa, unsigned int start, - unsigned int end, gfp_t flags) + unsigned int nr_elements, gfp_t flags) { int start_part; int end_part; int part_nr; + unsigned int end; struct flex_array_part *part; + end = start + nr_elements - 1; + if (start >= fa->total_nr_elements || end >= fa->total_nr_elements) return -ENOSPC; if (elements_fit_in_base(fa)) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 549120c56edd..102e9ec1b77a 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -545,7 +545,7 @@ static int policydb_index(struct policydb *p) goto out; rc = flex_array_prealloc(p->type_val_to_struct_array, 0, - p->p_types.nprim - 1, GFP_KERNEL | __GFP_ZERO); + p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto out; @@ -562,7 +562,7 @@ static int policydb_index(struct policydb *p) goto out; rc = flex_array_prealloc(p->sym_val_to_name[i], - 0, p->symtab[i].nprim - 1, + 0, p->symtab[i].nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto out; @@ -2439,7 +2439,7 @@ int policydb_read(struct policydb *p, void *fp) goto bad; /* preallocate so we don't have to worry about the put ever failing */ - rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1, + rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto bad; -- cgit v1.2.3