From c6d3aaa4e35c71a32a86ececacd4eea7ecfc316c Mon Sep 17 00:00:00 2001 From: Stephen Smalley <sds@tycho.nsa.gov> Date: Wed, 30 Sep 2009 13:37:50 -0400 Subject: selinux: dynamic class/perm discovery Modify SELinux to dynamically discover class and permission values upon policy load, based on the dynamic object class/perm discovery logic from libselinux. A mapping is created between kernel-private class and permission indices used outside the security server and the policy values used within the security server. The mappings are only applied upon kernel-internal computations; similar mappings for the private indices of userspace object managers is handled on a per-object manager basis by the userspace AVC. The interfaces for compute_av and transition_sid are split for kernel vs. userspace; the userspace functions are distinguished by a _user suffix. The kernel-private class indices are no longer tied to the policy values and thus do not need to skip indices for userspace classes; thus the kernel class index values are compressed. The flask.h definitions were regenerated by deleting the userspace classes from refpolicy's definitions and then regenerating the headers. Going forward, we can just maintain the flask.h, av_permissions.h, and classmap.h definitions separately from policy as they are no longer tied to the policy values. The next patch introduces a utility to automate generation of flask.h and av_permissions.h from the classmap.h definitions. The older kernel class and permission string tables are removed and replaced by a single security class mapping table that is walked at policy load to generate the mapping. The old kernel class validation logic is completely replaced by the mapping logic. The handle unknown logic is reworked. reject_unknown=1 is handled when the mappings are computed at policy load time, similar to the old handling by the class validation logic. allow_unknown=1 is handled when computing and mapping decisions - if the permission was not able to be mapped (i.e. undefined, mapped to zero), then it is automatically added to the allowed vector. If the class was not able to be mapped (i.e. undefined, mapped to zero), then all permissions are allowed for it if allow_unknown=1. avc_audit leverages the new security class mapping table to lookup the class and permission names from the kernel-private indices. The mdp program is updated to use the new table when generating the class definitions and allow rules for a minimal boot policy for the kernel. It should be noted that this policy will not include any userspace classes, nor will its policy index values for the kernel classes correspond with the ones in refpolicy (they will instead match the kernel-private indices). Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: James Morris <jmorris@namei.org> --- security/selinux/ss/services.c | 540 ++++++++++++++++++++++------------------- 1 file changed, 287 insertions(+), 253 deletions(-) (limited to 'security/selinux/ss/services.c') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ff17820d35e..e19baa81fde 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -70,11 +70,6 @@ unsigned int policydb_loaded_version; int selinux_policycap_netpeer; int selinux_policycap_openperm; -/* - * This is declared in avc.c - */ -extern const struct selinux_class_perm selinux_class_perm; - static DEFINE_RWLOCK(policy_rwlock); static struct sidtab sidtab; @@ -98,6 +93,158 @@ static int context_struct_compute_av(struct context *scontext, u16 tclass, u32 requested, struct av_decision *avd); + +struct selinux_mapping { + u16 value; /* policy value */ + unsigned num_perms; + u32 perms[sizeof(u32) * 8]; +}; + +static struct selinux_mapping *current_mapping; +static u16 current_mapping_size; + +static int selinux_set_mapping(struct policydb *pol, + struct security_class_mapping *map, + struct selinux_mapping **out_map_p, + u16 *out_map_size) +{ + struct selinux_mapping *out_map = NULL; + size_t size = sizeof(struct selinux_mapping); + u16 i, j; + unsigned k; + bool print_unknown_handle = false; + + /* Find number of classes in the input mapping */ + if (!map) + return -EINVAL; + i = 0; + while (map[i].name) + i++; + + /* Allocate space for the class records, plus one for class zero */ + out_map = kcalloc(++i, size, GFP_ATOMIC); + if (!out_map) + return -ENOMEM; + + /* Store the raw class and permission values */ + j = 0; + while (map[j].name) { + struct security_class_mapping *p_in = map + (j++); + struct selinux_mapping *p_out = out_map + j; + + /* An empty class string skips ahead */ + if (!strcmp(p_in->name, "")) { + p_out->num_perms = 0; + continue; + } + + p_out->value = string_to_security_class(pol, p_in->name); + if (!p_out->value) { + printk(KERN_INFO + "SELinux: Class %s not defined in policy.\n", + p_in->name); + if (pol->reject_unknown) + goto err; + p_out->num_perms = 0; + print_unknown_handle = true; + continue; + } + + k = 0; + while (p_in->perms && p_in->perms[k]) { + /* An empty permission string skips ahead */ + if (!*p_in->perms[k]) { + k++; + continue; + } + p_out->perms[k] = string_to_av_perm(pol, p_out->value, + p_in->perms[k]); + if (!p_out->perms[k]) { + printk(KERN_INFO + "SELinux: Permission %s in class %s not defined in policy.\n", + p_in->perms[k], p_in->name); + if (pol->reject_unknown) + goto err; + print_unknown_handle = true; + } + + k++; + } + p_out->num_perms = k; + } + + if (print_unknown_handle) + printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n", + pol->allow_unknown ? "allowed" : "denied"); + + *out_map_p = out_map; + *out_map_size = i; + return 0; +err: + kfree(out_map); + return -EINVAL; +} + +/* + * Get real, policy values from mapped values + */ + +static u16 unmap_class(u16 tclass) +{ + if (tclass < current_mapping_size) + return current_mapping[tclass].value; + + return tclass; +} + +static u32 unmap_perm(u16 tclass, u32 tperm) +{ + if (tclass < current_mapping_size) { + unsigned i; + u32 kperm = 0; + + for (i = 0; i < current_mapping[tclass].num_perms; i++) + if (tperm & (1<<i)) { + kperm |= current_mapping[tclass].perms[i]; + tperm &= ~(1<<i); + } + return kperm; + } + + return tperm; +} + +static void map_decision(u16 tclass, struct av_decision *avd, + int allow_unknown) +{ + if (tclass < current_mapping_size) { + unsigned i, n = current_mapping[tclass].num_perms; + u32 result; + + for (i = 0, result = 0; i < n; i++) { + if (avd->allowed & current_mapping[tclass].perms[i]) + result |= 1<<i; + if (allow_unknown && !current_mapping[tclass].perms[i]) + result |= 1<<i; + } + avd->allowed = result; + + for (i = 0, result = 0; i < n; i++) + if (avd->auditallow & current_mapping[tclass].perms[i]) + result |= 1<<i; + avd->auditallow = result; + + for (i = 0, result = 0; i < n; i++) { + if (avd->auditdeny & current_mapping[tclass].perms[i]) + result |= 1<<i; + if (!allow_unknown && !current_mapping[tclass].perms[i]) + result |= 1<<i; + } + avd->auditdeny = result; + } +} + + /* * Return the boolean value of a constraint expression * when it is applied to the specified source and target @@ -467,7 +614,6 @@ static int context_struct_compute_av(struct context *scontext, struct class_datum *tclass_datum; struct ebitmap *sattr, *tattr; struct ebitmap_node *snode, *tnode; - const struct selinux_class_perm *kdefs = &selinux_class_perm; unsigned int i, j; /* @@ -477,9 +623,9 @@ static int context_struct_compute_av(struct context *scontext, * to remain in the correct class. */ if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && - tclass <= SECCLASS_NETLINK_DNRT_SOCKET) - tclass = SECCLASS_NETLINK_SOCKET; + if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) && + tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET)) + tclass = unmap_class(SECCLASS_NETLINK_SOCKET); /* * Initialize the access vectors to the default values. @@ -490,33 +636,11 @@ static int context_struct_compute_av(struct context *scontext, avd->seqno = latest_granting; avd->flags = 0; - /* - * Check for all the invalid cases. - * - tclass 0 - * - tclass > policy and > kernel - * - tclass > policy but is a userspace class - * - tclass > policy but we do not allow unknowns - */ - if (unlikely(!tclass)) - goto inval_class; - if (unlikely(tclass > policydb.p_classes.nprim)) - if (tclass > kdefs->cts_len || - !kdefs->class_to_string[tclass] || - !policydb.allow_unknown) - goto inval_class; - - /* - * Kernel class and we allow unknown so pad the allow decision - * the pad will be all 1 for unknown classes. - */ - if (tclass <= kdefs->cts_len && policydb.allow_unknown) - avd->allowed = policydb.undefined_perms[tclass - 1]; - - /* - * Not in policy. Since decision is completed (all 1 or all 0) return. - */ - if (unlikely(tclass > policydb.p_classes.nprim)) - return 0; + if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { + if (printk_ratelimit()) + printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); + return -EINVAL; + } tclass_datum = policydb.class_val_to_struct[tclass - 1]; @@ -568,8 +692,8 @@ static int context_struct_compute_av(struct context *scontext, * role is changing, then check the (current_role, new_role) * pair. */ - if (tclass == SECCLASS_PROCESS && - (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) && + if (tclass == policydb.process_class && + (avd->allowed & policydb.process_trans_perms) && scontext->role != tcontext->role) { for (ra = policydb.role_allow; ra; ra = ra->next) { if (scontext->role == ra->role && @@ -577,8 +701,7 @@ static int context_struct_compute_av(struct context *scontext, break; } if (!ra) - avd->allowed &= ~(PROCESS__TRANSITION | - PROCESS__DYNTRANSITION); + avd->allowed &= ~policydb.process_trans_perms; } /* @@ -590,21 +713,6 @@ static int context_struct_compute_av(struct context *scontext, tclass, requested, avd); return 0; - -inval_class: - if (!tclass || tclass > kdefs->cts_len || - !kdefs->class_to_string[tclass]) { - if (printk_ratelimit()) - printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", - __func__, tclass); - return -EINVAL; - } - - /* - * Known to the kernel, but not to the policy. - * Handle as a denial (allowed is 0). - */ - return 0; } static int security_validtrans_handle_fail(struct context *ocontext, @@ -636,13 +744,14 @@ out: } int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, - u16 tclass) + u16 orig_tclass) { struct context *ocontext; struct context *ncontext; struct context *tcontext; struct class_datum *tclass_datum; struct constraint_node *constraint; + u16 tclass; int rc = 0; if (!ss_initialized) @@ -650,6 +759,8 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, read_lock(&policy_rwlock); + tclass = unmap_class(orig_tclass); + /* * Remap extended Netlink classes for old policy versions. * Do this here rather than socket_type_to_security_class() @@ -657,9 +768,9 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, * to remain in the correct class. */ if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && - tclass <= SECCLASS_NETLINK_DNRT_SOCKET) - tclass = SECCLASS_NETLINK_SOCKET; + if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) && + tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET)) + tclass = unmap_class(SECCLASS_NETLINK_SOCKET); if (!tclass || tclass > policydb.p_classes.nprim) { printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", @@ -792,6 +903,38 @@ out: } +static int security_compute_av_core(u32 ssid, + u32 tsid, + u16 tclass, + u32 requested, + struct av_decision *avd) +{ + struct context *scontext = NULL, *tcontext = NULL; + int rc = 0; + + scontext = sidtab_search(&sidtab, ssid); + if (!scontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, ssid); + return -EINVAL; + } + tcontext = sidtab_search(&sidtab, tsid); + if (!tcontext) { + printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", + __func__, tsid); + return -EINVAL; + } + + rc = context_struct_compute_av(scontext, tcontext, tclass, + requested, avd); + + /* permissive domain? */ + if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) + avd->flags |= AVD_FLAGS_PERMISSIVE; + + return rc; +} + /** * security_compute_av - Compute access vector decisions. * @ssid: source security identifier @@ -807,12 +950,45 @@ out: */ int security_compute_av(u32 ssid, u32 tsid, - u16 tclass, - u32 requested, + u16 orig_tclass, + u32 orig_requested, struct av_decision *avd) { - struct context *scontext = NULL, *tcontext = NULL; - int rc = 0; + u16 tclass; + u32 requested; + int rc; + + if (!ss_initialized) + goto allow; + + read_lock(&policy_rwlock); + requested = unmap_perm(orig_tclass, orig_requested); + tclass = unmap_class(orig_tclass); + if (unlikely(orig_tclass && !tclass)) { + if (policydb.allow_unknown) + goto allow; + return -EINVAL; + } + rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); + map_decision(orig_tclass, avd, policydb.allow_unknown); + read_unlock(&policy_rwlock); + return rc; +allow: + avd->allowed = 0xffffffff; + avd->auditallow = 0; + avd->auditdeny = 0xffffffff; + avd->seqno = latest_granting; + avd->flags = 0; + return 0; +} + +int security_compute_av_user(u32 ssid, + u32 tsid, + u16 tclass, + u32 requested, + struct av_decision *avd) +{ + int rc; if (!ss_initialized) { avd->allowed = 0xffffffff; @@ -823,29 +999,7 @@ int security_compute_av(u32 ssid, } read_lock(&policy_rwlock); - - scontext = sidtab_search(&sidtab, ssid); - if (!scontext) { - printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", - __func__, ssid); - rc = -EINVAL; - goto out; - } - tcontext = sidtab_search(&sidtab, tsid); - if (!tcontext) { - printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", - __func__, tsid); - rc = -EINVAL; - goto out; - } - - rc = context_struct_compute_av(scontext, tcontext, tclass, - requested, avd); - - /* permissive domain? */ - if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) - avd->flags |= AVD_FLAGS_PERMISSIVE; -out: + rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); read_unlock(&policy_rwlock); return rc; } @@ -1204,20 +1358,22 @@ out: static int security_compute_sid(u32 ssid, u32 tsid, - u16 tclass, + u16 orig_tclass, u32 specified, - u32 *out_sid) + u32 *out_sid, + bool kern) { struct context *scontext = NULL, *tcontext = NULL, newcontext; struct role_trans *roletr = NULL; struct avtab_key avkey; struct avtab_datum *avdatum; struct avtab_node *node; + u16 tclass; int rc = 0; if (!ss_initialized) { - switch (tclass) { - case SECCLASS_PROCESS: + switch (orig_tclass) { + case SECCLASS_PROCESS: /* kernel value */ *out_sid = ssid; break; default: @@ -1231,6 +1387,11 @@ static int security_compute_sid(u32 ssid, read_lock(&policy_rwlock); + if (kern) + tclass = unmap_class(orig_tclass); + else + tclass = orig_tclass; + scontext = sidtab_search(&sidtab, ssid); if (!scontext) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", @@ -1260,13 +1421,11 @@ static int security_compute_sid(u32 ssid, } /* Set the role and type to default values. */ - switch (tclass) { - case SECCLASS_PROCESS: + if (tclass == policydb.process_class) { /* Use the current role and type of process. */ newcontext.role = scontext->role; newcontext.type = scontext->type; - break; - default: + } else { /* Use the well-defined object role. */ newcontext.role = OBJECT_R_VAL; /* Use the type of the related object. */ @@ -1297,8 +1456,7 @@ static int security_compute_sid(u32 ssid, } /* Check for class-specific changes. */ - switch (tclass) { - case SECCLASS_PROCESS: + if (tclass == policydb.process_class) { if (specified & AVTAB_TRANSITION) { /* Look for a role transition rule. */ for (roletr = policydb.role_tr; roletr; @@ -1311,9 +1469,6 @@ static int security_compute_sid(u32 ssid, } } } - break; - default: - break; } /* Set the MLS attributes. @@ -1358,7 +1513,17 @@ int security_transition_sid(u32 ssid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid); + return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + out_sid, true); +} + +int security_transition_sid_user(u32 ssid, + u32 tsid, + u16 tclass, + u32 *out_sid) +{ + return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, + out_sid, false); } /** @@ -1379,7 +1544,8 @@ int security_member_sid(u32 ssid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid); + return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid, + false); } /** @@ -1400,144 +1566,8 @@ int security_change_sid(u32 ssid, u16 tclass, u32 *out_sid) { - return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid); -} - -/* - * Verify that each kernel class that is defined in the - * policy is correct - */ -static int validate_classes(struct policydb *p) -{ - int i, j; - struct class_datum *cladatum; - struct perm_datum *perdatum; - u32 nprim, tmp, common_pts_len, perm_val, pol_val; - u16 class_val; - const struct selinux_class_perm *kdefs = &selinux_class_perm; - const char *def_class, *def_perm, *pol_class; - struct symtab *perms; - bool print_unknown_handle = 0; - - if (p->allow_unknown) { - u32 num_classes = kdefs->cts_len; - p->undefined_perms = kcalloc(num_classes, sizeof(u32), GFP_KERNEL); - if (!p->undefined_perms) - return -ENOMEM; - } - - for (i = 1; i < kdefs->cts_len; i++) { - def_class = kdefs->class_to_string[i]; - if (!def_class) - continue; - if (i > p->p_classes.nprim) { - printk(KERN_INFO - "SELinux: class %s not defined in policy\n", - def_class); - if (p->reject_unknown) - return -EINVAL; - if (p->allow_unknown) - p->undefined_perms[i-1] = ~0U; - print_unknown_handle = 1; - continue; - } - pol_class = p->p_class_val_to_name[i-1]; - if (strcmp(pol_class, def_class)) { - printk(KERN_ERR - "SELinux: class %d is incorrect, found %s but should be %s\n", - i, pol_class, def_class); - return -EINVAL; - } - } - for (i = 0; i < kdefs->av_pts_len; i++) { - class_val = kdefs->av_perm_to_string[i].tclass; - perm_val = kdefs->av_perm_to_string[i].value; - def_perm = kdefs->av_perm_to_string[i].name; - if (class_val > p->p_classes.nprim) - continue; - pol_class = p->p_class_val_to_name[class_val-1]; - cladatum = hashtab_search(p->p_classes.table, pol_class); - BUG_ON(!cladatum); - perms = &cladatum->permissions; - nprim = 1 << (perms->nprim - 1); - if (perm_val > nprim) { - printk(KERN_INFO - "SELinux: permission %s in class %s not defined in policy\n", - def_perm, pol_class); - if (p->reject_unknown) - return -EINVAL; - if (p->allow_unknown) - p->undefined_perms[class_val-1] |= perm_val; - print_unknown_handle = 1; - continue; - } - perdatum = hashtab_search(perms->table, def_perm); - if (perdatum == NULL) { - printk(KERN_ERR - "SELinux: permission %s in class %s not found in policy, bad policy\n", - def_perm, pol_class); - return -EINVAL; - } - pol_val = 1 << (perdatum->value - 1); - if (pol_val != perm_val) { - printk(KERN_ERR - "SELinux: permission %s in class %s has incorrect value\n", - def_perm, pol_class); - return -EINVAL; - } - } - for (i = 0; i < kdefs->av_inherit_len; i++) { - class_val = kdefs->av_inherit[i].tclass; - if (class_val > p->p_classes.nprim) - continue; - pol_class = p->p_class_val_to_name[class_val-1]; - cladatum = hashtab_search(p->p_classes.table, pol_class); - BUG_ON(!cladatum); - if (!cladatum->comdatum) { - printk(KERN_ERR - "SELinux: class %s should have an inherits clause but does not\n", - pol_class); - return -EINVAL; - } - tmp = kdefs->av_inherit[i].common_base; - common_pts_len = 0; - while (!(tmp & 0x01)) { - common_pts_len++; - tmp >>= 1; - } - perms = &cladatum->comdatum->permissions; - for (j = 0; j < common_pts_len; j++) { - def_perm = kdefs->av_inherit[i].common_pts[j]; - if (j >= perms->nprim) { - printk(KERN_INFO - "SELinux: permission %s in class %s not defined in policy\n", - def_perm, pol_class); - if (p->reject_unknown) - return -EINVAL; - if (p->allow_unknown) - p->undefined_perms[class_val-1] |= (1 << j); - print_unknown_handle = 1; - continue; - } - perdatum = hashtab_search(perms->table, def_perm); - if (perdatum == NULL) { - printk(KERN_ERR - "SELinux: permission %s in class %s not found in policy, bad policy\n", - def_perm, pol_class); - return -EINVAL; - } - if (perdatum->value != j + 1) { - printk(KERN_ERR - "SELinux: permission %s in class %s has incorrect value\n", - def_perm, pol_class); - return -EINVAL; - } - } - } - if (print_unknown_handle) - printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n", - (security_get_allow_unknown() ? "allowed" : "denied")); - return 0; + return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid, + false); } /* Clone the SID into the new SID table. */ @@ -1710,8 +1740,10 @@ int security_load_policy(void *data, size_t len) { struct policydb oldpolicydb, newpolicydb; struct sidtab oldsidtab, newsidtab; + struct selinux_mapping *oldmap, *map = NULL; struct convert_context_args args; u32 seqno; + u16 map_size; int rc = 0; struct policy_file file = { data, len }, *fp = &file; @@ -1721,16 +1753,14 @@ int security_load_policy(void *data, size_t len) avtab_cache_destroy(); return -EINVAL; } - if (policydb_load_isids(&policydb, &sidtab)) { + if (selinux_set_mapping(&policydb, secclass_map, + ¤t_mapping, + ¤t_mapping_size)) { policydb_destroy(&policydb); avtab_cache_destroy(); return -EINVAL; } - /* Verify that the kernel defined classes are correct. */ - if (validate_classes(&policydb)) { - printk(KERN_ERR - "SELinux: the definition of a class is incorrect\n"); - sidtab_destroy(&sidtab); + if (policydb_load_isids(&policydb, &sidtab)) { policydb_destroy(&policydb); avtab_cache_destroy(); return -EINVAL; @@ -1759,13 +1789,9 @@ int security_load_policy(void *data, size_t len) return -ENOMEM; } - /* Verify that the kernel defined classes are correct. */ - if (validate_classes(&newpolicydb)) { - printk(KERN_ERR - "SELinux: the definition of a class is incorrect\n"); - rc = -EINVAL; + if (selinux_set_mapping(&newpolicydb, secclass_map, + &map, &map_size)) goto err; - } rc = security_preserve_bools(&newpolicydb); if (rc) { @@ -1799,6 +1825,9 @@ int security_load_policy(void *data, size_t len) memcpy(&policydb, &newpolicydb, sizeof policydb); sidtab_set(&sidtab, &newsidtab); security_load_policycaps(); + oldmap = current_mapping; + current_mapping = map; + current_mapping_size = map_size; seqno = ++latest_granting; policydb_loaded_version = policydb.policyvers; write_unlock_irq(&policy_rwlock); @@ -1806,6 +1835,7 @@ int security_load_policy(void *data, size_t len) /* Free the old policydb and SID table. */ policydb_destroy(&oldpolicydb); sidtab_destroy(&oldsidtab); + kfree(oldmap); avc_ss_reset(seqno); selnl_notify_policyload(seqno); @@ -1815,6 +1845,7 @@ int security_load_policy(void *data, size_t len) return 0; err: + kfree(map); sidtab_destroy(&newsidtab); policydb_destroy(&newpolicydb); return rc; @@ -2091,7 +2122,7 @@ out_unlock: } for (i = 0, j = 0; i < mynel; i++) { rc = avc_has_perm_noaudit(fromsid, mysids[i], - SECCLASS_PROCESS, + SECCLASS_PROCESS, /* kernel value */ PROCESS__TRANSITION, AVC_STRICT, NULL); if (!rc) @@ -2119,10 +2150,11 @@ out: */ int security_genfs_sid(const char *fstype, char *path, - u16 sclass, + u16 orig_sclass, u32 *sid) { int len; + u16 sclass; struct genfs *genfs; struct ocontext *c; int rc = 0, cmp = 0; @@ -2132,6 +2164,8 @@ int security_genfs_sid(const char *fstype, read_lock(&policy_rwlock); + sclass = unmap_class(orig_sclass); + for (genfs = policydb.genfs; genfs; genfs = genfs->next) { cmp = strcmp(fstype, genfs->fstype); if (cmp <= 0) -- cgit v1.2.3-70-g09d2 From 941fc5b2bf8f7dd1d0a9c502e152fa719ff6578e Mon Sep 17 00:00:00 2001 From: Stephen Smalley <sds@tycho.nsa.gov> Date: Thu, 1 Oct 2009 14:48:23 -0400 Subject: selinux: drop remapping of netlink classes Drop remapping of netlink classes and bypass of permission checking based on netlink message type for policy version < 18. This removes compatibility code introduced when the original single netlink security class used for all netlink sockets was split into finer-grained netlink classes based on netlink protocol and when permission checking was added based on netlink message type in Linux 2.6.8. The only known distribution that shipped with SELinux and policy < 18 was Fedora Core 2, which was EOL'd on 2005-04-11. Given that the remapping code was never updated to address the addition of newer netlink classes, that the corresponding userland support was dropped in 2005, and that the assumptions made by the remapping code about the fixed ordering among netlink classes in the policy may be violated in the future due to the dynamic class/perm discovery support, we should drop this compatibility code now. Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: James Morris <jmorris@namei.org> --- security/selinux/hooks.c | 6 +----- security/selinux/ss/services.c | 25 ------------------------- 2 files changed, 1 insertion(+), 30 deletions(-) (limited to 'security/selinux/ss/services.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a985d0bc59b..a29d6612a32 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -91,7 +91,6 @@ #define NUM_SEL_MNT_OPTS 5 -extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); extern struct security_operations *security_ops; @@ -4714,10 +4713,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) if (err) return err; - if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) - err = selinux_nlmsg_perm(sk, skb); - - return err; + return selinux_nlmsg_perm(sk, skb); } static int selinux_netlink_recv(struct sk_buff *skb, int capability) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index e19baa81fde..f270e378c0e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -65,7 +65,6 @@ #include "audit.h" extern void selnl_notify_policyload(u32 seqno); -unsigned int policydb_loaded_version; int selinux_policycap_netpeer; int selinux_policycap_openperm; @@ -616,17 +615,6 @@ static int context_struct_compute_av(struct context *scontext, struct ebitmap_node *snode, *tnode; unsigned int i, j; - /* - * Remap extended Netlink classes for old policy versions. - * Do this here rather than socket_type_to_security_class() - * in case a newer policy version is loaded, allowing sockets - * to remain in the correct class. - */ - if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) && - tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET)) - tclass = unmap_class(SECCLASS_NETLINK_SOCKET); - /* * Initialize the access vectors to the default values. */ @@ -761,17 +749,6 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, tclass = unmap_class(orig_tclass); - /* - * Remap extended Netlink classes for old policy versions. - * Do this here rather than socket_type_to_security_class() - * in case a newer policy version is loaded, allowing sockets - * to remain in the correct class. - */ - if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) - if (tclass >= unmap_class(SECCLASS_NETLINK_ROUTE_SOCKET) && - tclass <= unmap_class(SECCLASS_NETLINK_DNRT_SOCKET)) - tclass = unmap_class(SECCLASS_NETLINK_SOCKET); - if (!tclass || tclass > policydb.p_classes.nprim) { printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", __func__, tclass); @@ -1766,7 +1743,6 @@ int security_load_policy(void *data, size_t len) return -EINVAL; } security_load_policycaps(); - policydb_loaded_version = policydb.policyvers; ss_initialized = 1; seqno = ++latest_granting; selinux_complete_init(); @@ -1829,7 +1805,6 @@ int security_load_policy(void *data, size_t len) current_mapping = map; current_mapping_size = map_size; seqno = ++latest_granting; - policydb_loaded_version = policydb.policyvers; write_unlock_irq(&policy_rwlock); /* Free the old policydb and SID table. */ -- cgit v1.2.3-70-g09d2 From b7f3008ad1d795935551e4dd810b0255a7bfa3c9 Mon Sep 17 00:00:00 2001 From: Stephen Smalley <sds@tycho.nsa.gov> Date: Mon, 19 Oct 2009 10:08:50 -0400 Subject: SELinux: fix locking issue introduced with c6d3aaa4e35c71a3 Ensure that we release the policy read lock on all exit paths from security_compute_av. Signed-off-by: Stephen D. Smalley <sds@tycho.nsa.gov> Signed-off-by: James Morris <jmorris@namei.org> --- security/selinux/ss/services.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'security/selinux/ss/services.c') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index f270e378c0e..77f6e54bb43 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -935,19 +935,22 @@ int security_compute_av(u32 ssid, u32 requested; int rc; + read_lock(&policy_rwlock); + if (!ss_initialized) goto allow; - read_lock(&policy_rwlock); requested = unmap_perm(orig_tclass, orig_requested); tclass = unmap_class(orig_tclass); if (unlikely(orig_tclass && !tclass)) { if (policydb.allow_unknown) goto allow; - return -EINVAL; + rc = -EINVAL; + goto out; } rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); map_decision(orig_tclass, avd, policydb.allow_unknown); +out: read_unlock(&policy_rwlock); return rc; allow: @@ -956,7 +959,8 @@ allow: avd->auditdeny = 0xffffffff; avd->seqno = latest_granting; avd->flags = 0; - return 0; + rc = 0; + goto out; } int security_compute_av_user(u32 ssid, -- cgit v1.2.3-70-g09d2 From 0bce95279909aa4cc401a2e3140b4295ca22e72a Mon Sep 17 00:00:00 2001 From: Eric Paris <eparis@redhat.com> Date: Mon, 23 Nov 2009 16:47:23 -0500 Subject: SELinux: print denials for buggy kernel with unknown perms Historically we've seen cases where permissions are requested for classes where they do not exist. In particular we have seen CIFS forget to set i_mode to indicate it is a directory so when we later check something like remove_name we have problems since it wasn't defined in tclass file. This used to result in a avc which included the permission 0x2000 or something. Currently the kernel will deny the operations (good thing) but will not print ANY information (bad thing). First the auditdeny field is no extended to include unknown permissions. After that is fixed the logic in avc_dump_query to output this information isn't right since it will remove the permission from the av and print the phrase "<NULL>". This takes us back to the behavior before the classmap rewrite. Signed-off-by: Eric Paris <eparis@redhat.com> Signed-off-by: James Morris <jmorris@namei.org> --- security/selinux/avc.c | 2 +- security/selinux/ss/services.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'security/selinux/ss/services.c') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 18f4103e02b..f2dde268165 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -117,7 +117,7 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) i = 0; perm = 1; while (i < (sizeof(av) * 8)) { - if (perm & av) { + if ((perm & av) && perms[i]) { audit_log_format(ab, " %s", perms[i]); av &= ~perm; } diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 77f6e54bb43..d6bb20cbad6 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -239,6 +239,13 @@ static void map_decision(u16 tclass, struct av_decision *avd, if (!allow_unknown && !current_mapping[tclass].perms[i]) result |= 1<<i; } + /* + * In case the kernel has a bug and requests a permission + * between num_perms and the maximum permission number, we + * should audit that denial + */ + for (; i < (sizeof(u32)*8); i++) + result |= 1<<i; avd->auditdeny = result; } } -- cgit v1.2.3-70-g09d2