From 55fcf09b3fe4325c9395ebbb0322a547a157ebc7 Mon Sep 17 00:00:00 2001 From: "Christopher J. PeBenito" Date: Wed, 23 May 2007 09:12:06 -0400 Subject: selinux: add support for querying object classes and permissions from the running policy Add support to the SELinux security server for obtaining a list of classes, and for obtaining a list of permissions for a specified class. Signed-off-by: Christopher J. PeBenito Signed-off-by: James Morris --- security/selinux/ss/services.c | 95 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'security/selinux/ss/services.c') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 40660ffd49b6..e4249adaa880 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1996,6 +1996,101 @@ out: return rc; } +static int get_classes_callback(void *k, void *d, void *args) +{ + struct class_datum *datum = d; + char *name = k, **classes = args; + int value = datum->value - 1; + + classes[value] = kstrdup(name, GFP_ATOMIC); + if (!classes[value]) + return -ENOMEM; + + return 0; +} + +int security_get_classes(char ***classes, int *nclasses) +{ + int rc = -ENOMEM; + + POLICY_RDLOCK; + + *nclasses = policydb.p_classes.nprim; + *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC); + if (!*classes) + goto out; + + rc = hashtab_map(policydb.p_classes.table, get_classes_callback, + *classes); + if (rc < 0) { + int i; + for (i = 0; i < *nclasses; i++) + kfree((*classes)[i]); + kfree(*classes); + } + +out: + POLICY_RDUNLOCK; + return rc; +} + +static int get_permissions_callback(void *k, void *d, void *args) +{ + struct perm_datum *datum = d; + char *name = k, **perms = args; + int value = datum->value - 1; + + perms[value] = kstrdup(name, GFP_ATOMIC); + if (!perms[value]) + return -ENOMEM; + + return 0; +} + +int security_get_permissions(char *class, char ***perms, int *nperms) +{ + int rc = -ENOMEM, i; + struct class_datum *match; + + POLICY_RDLOCK; + + match = hashtab_search(policydb.p_classes.table, class); + if (!match) { + printk(KERN_ERR "%s: unrecognized class %s\n", + __FUNCTION__, class); + rc = -EINVAL; + goto out; + } + + *nperms = match->permissions.nprim; + *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC); + if (!*perms) + goto out; + + if (match->comdatum) { + rc = hashtab_map(match->comdatum->permissions.table, + get_permissions_callback, *perms); + if (rc < 0) + goto err; + } + + rc = hashtab_map(match->permissions.table, get_permissions_callback, + *perms); + if (rc < 0) + goto err; + +out: + POLICY_RDUNLOCK; + return rc; + +err: + POLICY_RDUNLOCK; + for (i = 0; i < *nperms; i++) + kfree((*perms)[i]); + kfree(*perms); + return rc; +} + struct selinux_audit_rule { u32 au_seqno; struct context au_ctxt; -- cgit v1.2.3 From 2c3c05dbcbc7b9d71549fe0e2b249f10f5a66518 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Thu, 7 Jun 2007 15:34:10 -0400 Subject: SELinux: allow preemption between transition permission checks In security_get_user_sids, move the transition permission checks outside of the section holding the policy rdlock, and use the AVC to perform the checks, calling cond_resched after each one. These changes should allow preemption between the individual checks and enable caching of the results. It may however increase the overall time spent in the function in some cases, particularly in the cache miss case. The long term fix will be to take much of this logic to userspace by exporting additional state via selinuxfs, and ultimately deprecating and eliminating this interface from the kernel. Tested-by: Ingo Molnar Signed-off-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/services.c | 49 ++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'security/selinux/ss/services.c') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index e4249adaa880..b5f017f07a75 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1587,19 +1587,18 @@ int security_get_user_sids(u32 fromsid, u32 *nel) { struct context *fromcon, usercon; - u32 *mysids, *mysids2, sid; + u32 *mysids = NULL, *mysids2, sid; u32 mynel = 0, maxnel = SIDS_NEL; struct user_datum *user; struct role_datum *role; - struct av_decision avd; struct ebitmap_node *rnode, *tnode; int rc = 0, i, j; - if (!ss_initialized) { - *sids = NULL; - *nel = 0; + *sids = NULL; + *nel = 0; + + if (!ss_initialized) goto out; - } POLICY_RDLOCK; @@ -1635,17 +1634,9 @@ int security_get_user_sids(u32 fromsid, if (mls_setup_user_range(fromcon, user, &usercon)) continue; - rc = context_struct_compute_av(fromcon, &usercon, - SECCLASS_PROCESS, - PROCESS__TRANSITION, - &avd); - if (rc || !(avd.allowed & PROCESS__TRANSITION)) - continue; rc = sidtab_context_to_sid(&sidtab, &usercon, &sid); - if (rc) { - kfree(mysids); + if (rc) goto out_unlock; - } if (mynel < maxnel) { mysids[mynel++] = sid; } else { @@ -1653,7 +1644,6 @@ int security_get_user_sids(u32 fromsid, mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC); if (!mysids2) { rc = -ENOMEM; - kfree(mysids); goto out_unlock; } memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); @@ -1664,11 +1654,32 @@ int security_get_user_sids(u32 fromsid, } } - *sids = mysids; - *nel = mynel; - out_unlock: POLICY_RDUNLOCK; + if (rc || !mynel) { + kfree(mysids); + goto out; + } + + mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); + if (!mysids2) { + rc = -ENOMEM; + kfree(mysids); + goto out; + } + for (i = 0, j = 0; i < mynel; i++) { + rc = avc_has_perm_noaudit(fromsid, mysids[i], + SECCLASS_PROCESS, + PROCESS__TRANSITION, AVC_STRICT, + NULL); + if (!rc) + mysids2[j++] = mysids[i]; + cond_resched(); + } + rc = 0; + kfree(mysids); + *sids = mysids2; + *nel = j; out: return rc; } -- cgit v1.2.3