Commit fb2a624d authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'selinux-pr-20181224' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull selinux patches from Paul Moore:
 "I already used my best holiday pull request lines in the audit pull
  request, so this one is going to be a bit more boring, sorry about
  that. To make up for this, we do have a birthday of sorts to
  celebrate: SELinux turns 18 years old this December. Perhaps not the
  most exciting thing in the world for most people, but I think it's
  safe to say that anyone reading this email doesn't exactly fall into
  the "most people" category.

  Back to business and the pull request itself:

  Ondrej has five patches in this pull request and I lump them into
  three categories: one patch to always allow submounts (using similar
  logic to elsewhere in the kernel), one to fix some issues with the
  SELinux policydb, and the others to cleanup and improve the SELinux
  sidtab.

  The other patches from Alexey and Petr and trivial fixes that are
  adequately described in their respective subject lines.

  With this last pull request of the year, I want to thank everyone who
  has contributed patches, testing, and reviews to the SELinux project
  this year, and the past 18 years. Like any good open source effort,
  SELinux is only as good as the community which supports it, and I'm
  very happy that we have the community we do - thank you all!"

* tag 'selinux-pr-20181224' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: overhaul sidtab to fix bug and improve performance
  selinux: use separate table for initial SID lookup
  selinux: make "selinux_policycap_names[]" const char *
  selinux: always allow mounting submounts
  selinux: refactor sidtab conversion
  Documentation: Update SELinux reference policy URL
  selinux: policydb - fix byte order and alignment issues
parents 047ce6d3 ee1a84fd
......@@ -6,7 +6,7 @@ If you want to use SELinux, chances are you will want
to use the distro-provided policies, or install the
latest reference policy release from
http://oss.tresys.com/projects/refpolicy
https://github.com/SELinuxProject/refpolicy
However, if you want to install a dummy policy for
testing, you can do using ``mdp`` provided under
......
......@@ -2934,7 +2934,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
return rc;
/* Allow all mounts performed by the kernel */
if (flags & MS_KERNMOUNT)
if (flags & (MS_KERNMOUNT | MS_SUBMOUNT))
return 0;
ad.type = LSM_AUDIT_DATA_DENTRY;
......
......@@ -81,7 +81,7 @@ enum {
};
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
extern const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
/*
* type_datum properties
......
......@@ -440,16 +440,17 @@ int mls_setup_user_range(struct policydb *p,
/*
* Convert the MLS fields in the security context
* structure `c' from the values specified in the
* policy `oldp' to the values specified in the policy `newp'.
* structure `oldc' from the values specified in the
* policy `oldp' to the values specified in the policy `newp',
* storing the resulting context in `newc'.
*/
int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
struct context *c)
struct context *oldc,
struct context *newc)
{
struct level_datum *levdatum;
struct cat_datum *catdatum;
struct ebitmap bitmap;
struct ebitmap_node *node;
int l, i;
......@@ -459,28 +460,25 @@ int mls_convert_context(struct policydb *oldp,
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(newp->p_levels.table,
sym_name(oldp, SYM_LEVELS,
c->range.level[l].sens - 1));
oldc->range.level[l].sens - 1));
if (!levdatum)
return -EINVAL;
c->range.level[l].sens = levdatum->level->sens;
newc->range.level[l].sens = levdatum->level->sens;
ebitmap_init(&bitmap);
ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
node, i) {
int rc;
catdatum = hashtab_search(newp->p_cats.table,
sym_name(oldp, SYM_CATS, i));
if (!catdatum)
return -EINVAL;
rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
rc = ebitmap_set_bit(&newc->range.level[l].cat,
catdatum->value - 1, 1);
if (rc)
return rc;
cond_resched();
}
ebitmap_destroy(&c->range.level[l].cat);
c->range.level[l].cat = bitmap;
}
return 0;
......
......@@ -46,7 +46,8 @@ int mls_range_set(struct context *context, struct mls_range *range);
int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
struct context *context);
struct context *oldc,
struct context *newc);
int mls_compute_sid(struct policydb *p,
struct context *scontext,
......
......@@ -909,13 +909,21 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
if (!c->context[0].user) {
pr_err("SELinux: SID %s was never defined.\n",
c->u.name);
sidtab_destroy(s);
goto out;
}
if (c->sid[0] == SECSID_NULL || c->sid[0] > SECINITSID_NUM) {
pr_err("SELinux: Initial SID %s out of range.\n",
c->u.name);
sidtab_destroy(s);
goto out;
}
rc = sidtab_insert(s, c->sid[0], &c->context[0]);
rc = sidtab_set_initial(s, c->sid[0], &c->context[0]);
if (rc) {
pr_err("SELinux: unable to load initial SID %s.\n",
c->u.name);
sidtab_destroy(s);
goto out;
}
}
......@@ -2108,6 +2116,7 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
{
int i, j, rc;
u32 nel, len;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
u32 nodebuf[8];
......@@ -2217,21 +2226,30 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
goto out;
break;
}
case OCON_IBPKEY:
rc = next_entry(nodebuf, fp, sizeof(u32) * 4);
case OCON_IBPKEY: {
u32 pkey_lo, pkey_hi;
rc = next_entry(prefixbuf, fp, sizeof(u64));
if (rc)
goto out;
/* we need to have subnet_prefix in CPU order */
c->u.ibpkey.subnet_prefix = be64_to_cpu(prefixbuf[0]);
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto out;
c->u.ibpkey.subnet_prefix = be64_to_cpu(*((__be64 *)nodebuf));
pkey_lo = le32_to_cpu(buf[0]);
pkey_hi = le32_to_cpu(buf[1]);
if (nodebuf[2] > 0xffff ||
nodebuf[3] > 0xffff) {
if (pkey_lo > U16_MAX || pkey_hi > U16_MAX) {
rc = -EINVAL;
goto out;
}
c->u.ibpkey.low_pkey = le32_to_cpu(nodebuf[2]);
c->u.ibpkey.high_pkey = le32_to_cpu(nodebuf[3]);
c->u.ibpkey.low_pkey = pkey_lo;
c->u.ibpkey.high_pkey = pkey_hi;
rc = context_read_and_validate(&c->context[0],
p,
......@@ -2239,7 +2257,10 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
if (rc)
goto out;
break;
case OCON_IBENDPORT:
}
case OCON_IBENDPORT: {
u32 port;
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto out;
......@@ -2249,12 +2270,13 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
if (rc)
goto out;
if (buf[1] > 0xff || buf[1] == 0) {
port = le32_to_cpu(buf[1]);
if (port > U8_MAX || port == 0) {
rc = -EINVAL;
goto out;
}
c->u.ibendport.port = le32_to_cpu(buf[1]);
c->u.ibendport.port = port;
rc = context_read_and_validate(&c->context[0],
p,
......@@ -2262,7 +2284,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
if (rc)
goto out;
break;
}
} /* end case */
} /* end switch */
}
}
rc = 0;
......@@ -3105,6 +3128,7 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
{
unsigned int i, j, rc;
size_t nel, len;
__be64 prefixbuf[1];
__le32 buf[3];
u32 nodebuf[8];
struct ocontext *c;
......@@ -3192,12 +3216,17 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
return rc;
break;
case OCON_IBPKEY:
*((__be64 *)nodebuf) = cpu_to_be64(c->u.ibpkey.subnet_prefix);
/* subnet_prefix is in CPU order */
prefixbuf[0] = cpu_to_be64(c->u.ibpkey.subnet_prefix);
nodebuf[2] = cpu_to_le32(c->u.ibpkey.low_pkey);
nodebuf[3] = cpu_to_le32(c->u.ibpkey.high_pkey);
rc = put_entry(prefixbuf, sizeof(u64), 1, fp);
if (rc)
return rc;
rc = put_entry(nodebuf, sizeof(u32), 4, fp);
buf[0] = cpu_to_le32(c->u.ibpkey.low_pkey);
buf[1] = cpu_to_le32(c->u.ibpkey.high_pkey);
rc = put_entry(buf, sizeof(u32), 2, fp);
if (rc)
return rc;
rc = context_write(p, &c->context[0], fp);
......
......@@ -71,7 +71,7 @@
#include "audit.h"
/* Policy capability names */
char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
"network_peer_controls",
"open_perms",
"extended_socket_class",
......@@ -776,7 +776,7 @@ static int security_compute_validatetrans(struct selinux_state *state,
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
if (!user)
tclass = unmap_class(&state->ss->map, orig_tclass);
......@@ -876,7 +876,7 @@ int security_bounded_transition(struct selinux_state *state,
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
rc = -EINVAL;
old_context = sidtab_search(sidtab, old_sid);
......@@ -1034,7 +1034,7 @@ void security_compute_xperms_decision(struct selinux_state *state,
goto allow;
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
......@@ -1123,7 +1123,7 @@ void security_compute_av(struct selinux_state *state,
goto allow;
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
......@@ -1177,7 +1177,7 @@ void security_compute_av_user(struct selinux_state *state,
goto allow;
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
......@@ -1315,7 +1315,7 @@ static int security_sid_to_context_core(struct selinux_state *state,
}
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
if (force)
context = sidtab_search_force(sidtab, sid);
else
......@@ -1483,7 +1483,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
}
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
rc = string_to_context_struct(policydb, sidtab, scontext2,
&context, def_sid);
if (rc == -EINVAL && force) {
......@@ -1668,7 +1668,7 @@ static int security_compute_sid(struct selinux_state *state,
}
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
......@@ -1880,19 +1880,6 @@ int security_change_sid(struct selinux_state *state,
out_sid, false);
}
/* Clone the SID into the new SID table. */
static int clone_sid(u32 sid,
struct context *context,
void *arg)
{
struct sidtab *s = arg;
if (sid > SECINITSID_NUM)
return sidtab_insert(s, sid, context);
else
return 0;
}
static inline int convert_context_handle_invalid_context(
struct selinux_state *state,
struct context *context)
......@@ -1920,101 +1907,84 @@ struct convert_context_args {
/*
* Convert the values in the security context
* structure `c' from the values specified
* structure `oldc' from the values specified
* in the policy `p->oldp' to the values specified
* in the policy `p->newp'. Verify that the
* context is valid under the new policy.
* in the policy `p->newp', storing the new context
* in `newc'. Verify that the context is valid
* under the new policy.
*/
static int convert_context(u32 key,
struct context *c,
void *p)
static int convert_context(struct context *oldc, struct context *newc, void *p)
{
struct convert_context_args *args;
struct context oldc;
struct ocontext *oc;
struct mls_range *range;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *s;
u32 len;
int rc = 0;
if (key <= SECINITSID_NUM)
goto out;
int rc;
args = p;
if (c->str) {
struct context ctx;
rc = -ENOMEM;
s = kstrdup(c->str, GFP_KERNEL);
if (oldc->str) {
s = kstrdup(oldc->str, GFP_KERNEL);
if (!s)
goto out;
return -ENOMEM;
rc = string_to_context_struct(args->newp, NULL, s,
&ctx, SECSID_NULL);
kfree(s);
if (!rc) {
pr_info("SELinux: Context %s became valid (mapped).\n",
c->str);
/* Replace string with mapped representation. */
kfree(c->str);
memcpy(c, &ctx, sizeof(*c));
goto out;
} else if (rc == -EINVAL) {
newc, SECSID_NULL);
if (rc == -EINVAL) {
/* Retain string representation for later mapping. */
rc = 0;
goto out;
} else {
context_init(newc);
newc->str = s;
newc->len = oldc->len;
return 0;
}
kfree(s);
if (rc) {
/* Other error condition, e.g. ENOMEM. */
pr_err("SELinux: Unable to map context %s, rc = %d.\n",
c->str, -rc);
goto out;
oldc->str, -rc);
return rc;
}
pr_info("SELinux: Context %s became valid (mapped).\n",
oldc->str);
return 0;
}
rc = context_cpy(&oldc, c);
if (rc)
goto out;
context_init(newc);
/* Convert the user. */
rc = -EINVAL;
usrdatum = hashtab_search(args->newp->p_users.table,
sym_name(args->oldp, SYM_USERS, c->user - 1));
sym_name(args->oldp,
SYM_USERS, oldc->user - 1));
if (!usrdatum)
goto bad;
c->user = usrdatum->value;
newc->user = usrdatum->value;
/* Convert the role. */
rc = -EINVAL;
role = hashtab_search(args->newp->p_roles.table,
sym_name(args->oldp, SYM_ROLES, c->role - 1));
sym_name(args->oldp, SYM_ROLES, oldc->role - 1));
if (!role)
goto bad;
c->role = role->value;
newc->role = role->value;
/* Convert the type. */
rc = -EINVAL;
typdatum = hashtab_search(args->newp->p_types.table,
sym_name(args->oldp, SYM_TYPES, c->type - 1));
sym_name(args->oldp,
SYM_TYPES, oldc->type - 1));
if (!typdatum)
goto bad;
c->type = typdatum->value;
newc->type = typdatum->value;
/* Convert the MLS fields if dealing with MLS policies */
if (args->oldp->mls_enabled && args->newp->mls_enabled) {
rc = mls_convert_context(args->oldp, args->newp, c);
rc = mls_convert_context(args->oldp, args->newp, oldc, newc);
if (rc)
goto bad;
} else if (args->oldp->mls_enabled && !args->newp->mls_enabled) {
/*
* Switching between MLS and non-MLS policy:
* free any storage used by the MLS fields in the
* context for all existing entries in the sidtab.
*/
mls_context_destroy(c);
} else if (!args->oldp->mls_enabled && args->newp->mls_enabled) {
/*
* Switching between non-MLS and MLS policy:
......@@ -2032,38 +2002,30 @@ static int convert_context(u32 key,
" the initial SIDs list\n");
goto bad;
}
range = &oc->context[0].range;
rc = mls_range_set(c, range);
rc = mls_range_set(newc, &oc->context[0].range);
if (rc)
goto bad;
}
/* Check the validity of the new context. */
if (!policydb_context_isvalid(args->newp, c)) {
rc = convert_context_handle_invalid_context(args->state,
&oldc);
if (!policydb_context_isvalid(args->newp, newc)) {
rc = convert_context_handle_invalid_context(args->state, oldc);
if (rc)
goto bad;
}
context_destroy(&oldc);
rc = 0;
out:
return rc;
return 0;
bad:
/* Map old representation to string and save it. */
rc = context_struct_to_string(args->oldp, &oldc, &s, &len);
rc = context_struct_to_string(args->oldp, oldc, &s, &len);
if (rc)
return rc;
context_destroy(&oldc);
context_destroy(c);
c->str = s;
c->len = len;
context_destroy(newc);
newc->str = s;
newc->len = len;
pr_info("SELinux: Context %s became invalid (unmapped).\n",
c->str);
rc = 0;
goto out;
newc->str);
return 0;
}
static void security_load_policycaps(struct selinux_state *state)
......@@ -2103,11 +2065,11 @@ static int security_preserve_bools(struct selinux_state *state,
int security_load_policy(struct selinux_state *state, void *data, size_t len)
{
struct policydb *policydb;
struct sidtab *sidtab;
struct sidtab *oldsidtab, *newsidtab;
struct policydb *oldpolicydb, *newpolicydb;
struct sidtab oldsidtab, newsidtab;
struct selinux_mapping *oldmapping;
struct selinux_map newmap;
struct sidtab_convert_params convert_params;
struct convert_context_args args;
u32 seqno;
int rc = 0;
......@@ -2121,27 +2083,37 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
newpolicydb = oldpolicydb + 1;
policydb = &state->ss->policydb;
sidtab = &state->ss->sidtab;
newsidtab = kmalloc(sizeof(*newsidtab), GFP_KERNEL);
if (!newsidtab) {
rc = -ENOMEM;
goto out;
}
if (!state->initialized) {
rc = policydb_read(policydb, fp);
if (rc)
if (rc) {
kfree(newsidtab);
goto out;
}
policydb->len = len;
rc = selinux_set_mapping(policydb, secclass_map,
&state->ss->map);
if (rc) {
kfree(newsidtab);
policydb_destroy(policydb);
goto out;
}
rc = policydb_load_isids(policydb, sidtab);
rc = policydb_load_isids(policydb, newsidtab);
if (rc) {
kfree(newsidtab);
policydb_destroy(policydb);
goto out;
}
state->ss->sidtab = newsidtab;
security_load_policycaps(state);
state->initialized = 1;
seqno = ++state->ss->latest_granting;
......@@ -2154,13 +2126,11 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
goto out;
}
#if 0
sidtab_hash_eval(sidtab, "sids");
#endif
rc = policydb_read(newpolicydb, fp);
if (rc)
if (rc) {
kfree(newsidtab);
goto out;
}
newpolicydb->len = len;
/* If switching between different policy types, log MLS status */
......@@ -2169,10 +2139,11 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
else if (!policydb->mls_enabled && newpolicydb->mls_enabled)
pr_info("SELinux: Enabling MLS support...\n");
rc = policydb_load_isids(newpolicydb, &newsidtab);
rc = policydb_load_isids(newpolicydb, newsidtab);
if (rc) {
pr_err("SELinux: unable to load the initial SIDs\n");
policydb_destroy(newpolicydb);
kfree(newsidtab);
goto out;
}
......@@ -2186,12 +2157,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
goto err;
}
/* Clone the SID table. */
sidtab_shutdown(sidtab);
rc = sidtab_map(sidtab, clone_sid, &newsidtab);
if (rc)
goto err;
oldsidtab = state->ss->sidtab;
/*
* Convert the internal representations of contexts
......@@ -2200,7 +2166,12 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
args.s