forked from luck/tmp_suning_uos_patched
selinux/stable-4.21 PR 20181224
-----BEGIN PGP SIGNATURE----- iQJIBAABCAAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAlwhAjMUHHBhdWxAcGF1 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXPF3RAAkM8wRBk3PoH68kgKsbcsE6MTyoVK tneNt/brRSG11sFhRNONE7TqfM+yM68BqsTRDL1Jn36ONnfL9VB49myHyDBR4jqj FSvvi58UhixwOQNyi7aQEEGg4ltenlcM3HJLPlmWr/PXDRiklBgfzobkKpKr7QWR NdME4/u2OTO+BWbIJZ1+d8kJoSsYPiPIQLRS85kpZ/bgl0Y92QYRJoWUNfmq9/Og al91gVCSqAy6LmWLD0ddrc8xH7YDG8T/ngBHNIUb04FPHlxwJgX8BKQA7nABKntL e+wy40FpuqAuZBMbOj6/NX5gHF12fMSq1oZx41OEmL0JqqzLDuBNKlN7IWMXgZEJ 96HcFRzJWydxmf645GnMR3qsHcSvLhNGGCOXiX3NdhEsO3tJ2sJs5Jz/Af7pN9f+ Jk8ihOqOf9jd6jIW74Iev92RPOnh8aau20tueOaA/sS6t4ByyNRxFodi82x3htNg IN0z6DGBIa/aEMjfLRN5Ihj0I7Y184fWW/mGfPTZnnjifjhTWacbHKa3b4BtfsA/ ZXp/QWBhY3fGNy3+rd3j1u3ZGz/4kfoGZUIlT0LssXpnQzP2H0T74CSbIbQj1Dgf rZq34A3wndYI1AZwo/0I/sd0lwW538peQ9zPe88gZhh02IwNEjVRzoA+a1oLVywQ 3vIyeD++NrB6HGM= =uWQk -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
fb2a624d5f
|
@ -6,7 +6,7 @@ If you want to use SELinux, chances are you will want
|
||||||
to use the distro-provided policies, or install the
|
to use the distro-provided policies, or install the
|
||||||
latest reference policy release from
|
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
|
However, if you want to install a dummy policy for
|
||||||
testing, you can do using ``mdp`` provided under
|
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;
|
return rc;
|
||||||
|
|
||||||
/* Allow all mounts performed by the kernel */
|
/* Allow all mounts performed by the kernel */
|
||||||
if (flags & MS_KERNMOUNT)
|
if (flags & (MS_KERNMOUNT | MS_SUBMOUNT))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ad.type = LSM_AUDIT_DATA_DENTRY;
|
ad.type = LSM_AUDIT_DATA_DENTRY;
|
||||||
|
|
|
@ -81,7 +81,7 @@ enum {
|
||||||
};
|
};
|
||||||
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
|
#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
|
* type_datum properties
|
||||||
|
|
|
@ -440,16 +440,17 @@ int mls_setup_user_range(struct policydb *p,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert the MLS fields in the security context
|
* Convert the MLS fields in the security context
|
||||||
* structure `c' from the values specified in the
|
* structure `oldc' from the values specified in the
|
||||||
* policy `oldp' to the values specified in the policy `newp'.
|
* policy `oldp' to the values specified in the policy `newp',
|
||||||
|
* storing the resulting context in `newc'.
|
||||||
*/
|
*/
|
||||||
int mls_convert_context(struct policydb *oldp,
|
int mls_convert_context(struct policydb *oldp,
|
||||||
struct policydb *newp,
|
struct policydb *newp,
|
||||||
struct context *c)
|
struct context *oldc,
|
||||||
|
struct context *newc)
|
||||||
{
|
{
|
||||||
struct level_datum *levdatum;
|
struct level_datum *levdatum;
|
||||||
struct cat_datum *catdatum;
|
struct cat_datum *catdatum;
|
||||||
struct ebitmap bitmap;
|
|
||||||
struct ebitmap_node *node;
|
struct ebitmap_node *node;
|
||||||
int l, i;
|
int l, i;
|
||||||
|
|
||||||
|
@ -459,28 +460,25 @@ int mls_convert_context(struct policydb *oldp,
|
||||||
for (l = 0; l < 2; l++) {
|
for (l = 0; l < 2; l++) {
|
||||||
levdatum = hashtab_search(newp->p_levels.table,
|
levdatum = hashtab_search(newp->p_levels.table,
|
||||||
sym_name(oldp, SYM_LEVELS,
|
sym_name(oldp, SYM_LEVELS,
|
||||||
c->range.level[l].sens - 1));
|
oldc->range.level[l].sens - 1));
|
||||||
|
|
||||||
if (!levdatum)
|
if (!levdatum)
|
||||||
return -EINVAL;
|
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(&oldc->range.level[l].cat,
|
||||||
ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
|
node, i) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
catdatum = hashtab_search(newp->p_cats.table,
|
catdatum = hashtab_search(newp->p_cats.table,
|
||||||
sym_name(oldp, SYM_CATS, i));
|
sym_name(oldp, SYM_CATS, i));
|
||||||
if (!catdatum)
|
if (!catdatum)
|
||||||
return -EINVAL;
|
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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
cond_resched();
|
|
||||||
}
|
}
|
||||||
ebitmap_destroy(&c->range.level[l].cat);
|
|
||||||
c->range.level[l].cat = bitmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -46,7 +46,8 @@ int mls_range_set(struct context *context, struct mls_range *range);
|
||||||
|
|
||||||
int mls_convert_context(struct policydb *oldp,
|
int mls_convert_context(struct policydb *oldp,
|
||||||
struct policydb *newp,
|
struct policydb *newp,
|
||||||
struct context *context);
|
struct context *oldc,
|
||||||
|
struct context *newc);
|
||||||
|
|
||||||
int mls_compute_sid(struct policydb *p,
|
int mls_compute_sid(struct policydb *p,
|
||||||
struct context *scontext,
|
struct context *scontext,
|
||||||
|
|
|
@ -909,13 +909,21 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
|
||||||
if (!c->context[0].user) {
|
if (!c->context[0].user) {
|
||||||
pr_err("SELinux: SID %s was never defined.\n",
|
pr_err("SELinux: SID %s was never defined.\n",
|
||||||
c->u.name);
|
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;
|
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) {
|
if (rc) {
|
||||||
pr_err("SELinux: unable to load initial SID %s.\n",
|
pr_err("SELinux: unable to load initial SID %s.\n",
|
||||||
c->u.name);
|
c->u.name);
|
||||||
|
sidtab_destroy(s);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2108,6 +2116,7 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
|
||||||
{
|
{
|
||||||
int i, j, rc;
|
int i, j, rc;
|
||||||
u32 nel, len;
|
u32 nel, len;
|
||||||
|
__be64 prefixbuf[1];
|
||||||
__le32 buf[3];
|
__le32 buf[3];
|
||||||
struct ocontext *l, *c;
|
struct ocontext *l, *c;
|
||||||
u32 nodebuf[8];
|
u32 nodebuf[8];
|
||||||
|
@ -2217,21 +2226,30 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
|
||||||
goto out;
|
goto out;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OCON_IBPKEY:
|
case OCON_IBPKEY: {
|
||||||
rc = next_entry(nodebuf, fp, sizeof(u32) * 4);
|
u32 pkey_lo, pkey_hi;
|
||||||
|
|
||||||
|
rc = next_entry(prefixbuf, fp, sizeof(u64));
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
c->u.ibpkey.subnet_prefix = be64_to_cpu(*((__be64 *)nodebuf));
|
/* we need to have subnet_prefix in CPU order */
|
||||||
|
c->u.ibpkey.subnet_prefix = be64_to_cpu(prefixbuf[0]);
|
||||||
|
|
||||||
if (nodebuf[2] > 0xffff ||
|
rc = next_entry(buf, fp, sizeof(u32) * 2);
|
||||||
nodebuf[3] > 0xffff) {
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pkey_lo = le32_to_cpu(buf[0]);
|
||||||
|
pkey_hi = le32_to_cpu(buf[1]);
|
||||||
|
|
||||||
|
if (pkey_lo > U16_MAX || pkey_hi > U16_MAX) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->u.ibpkey.low_pkey = le32_to_cpu(nodebuf[2]);
|
c->u.ibpkey.low_pkey = pkey_lo;
|
||||||
c->u.ibpkey.high_pkey = le32_to_cpu(nodebuf[3]);
|
c->u.ibpkey.high_pkey = pkey_hi;
|
||||||
|
|
||||||
rc = context_read_and_validate(&c->context[0],
|
rc = context_read_and_validate(&c->context[0],
|
||||||
p,
|
p,
|
||||||
|
@ -2239,7 +2257,10 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
break;
|
break;
|
||||||
case OCON_IBENDPORT:
|
}
|
||||||
|
case OCON_IBENDPORT: {
|
||||||
|
u32 port;
|
||||||
|
|
||||||
rc = next_entry(buf, fp, sizeof(u32) * 2);
|
rc = next_entry(buf, fp, sizeof(u32) * 2);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -2249,12 +2270,13 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (buf[1] > 0xff || buf[1] == 0) {
|
port = le32_to_cpu(buf[1]);
|
||||||
|
if (port > U8_MAX || port == 0) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->u.ibendport.port = le32_to_cpu(buf[1]);
|
c->u.ibendport.port = port;
|
||||||
|
|
||||||
rc = context_read_and_validate(&c->context[0],
|
rc = context_read_and_validate(&c->context[0],
|
||||||
p,
|
p,
|
||||||
|
@ -2262,7 +2284,8 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
break;
|
break;
|
||||||
}
|
} /* end case */
|
||||||
|
} /* end switch */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
@ -3105,6 +3128,7 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
|
||||||
{
|
{
|
||||||
unsigned int i, j, rc;
|
unsigned int i, j, rc;
|
||||||
size_t nel, len;
|
size_t nel, len;
|
||||||
|
__be64 prefixbuf[1];
|
||||||
__le32 buf[3];
|
__le32 buf[3];
|
||||||
u32 nodebuf[8];
|
u32 nodebuf[8];
|
||||||
struct ocontext *c;
|
struct ocontext *c;
|
||||||
|
@ -3192,12 +3216,17 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
|
||||||
return rc;
|
return rc;
|
||||||
break;
|
break;
|
||||||
case OCON_IBPKEY:
|
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);
|
rc = put_entry(prefixbuf, sizeof(u64), 1, fp);
|
||||||
nodebuf[3] = cpu_to_le32(c->u.ibpkey.high_pkey);
|
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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
rc = context_write(p, &c->context[0], fp);
|
rc = context_write(p, &c->context[0], fp);
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
#include "audit.h"
|
#include "audit.h"
|
||||||
|
|
||||||
/* Policy capability names */
|
/* Policy capability names */
|
||||||
char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
|
const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
|
||||||
"network_peer_controls",
|
"network_peer_controls",
|
||||||
"open_perms",
|
"open_perms",
|
||||||
"extended_socket_class",
|
"extended_socket_class",
|
||||||
|
@ -776,7 +776,7 @@ static int security_compute_validatetrans(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
tclass = unmap_class(&state->ss->map, orig_tclass);
|
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);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
old_context = sidtab_search(sidtab, old_sid);
|
old_context = sidtab_search(sidtab, old_sid);
|
||||||
|
@ -1034,7 +1034,7 @@ void security_compute_xperms_decision(struct selinux_state *state,
|
||||||
goto allow;
|
goto allow;
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
scontext = sidtab_search(sidtab, ssid);
|
scontext = sidtab_search(sidtab, ssid);
|
||||||
if (!scontext) {
|
if (!scontext) {
|
||||||
|
@ -1123,7 +1123,7 @@ void security_compute_av(struct selinux_state *state,
|
||||||
goto allow;
|
goto allow;
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
scontext = sidtab_search(sidtab, ssid);
|
scontext = sidtab_search(sidtab, ssid);
|
||||||
if (!scontext) {
|
if (!scontext) {
|
||||||
|
@ -1177,7 +1177,7 @@ void security_compute_av_user(struct selinux_state *state,
|
||||||
goto allow;
|
goto allow;
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
scontext = sidtab_search(sidtab, ssid);
|
scontext = sidtab_search(sidtab, ssid);
|
||||||
if (!scontext) {
|
if (!scontext) {
|
||||||
|
@ -1315,7 +1315,7 @@ static int security_sid_to_context_core(struct selinux_state *state,
|
||||||
}
|
}
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
if (force)
|
if (force)
|
||||||
context = sidtab_search_force(sidtab, sid);
|
context = sidtab_search_force(sidtab, sid);
|
||||||
else
|
else
|
||||||
|
@ -1483,7 +1483,7 @@ static int security_context_to_sid_core(struct selinux_state *state,
|
||||||
}
|
}
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
rc = string_to_context_struct(policydb, sidtab, scontext2,
|
rc = string_to_context_struct(policydb, sidtab, scontext2,
|
||||||
&context, def_sid);
|
&context, def_sid);
|
||||||
if (rc == -EINVAL && force) {
|
if (rc == -EINVAL && force) {
|
||||||
|
@ -1668,7 +1668,7 @@ static int security_compute_sid(struct selinux_state *state,
|
||||||
}
|
}
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
scontext = sidtab_search(sidtab, ssid);
|
scontext = sidtab_search(sidtab, ssid);
|
||||||
if (!scontext) {
|
if (!scontext) {
|
||||||
|
@ -1880,19 +1880,6 @@ int security_change_sid(struct selinux_state *state,
|
||||||
out_sid, false);
|
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(
|
static inline int convert_context_handle_invalid_context(
|
||||||
struct selinux_state *state,
|
struct selinux_state *state,
|
||||||
struct context *context)
|
struct context *context)
|
||||||
|
@ -1920,101 +1907,84 @@ struct convert_context_args {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert the values in the security context
|
* 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->oldp' to the values specified
|
||||||
* in the policy `p->newp'. Verify that the
|
* in the policy `p->newp', storing the new context
|
||||||
* context is valid under the new policy.
|
* in `newc'. Verify that the context is valid
|
||||||
|
* under the new policy.
|
||||||
*/
|
*/
|
||||||
static int convert_context(u32 key,
|
static int convert_context(struct context *oldc, struct context *newc, void *p)
|
||||||
struct context *c,
|
|
||||||
void *p)
|
|
||||||
{
|
{
|
||||||
struct convert_context_args *args;
|
struct convert_context_args *args;
|
||||||
struct context oldc;
|
|
||||||
struct ocontext *oc;
|
struct ocontext *oc;
|
||||||
struct mls_range *range;
|
|
||||||
struct role_datum *role;
|
struct role_datum *role;
|
||||||
struct type_datum *typdatum;
|
struct type_datum *typdatum;
|
||||||
struct user_datum *usrdatum;
|
struct user_datum *usrdatum;
|
||||||
char *s;
|
char *s;
|
||||||
u32 len;
|
u32 len;
|
||||||
int rc = 0;
|
int rc;
|
||||||
|
|
||||||
if (key <= SECINITSID_NUM)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
args = p;
|
args = p;
|
||||||
|
|
||||||
if (c->str) {
|
if (oldc->str) {
|
||||||
struct context ctx;
|
s = kstrdup(oldc->str, GFP_KERNEL);
|
||||||
|
|
||||||
rc = -ENOMEM;
|
|
||||||
s = kstrdup(c->str, GFP_KERNEL);
|
|
||||||
if (!s)
|
if (!s)
|
||||||
goto out;
|
return -ENOMEM;
|
||||||
|
|
||||||
rc = string_to_context_struct(args->newp, NULL, s,
|
rc = string_to_context_struct(args->newp, NULL, s,
|
||||||
&ctx, SECSID_NULL);
|
newc, SECSID_NULL);
|
||||||
kfree(s);
|
if (rc == -EINVAL) {
|
||||||
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) {
|
|
||||||
/* Retain string representation for later mapping. */
|
/* Retain string representation for later mapping. */
|
||||||
rc = 0;
|
context_init(newc);
|
||||||
goto out;
|
newc->str = s;
|
||||||
} else {
|
newc->len = oldc->len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
kfree(s);
|
||||||
|
if (rc) {
|
||||||
/* Other error condition, e.g. ENOMEM. */
|
/* Other error condition, e.g. ENOMEM. */
|
||||||
pr_err("SELinux: Unable to map context %s, rc = %d.\n",
|
pr_err("SELinux: Unable to map context %s, rc = %d.\n",
|
||||||
c->str, -rc);
|
oldc->str, -rc);
|
||||||
goto out;
|
return rc;
|
||||||
}
|
}
|
||||||
|
pr_info("SELinux: Context %s became valid (mapped).\n",
|
||||||
|
oldc->str);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = context_cpy(&oldc, c);
|
context_init(newc);
|
||||||
if (rc)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* Convert the user. */
|
/* Convert the user. */
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
usrdatum = hashtab_search(args->newp->p_users.table,
|
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)
|
if (!usrdatum)
|
||||||
goto bad;
|
goto bad;
|
||||||
c->user = usrdatum->value;
|
newc->user = usrdatum->value;
|
||||||
|
|
||||||
/* Convert the role. */
|
/* Convert the role. */
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
role = hashtab_search(args->newp->p_roles.table,
|
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)
|
if (!role)
|
||||||
goto bad;
|
goto bad;
|
||||||
c->role = role->value;
|
newc->role = role->value;
|
||||||
|
|
||||||
/* Convert the type. */
|
/* Convert the type. */
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
typdatum = hashtab_search(args->newp->p_types.table,
|
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)
|
if (!typdatum)
|
||||||
goto bad;
|
goto bad;
|
||||||
c->type = typdatum->value;
|
newc->type = typdatum->value;
|
||||||
|
|
||||||
/* Convert the MLS fields if dealing with MLS policies */
|
/* Convert the MLS fields if dealing with MLS policies */
|
||||||
if (args->oldp->mls_enabled && args->newp->mls_enabled) {
|
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)
|
if (rc)
|
||||||
goto bad;
|
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) {
|
} else if (!args->oldp->mls_enabled && args->newp->mls_enabled) {
|
||||||
/*
|
/*
|
||||||
* Switching between non-MLS and MLS policy:
|
* Switching between non-MLS and MLS policy:
|
||||||
|
@ -2032,38 +2002,30 @@ static int convert_context(u32 key,
|
||||||
" the initial SIDs list\n");
|
" the initial SIDs list\n");
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
range = &oc->context[0].range;
|
rc = mls_range_set(newc, &oc->context[0].range);
|
||||||
rc = mls_range_set(c, range);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check the validity of the new context. */
|
/* Check the validity of the new context. */
|
||||||
if (!policydb_context_isvalid(args->newp, c)) {
|
if (!policydb_context_isvalid(args->newp, newc)) {
|
||||||
rc = convert_context_handle_invalid_context(args->state,
|
rc = convert_context_handle_invalid_context(args->state, oldc);
|
||||||
&oldc);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
context_destroy(&oldc);
|
return 0;
|
||||||
|
|
||||||
rc = 0;
|
|
||||||
out:
|
|
||||||
return rc;
|
|
||||||
bad:
|
bad:
|
||||||
/* Map old representation to string and save it. */
|
/* 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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
context_destroy(&oldc);
|
context_destroy(newc);
|
||||||
context_destroy(c);
|
newc->str = s;
|
||||||
c->str = s;
|
newc->len = len;
|
||||||
c->len = len;
|
|
||||||
pr_info("SELinux: Context %s became invalid (unmapped).\n",
|
pr_info("SELinux: Context %s became invalid (unmapped).\n",
|
||||||
c->str);
|
newc->str);
|
||||||
rc = 0;
|
return 0;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void security_load_policycaps(struct selinux_state *state)
|
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)
|
int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
{
|
{
|
||||||
struct policydb *policydb;
|
struct policydb *policydb;
|
||||||
struct sidtab *sidtab;
|
struct sidtab *oldsidtab, *newsidtab;
|
||||||
struct policydb *oldpolicydb, *newpolicydb;
|
struct policydb *oldpolicydb, *newpolicydb;
|
||||||
struct sidtab oldsidtab, newsidtab;
|
|
||||||
struct selinux_mapping *oldmapping;
|
struct selinux_mapping *oldmapping;
|
||||||
struct selinux_map newmap;
|
struct selinux_map newmap;
|
||||||
|
struct sidtab_convert_params convert_params;
|
||||||
struct convert_context_args args;
|
struct convert_context_args args;
|
||||||
u32 seqno;
|
u32 seqno;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
@ -2121,27 +2083,37 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
newpolicydb = oldpolicydb + 1;
|
newpolicydb = oldpolicydb + 1;
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
|
||||||
|
newsidtab = kmalloc(sizeof(*newsidtab), GFP_KERNEL);
|
||||||
|
if (!newsidtab) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (!state->initialized) {
|
if (!state->initialized) {
|
||||||
rc = policydb_read(policydb, fp);
|
rc = policydb_read(policydb, fp);
|
||||||
if (rc)
|
if (rc) {
|
||||||
|
kfree(newsidtab);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
policydb->len = len;
|
policydb->len = len;
|
||||||
rc = selinux_set_mapping(policydb, secclass_map,
|
rc = selinux_set_mapping(policydb, secclass_map,
|
||||||
&state->ss->map);
|
&state->ss->map);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
kfree(newsidtab);
|
||||||
policydb_destroy(policydb);
|
policydb_destroy(policydb);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = policydb_load_isids(policydb, sidtab);
|
rc = policydb_load_isids(policydb, newsidtab);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
kfree(newsidtab);
|
||||||
policydb_destroy(policydb);
|
policydb_destroy(policydb);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state->ss->sidtab = newsidtab;
|
||||||
security_load_policycaps(state);
|
security_load_policycaps(state);
|
||||||
state->initialized = 1;
|
state->initialized = 1;
|
||||||
seqno = ++state->ss->latest_granting;
|
seqno = ++state->ss->latest_granting;
|
||||||
|
@ -2154,13 +2126,11 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
sidtab_hash_eval(sidtab, "sids");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
rc = policydb_read(newpolicydb, fp);
|
rc = policydb_read(newpolicydb, fp);
|
||||||
if (rc)
|
if (rc) {
|
||||||
|
kfree(newsidtab);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
newpolicydb->len = len;
|
newpolicydb->len = len;
|
||||||
/* If switching between different policy types, log MLS status */
|
/* 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)
|
else if (!policydb->mls_enabled && newpolicydb->mls_enabled)
|
||||||
pr_info("SELinux: Enabling MLS support...\n");
|
pr_info("SELinux: Enabling MLS support...\n");
|
||||||
|
|
||||||
rc = policydb_load_isids(newpolicydb, &newsidtab);
|
rc = policydb_load_isids(newpolicydb, newsidtab);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("SELinux: unable to load the initial SIDs\n");
|
pr_err("SELinux: unable to load the initial SIDs\n");
|
||||||
policydb_destroy(newpolicydb);
|
policydb_destroy(newpolicydb);
|
||||||
|
kfree(newsidtab);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2186,12 +2157,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clone the SID table. */
|
oldsidtab = state->ss->sidtab;
|
||||||
sidtab_shutdown(sidtab);
|
|
||||||
|
|
||||||
rc = sidtab_map(sidtab, clone_sid, &newsidtab);
|
|
||||||
if (rc)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert the internal representations of contexts
|
* Convert the internal representations of contexts
|
||||||
|
@ -2200,7 +2166,12 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
args.state = state;
|
args.state = state;
|
||||||
args.oldp = policydb;
|
args.oldp = policydb;
|
||||||
args.newp = newpolicydb;
|
args.newp = newpolicydb;
|
||||||
rc = sidtab_map(&newsidtab, convert_context, &args);
|
|
||||||
|
convert_params.func = convert_context;
|
||||||
|
convert_params.args = &args;
|
||||||
|
convert_params.target = newsidtab;
|
||||||
|
|
||||||
|
rc = sidtab_convert(oldsidtab, &convert_params);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("SELinux: unable to convert the internal"
|
pr_err("SELinux: unable to convert the internal"
|
||||||
" representation of contexts in the new SID"
|
" representation of contexts in the new SID"
|
||||||
|
@ -2210,12 +2181,11 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
|
|
||||||
/* Save the old policydb and SID table to free later. */
|
/* Save the old policydb and SID table to free later. */
|
||||||
memcpy(oldpolicydb, policydb, sizeof(*policydb));
|
memcpy(oldpolicydb, policydb, sizeof(*policydb));
|
||||||
sidtab_set(&oldsidtab, sidtab);
|
|
||||||
|
|
||||||
/* Install the new policydb and SID table. */
|
/* Install the new policydb and SID table. */
|
||||||
write_lock_irq(&state->ss->policy_rwlock);
|
write_lock_irq(&state->ss->policy_rwlock);
|
||||||
memcpy(policydb, newpolicydb, sizeof(*policydb));
|
memcpy(policydb, newpolicydb, sizeof(*policydb));
|
||||||
sidtab_set(sidtab, &newsidtab);
|
state->ss->sidtab = newsidtab;
|
||||||
security_load_policycaps(state);
|
security_load_policycaps(state);
|
||||||
oldmapping = state->ss->map.mapping;
|
oldmapping = state->ss->map.mapping;
|
||||||
state->ss->map.mapping = newmap.mapping;
|
state->ss->map.mapping = newmap.mapping;
|
||||||
|
@ -2225,7 +2195,8 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
|
|
||||||
/* Free the old policydb and SID table. */
|
/* Free the old policydb and SID table. */
|
||||||
policydb_destroy(oldpolicydb);
|
policydb_destroy(oldpolicydb);
|
||||||
sidtab_destroy(&oldsidtab);
|
sidtab_destroy(oldsidtab);
|
||||||
|
kfree(oldsidtab);
|
||||||
kfree(oldmapping);
|
kfree(oldmapping);
|
||||||
|
|
||||||
avc_ss_reset(state->avc, seqno);
|
avc_ss_reset(state->avc, seqno);
|
||||||
|
@ -2239,7 +2210,8 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
|
||||||
|
|
||||||
err:
|
err:
|
||||||
kfree(newmap.mapping);
|
kfree(newmap.mapping);
|
||||||
sidtab_destroy(&newsidtab);
|
sidtab_destroy(newsidtab);
|
||||||
|
kfree(newsidtab);
|
||||||
policydb_destroy(newpolicydb);
|
policydb_destroy(newpolicydb);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -2276,7 +2248,7 @@ int security_port_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_PORT];
|
c = policydb->ocontexts[OCON_PORT];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2322,7 +2294,7 @@ int security_ib_pkey_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_IBPKEY];
|
c = policydb->ocontexts[OCON_IBPKEY];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2368,7 +2340,7 @@ int security_ib_endport_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_IBENDPORT];
|
c = policydb->ocontexts[OCON_IBENDPORT];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2414,7 +2386,7 @@ int security_netif_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_NETIF];
|
c = policydb->ocontexts[OCON_NETIF];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2479,7 +2451,7 @@ int security_node_sid(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case AF_INET: {
|
case AF_INET: {
|
||||||
|
@ -2579,7 +2551,7 @@ int security_get_user_sids(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
context_init(&usercon);
|
context_init(&usercon);
|
||||||
|
|
||||||
|
@ -2681,7 +2653,7 @@ static inline int __security_genfs_sid(struct selinux_state *state,
|
||||||
u32 *sid)
|
u32 *sid)
|
||||||
{
|
{
|
||||||
struct policydb *policydb = &state->ss->policydb;
|
struct policydb *policydb = &state->ss->policydb;
|
||||||
struct sidtab *sidtab = &state->ss->sidtab;
|
struct sidtab *sidtab = state->ss->sidtab;
|
||||||
int len;
|
int len;
|
||||||
u16 sclass;
|
u16 sclass;
|
||||||
struct genfs *genfs;
|
struct genfs *genfs;
|
||||||
|
@ -2767,7 +2739,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
policydb = &state->ss->policydb;
|
policydb = &state->ss->policydb;
|
||||||
sidtab = &state->ss->sidtab;
|
sidtab = state->ss->sidtab;
|
||||||
|
|
||||||
c = policydb->ocontexts[OCON_FSUSE];
|
c = policydb->ocontexts[OCON_FSUSE];
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -2973,7 +2945,7 @@ int security_sid_mls_copy(struct selinux_state *state,
|
||||||
u32 sid, u32 mls_sid, u32 *new_sid)
|
u32 sid, u32 mls_sid, u32 *new_sid)
|
||||||
{
|
{
|
||||||
struct policydb *policydb = &state->ss->policydb;
|
struct policydb *policydb = &state->ss->policydb;
|
||||||
struct sidtab *sidtab = &state->ss->sidtab;
|
struct sidtab *sidtab = state->ss->sidtab;
|
||||||
struct context *context1;
|
struct context *context1;
|
||||||
struct context *context2;
|
struct context *context2;
|
||||||
struct context newcon;
|
struct context newcon;
|
||||||
|
@ -3064,7 +3036,7 @@ int security_net_peersid_resolve(struct selinux_state *state,
|
||||||
u32 *peer_sid)
|
u32 *peer_sid)
|
||||||
{
|
{
|
||||||
struct policydb *policydb = &state->ss->policydb;
|
struct policydb *policydb = &state->ss->policydb;
|
||||||
struct sidtab *sidtab = &state->ss->sidtab;
|
struct sidtab *sidtab = state->ss->sidtab;
|
||||||
int rc;
|
int rc;
|
||||||
struct context *nlbl_ctx;
|
struct context *nlbl_ctx;
|
||||||
struct context *xfrm_ctx;
|
struct context *xfrm_ctx;
|
||||||
|
@ -3425,7 +3397,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxt = sidtab_search(&state->ss->sidtab, sid);
|
ctxt = sidtab_search(state->ss->sidtab, sid);
|
||||||
if (unlikely(!ctxt)) {
|
if (unlikely(!ctxt)) {
|
||||||
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
|
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
|
||||||
sid);
|
sid);
|
||||||
|
@ -3588,7 +3560,7 @@ int security_netlbl_secattr_to_sid(struct selinux_state *state,
|
||||||
u32 *sid)
|
u32 *sid)
|
||||||
{
|
{
|
||||||
struct policydb *policydb = &state->ss->policydb;
|
struct policydb *policydb = &state->ss->policydb;
|
||||||
struct sidtab *sidtab = &state->ss->sidtab;
|
struct sidtab *sidtab = state->ss->sidtab;
|
||||||
int rc;
|
int rc;
|
||||||
struct context *ctx;
|
struct context *ctx;
|
||||||
struct context ctx_new;
|
struct context ctx_new;
|
||||||
|
@ -3666,7 +3638,7 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state,
|
||||||
read_lock(&state->ss->policy_rwlock);
|
read_lock(&state->ss->policy_rwlock);
|
||||||
|
|
||||||
rc = -ENOENT;
|
rc = -ENOENT;
|
||||||
ctx = sidtab_search(&state->ss->sidtab, sid);
|
ctx = sidtab_search(state->ss->sidtab, sid);
|
||||||
if (ctx == NULL)
|
if (ctx == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ struct selinux_map {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct selinux_ss {
|
struct selinux_ss {
|
||||||
struct sidtab sidtab;
|
struct sidtab *sidtab;
|
||||||
struct policydb policydb;
|
struct policydb policydb;
|
||||||
rwlock_t policy_rwlock;
|
rwlock_t policy_rwlock;
|
||||||
u32 latest_granting;
|
u32 latest_granting;
|
||||||
|
|
|
@ -2,108 +2,164 @@
|
||||||
/*
|
/*
|
||||||
* Implementation of the SID table type.
|
* Implementation of the SID table type.
|
||||||
*
|
*
|
||||||
* Author : Stephen Smalley, <sds@tycho.nsa.gov>
|
* Original author: Stephen Smalley, <sds@tycho.nsa.gov>
|
||||||
|
* Author: Ondrej Mosnacek, <omosnacek@gmail.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Red Hat, Inc.
|
||||||
*/
|
*/
|
||||||
|
#include <linux/errno.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/atomic.h>
|
||||||
#include "flask.h"
|
#include "flask.h"
|
||||||
#include "security.h"
|
#include "security.h"
|
||||||
#include "sidtab.h"
|
#include "sidtab.h"
|
||||||
|
|
||||||
#define SIDTAB_HASH(sid) \
|
|
||||||
(sid & SIDTAB_HASH_MASK)
|
|
||||||
|
|
||||||
int sidtab_init(struct sidtab *s)
|
int sidtab_init(struct sidtab *s)
|
||||||
{
|
{
|
||||||
int i;
|
u32 i;
|
||||||
|
|
||||||
|
memset(s->roots, 0, sizeof(s->roots));
|
||||||
|
|
||||||
|
for (i = 0; i < SIDTAB_RCACHE_SIZE; i++)
|
||||||
|
atomic_set(&s->rcache[i], -1);
|
||||||
|
|
||||||
|
for (i = 0; i < SECINITSID_NUM; i++)
|
||||||
|
s->isids[i].set = 0;
|
||||||
|
|
||||||
|
atomic_set(&s->count, 0);
|
||||||
|
|
||||||
|
s->convert = NULL;
|
||||||
|
|
||||||
s->htable = kmalloc_array(SIDTAB_SIZE, sizeof(*s->htable), GFP_ATOMIC);
|
|
||||||
if (!s->htable)
|
|
||||||
return -ENOMEM;
|
|
||||||
for (i = 0; i < SIDTAB_SIZE; i++)
|
|
||||||
s->htable[i] = NULL;
|
|
||||||
s->nel = 0;
|
|
||||||
s->next_sid = 1;
|
|
||||||
s->shutdown = 0;
|
|
||||||
spin_lock_init(&s->lock);
|
spin_lock_init(&s->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
|
int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
|
||||||
{
|
{
|
||||||
int hvalue;
|
struct sidtab_isid_entry *entry;
|
||||||
struct sidtab_node *prev, *cur, *newnode;
|
int rc;
|
||||||
|
|
||||||
if (!s)
|
if (sid == 0 || sid > SECINITSID_NUM)
|
||||||
return -ENOMEM;
|
return -EINVAL;
|
||||||
|
|
||||||
hvalue = SIDTAB_HASH(sid);
|
entry = &s->isids[sid - 1];
|
||||||
prev = NULL;
|
|
||||||
cur = s->htable[hvalue];
|
|
||||||
while (cur && sid > cur->sid) {
|
|
||||||
prev = cur;
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur && sid == cur->sid)
|
rc = context_cpy(&entry->context, context);
|
||||||
return -EEXIST;
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
|
entry->set = 1;
|
||||||
if (!newnode)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
newnode->sid = sid;
|
|
||||||
if (context_cpy(&newnode->context, context)) {
|
|
||||||
kfree(newnode);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev) {
|
|
||||||
newnode->next = prev->next;
|
|
||||||
wmb();
|
|
||||||
prev->next = newnode;
|
|
||||||
} else {
|
|
||||||
newnode->next = s->htable[hvalue];
|
|
||||||
wmb();
|
|
||||||
s->htable[hvalue] = newnode;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->nel++;
|
|
||||||
if (sid >= s->next_sid)
|
|
||||||
s->next_sid = sid + 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 sidtab_level_from_count(u32 count)
|
||||||
|
{
|
||||||
|
u32 capacity = SIDTAB_LEAF_ENTRIES;
|
||||||
|
u32 level = 0;
|
||||||
|
|
||||||
|
while (count > capacity) {
|
||||||
|
capacity <<= SIDTAB_INNER_SHIFT;
|
||||||
|
++level;
|
||||||
|
}
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sidtab_alloc_roots(struct sidtab *s, u32 level)
|
||||||
|
{
|
||||||
|
u32 l;
|
||||||
|
|
||||||
|
if (!s->roots[0].ptr_leaf) {
|
||||||
|
s->roots[0].ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (!s->roots[0].ptr_leaf)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
for (l = 1; l <= level; ++l)
|
||||||
|
if (!s->roots[l].ptr_inner) {
|
||||||
|
s->roots[l].ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (!s->roots[l].ptr_inner)
|
||||||
|
return -ENOMEM;
|
||||||
|
s->roots[l].ptr_inner->entries[0] = s->roots[l - 1];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc)
|
||||||
|
{
|
||||||
|
union sidtab_entry_inner *entry;
|
||||||
|
u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES;
|
||||||
|
|
||||||
|
/* find the level of the subtree we need */
|
||||||
|
level = sidtab_level_from_count(index + 1);
|
||||||
|
capacity_shift = level * SIDTAB_INNER_SHIFT;
|
||||||
|
|
||||||
|
/* allocate roots if needed */
|
||||||
|
if (alloc && sidtab_alloc_roots(s, level) != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* lookup inside the subtree */
|
||||||
|
entry = &s->roots[level];
|
||||||
|
while (level != 0) {
|
||||||
|
capacity_shift -= SIDTAB_INNER_SHIFT;
|
||||||
|
--level;
|
||||||
|
|
||||||
|
entry = &entry->ptr_inner->entries[leaf_index >> capacity_shift];
|
||||||
|
leaf_index &= ((u32)1 << capacity_shift) - 1;
|
||||||
|
|
||||||
|
if (!entry->ptr_inner) {
|
||||||
|
if (alloc)
|
||||||
|
entry->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (!entry->ptr_inner)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!entry->ptr_leaf) {
|
||||||
|
if (alloc)
|
||||||
|
entry->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (!entry->ptr_leaf)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES].context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct context *sidtab_lookup(struct sidtab *s, u32 index)
|
||||||
|
{
|
||||||
|
u32 count = (u32)atomic_read(&s->count);
|
||||||
|
|
||||||
|
if (index >= count)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* read entries after reading count */
|
||||||
|
smp_rmb();
|
||||||
|
|
||||||
|
return sidtab_do_lookup(s, index, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid)
|
||||||
|
{
|
||||||
|
return s->isids[sid - 1].set ? &s->isids[sid - 1].context : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
|
static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
|
||||||
{
|
{
|
||||||
int hvalue;
|
struct context *context;
|
||||||
struct sidtab_node *cur;
|
|
||||||
|
|
||||||
if (!s)
|
if (sid != 0) {
|
||||||
return NULL;
|
if (sid > SECINITSID_NUM)
|
||||||
|
context = sidtab_lookup(s, sid - (SECINITSID_NUM + 1));
|
||||||
hvalue = SIDTAB_HASH(sid);
|
else
|
||||||
cur = s->htable[hvalue];
|
context = sidtab_lookup_initial(s, sid);
|
||||||
while (cur && sid > cur->sid)
|
if (context && (!context->len || force))
|
||||||
cur = cur->next;
|
return context;
|
||||||
|
|
||||||
if (force && cur && sid == cur->sid && cur->context.len)
|
|
||||||
return &cur->context;
|
|
||||||
|
|
||||||
if (!cur || sid != cur->sid || cur->context.len) {
|
|
||||||
/* Remap invalid SIDs to the unlabeled SID. */
|
|
||||||
sid = SECINITSID_UNLABELED;
|
|
||||||
hvalue = SIDTAB_HASH(sid);
|
|
||||||
cur = s->htable[hvalue];
|
|
||||||
while (cur && sid > cur->sid)
|
|
||||||
cur = cur->next;
|
|
||||||
if (!cur || sid != cur->sid)
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &cur->context;
|
return sidtab_lookup_initial(s, SECINITSID_UNLABELED);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct context *sidtab_search(struct sidtab *s, u32 sid)
|
struct context *sidtab_search(struct sidtab *s, u32 sid)
|
||||||
|
@ -116,191 +172,324 @@ struct context *sidtab_search_force(struct sidtab *s, u32 sid)
|
||||||
return sidtab_search_core(s, sid, 1);
|
return sidtab_search_core(s, sid, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sidtab_map(struct sidtab *s,
|
static int sidtab_find_context(union sidtab_entry_inner entry,
|
||||||
int (*apply) (u32 sid,
|
u32 *pos, u32 count, u32 level,
|
||||||
struct context *context,
|
struct context *context, u32 *index)
|
||||||
void *args),
|
|
||||||
void *args)
|
|
||||||
{
|
{
|
||||||
int i, rc = 0;
|
int rc;
|
||||||
struct sidtab_node *cur;
|
u32 i;
|
||||||
|
|
||||||
if (!s)
|
if (level != 0) {
|
||||||
goto out;
|
struct sidtab_node_inner *node = entry.ptr_inner;
|
||||||
|
|
||||||
for (i = 0; i < SIDTAB_SIZE; i++) {
|
i = 0;
|
||||||
cur = s->htable[i];
|
while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
|
||||||
while (cur) {
|
rc = sidtab_find_context(node->entries[i],
|
||||||
rc = apply(cur->sid, &cur->context, args);
|
pos, count, level - 1,
|
||||||
if (rc)
|
context, index);
|
||||||
goto out;
|
if (rc == 0)
|
||||||
cur = cur->next;
|
return 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct sidtab_node_leaf *node = entry.ptr_leaf;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
|
||||||
|
if (context_cmp(&node->entries[i].context, context)) {
|
||||||
|
*index = *pos;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
(*pos)++;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out:
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos)
|
||||||
|
{
|
||||||
|
while (pos > 0) {
|
||||||
|
atomic_set(&s->rcache[pos], atomic_read(&s->rcache[pos - 1]));
|
||||||
|
--pos;
|
||||||
|
}
|
||||||
|
atomic_set(&s->rcache[0], (int)index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sidtab_rcache_push(struct sidtab *s, u32 index)
|
||||||
|
{
|
||||||
|
sidtab_rcache_update(s, index, SIDTAB_RCACHE_SIZE - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sidtab_rcache_search(struct sidtab *s, struct context *context,
|
||||||
|
u32 *index)
|
||||||
|
{
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) {
|
||||||
|
int v = atomic_read(&s->rcache[i]);
|
||||||
|
|
||||||
|
if (v < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (context_cmp(sidtab_do_lookup(s, (u32)v, 0), context)) {
|
||||||
|
sidtab_rcache_update(s, (u32)v, i);
|
||||||
|
*index = (u32)v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
|
||||||
|
u32 *index)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
u32 count = (u32)atomic_read(&s->count);
|
||||||
|
u32 count_locked, level, pos;
|
||||||
|
struct sidtab_convert_params *convert;
|
||||||
|
struct context *dst, *dst_convert;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = sidtab_rcache_search(s, context, index);
|
||||||
|
if (rc == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
level = sidtab_level_from_count(count);
|
||||||
|
|
||||||
|
/* read entries after reading count */
|
||||||
|
smp_rmb();
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
rc = sidtab_find_context(s->roots[level], &pos, count, level,
|
||||||
|
context, index);
|
||||||
|
if (rc == 0) {
|
||||||
|
sidtab_rcache_push(s, *index);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lock-free search failed: lock, re-search, and insert if not found */
|
||||||
|
spin_lock_irqsave(&s->lock, flags);
|
||||||
|
|
||||||
|
convert = s->convert;
|
||||||
|
count_locked = (u32)atomic_read(&s->count);
|
||||||
|
level = sidtab_level_from_count(count_locked);
|
||||||
|
|
||||||
|
/* if count has changed before we acquired the lock, then catch up */
|
||||||
|
while (count < count_locked) {
|
||||||
|
if (context_cmp(sidtab_do_lookup(s, count, 0), context)) {
|
||||||
|
sidtab_rcache_push(s, count);
|
||||||
|
*index = count;
|
||||||
|
rc = 0;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* insert context into new entry */
|
||||||
|
rc = -ENOMEM;
|
||||||
|
dst = sidtab_do_lookup(s, count, 1);
|
||||||
|
if (!dst)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
rc = context_cpy(dst, context);
|
||||||
|
if (rc)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we are building a new sidtab, we need to convert the context
|
||||||
|
* and insert it there as well
|
||||||
|
*/
|
||||||
|
if (convert) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
dst_convert = sidtab_do_lookup(convert->target, count, 1);
|
||||||
|
if (!dst_convert) {
|
||||||
|
context_destroy(dst);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = convert->func(context, dst_convert, convert->args);
|
||||||
|
if (rc) {
|
||||||
|
context_destroy(dst);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* at this point we know the insert won't fail */
|
||||||
|
atomic_set(&convert->target->count, count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->len)
|
||||||
|
pr_info("SELinux: Context %s is not valid (left unmapped).\n",
|
||||||
|
context->str);
|
||||||
|
|
||||||
|
sidtab_rcache_push(s, count);
|
||||||
|
*index = count;
|
||||||
|
|
||||||
|
/* write entries before writing new count */
|
||||||
|
smp_wmb();
|
||||||
|
|
||||||
|
atomic_set(&s->count, count + 1);
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
out_unlock:
|
||||||
|
spin_unlock_irqrestore(&s->lock, flags);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc)
|
int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid)
|
||||||
{
|
{
|
||||||
BUG_ON(loc >= SIDTAB_CACHE_LEN);
|
int rc;
|
||||||
|
u32 i;
|
||||||
|
|
||||||
while (loc > 0) {
|
for (i = 0; i < SECINITSID_NUM; i++) {
|
||||||
s->cache[loc] = s->cache[loc - 1];
|
struct sidtab_isid_entry *entry = &s->isids[i];
|
||||||
loc--;
|
|
||||||
}
|
|
||||||
s->cache[0] = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 sidtab_search_context(struct sidtab *s,
|
if (entry->set && context_cmp(context, &entry->context)) {
|
||||||
struct context *context)
|
*sid = i + 1;
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct sidtab_node *cur;
|
|
||||||
|
|
||||||
for (i = 0; i < SIDTAB_SIZE; i++) {
|
|
||||||
cur = s->htable[i];
|
|
||||||
while (cur) {
|
|
||||||
if (context_cmp(&cur->context, context)) {
|
|
||||||
sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1);
|
|
||||||
return cur->sid;
|
|
||||||
}
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct sidtab_node *node;
|
|
||||||
|
|
||||||
for (i = 0; i < SIDTAB_CACHE_LEN; i++) {
|
|
||||||
node = s->cache[i];
|
|
||||||
if (unlikely(!node))
|
|
||||||
return 0;
|
return 0;
|
||||||
if (context_cmp(&node->context, context)) {
|
|
||||||
sidtab_update_cache(s, node, i);
|
|
||||||
return node->sid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc = sidtab_reverse_lookup(s, context, sid);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
*sid += SECINITSID_NUM + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sidtab_convert_tree(union sidtab_entry_inner *edst,
|
||||||
|
union sidtab_entry_inner *esrc,
|
||||||
|
u32 *pos, u32 count, u32 level,
|
||||||
|
struct sidtab_convert_params *convert)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
if (level != 0) {
|
||||||
|
if (!edst->ptr_inner) {
|
||||||
|
edst->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!edst->ptr_inner)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
|
||||||
|
rc = sidtab_convert_tree(&edst->ptr_inner->entries[i],
|
||||||
|
&esrc->ptr_inner->entries[i],
|
||||||
|
pos, count, level - 1,
|
||||||
|
convert);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!edst->ptr_leaf) {
|
||||||
|
edst->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!edst->ptr_leaf)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
|
||||||
|
rc = convert->func(&esrc->ptr_leaf->entries[i].context,
|
||||||
|
&edst->ptr_leaf->entries[i].context,
|
||||||
|
convert->args);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
(*pos)++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
cond_resched();
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sidtab_context_to_sid(struct sidtab *s,
|
int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
|
||||||
struct context *context,
|
|
||||||
u32 *out_sid)
|
|
||||||
{
|
{
|
||||||
u32 sid;
|
|
||||||
int ret = 0;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
u32 count, level, pos;
|
||||||
|
int rc;
|
||||||
|
|
||||||
*out_sid = SECSID_NULL;
|
spin_lock_irqsave(&s->lock, flags);
|
||||||
|
|
||||||
sid = sidtab_search_cache(s, context);
|
/* concurrent policy loads are not allowed */
|
||||||
if (!sid)
|
if (s->convert) {
|
||||||
sid = sidtab_search_context(s, context);
|
spin_unlock_irqrestore(&s->lock, flags);
|
||||||
if (!sid) {
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = (u32)atomic_read(&s->count);
|
||||||
|
level = sidtab_level_from_count(count);
|
||||||
|
|
||||||
|
/* allocate last leaf in the new sidtab (to avoid race with
|
||||||
|
* live convert)
|
||||||
|
*/
|
||||||
|
rc = sidtab_do_lookup(params->target, count - 1, 1) ? 0 : -ENOMEM;
|
||||||
|
if (rc) {
|
||||||
|
spin_unlock_irqrestore(&s->lock, flags);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set count in case no new entries are added during conversion */
|
||||||
|
atomic_set(¶ms->target->count, count);
|
||||||
|
|
||||||
|
/* enable live convert of new entries */
|
||||||
|
s->convert = params;
|
||||||
|
|
||||||
|
/* we can safely do the rest of the conversion outside the lock */
|
||||||
|
spin_unlock_irqrestore(&s->lock, flags);
|
||||||
|
|
||||||
|
pr_info("SELinux: Converting %u SID table entries...\n", count);
|
||||||
|
|
||||||
|
/* convert all entries not covered by live convert */
|
||||||
|
pos = 0;
|
||||||
|
rc = sidtab_convert_tree(¶ms->target->roots[level],
|
||||||
|
&s->roots[level], &pos, count, level, params);
|
||||||
|
if (rc) {
|
||||||
|
/* we need to keep the old table - disable live convert */
|
||||||
spin_lock_irqsave(&s->lock, flags);
|
spin_lock_irqsave(&s->lock, flags);
|
||||||
/* Rescan now that we hold the lock. */
|
s->convert = NULL;
|
||||||
sid = sidtab_search_context(s, context);
|
|
||||||
if (sid)
|
|
||||||
goto unlock_out;
|
|
||||||
/* No SID exists for the context. Allocate a new one. */
|
|
||||||
if (s->next_sid == UINT_MAX || s->shutdown) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto unlock_out;
|
|
||||||
}
|
|
||||||
sid = s->next_sid++;
|
|
||||||
if (context->len)
|
|
||||||
pr_info("SELinux: Context %s is not valid (left unmapped).\n",
|
|
||||||
context->str);
|
|
||||||
ret = sidtab_insert(s, sid, context);
|
|
||||||
if (ret)
|
|
||||||
s->next_sid--;
|
|
||||||
unlock_out:
|
|
||||||
spin_unlock_irqrestore(&s->lock, flags);
|
spin_unlock_irqrestore(&s->lock, flags);
|
||||||
}
|
}
|
||||||
|
return rc;
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
*out_sid = sid;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sidtab_hash_eval(struct sidtab *h, char *tag)
|
static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
|
||||||
{
|
{
|
||||||
int i, chain_len, slots_used, max_chain_len;
|
u32 i;
|
||||||
struct sidtab_node *cur;
|
|
||||||
|
|
||||||
slots_used = 0;
|
if (level != 0) {
|
||||||
max_chain_len = 0;
|
struct sidtab_node_inner *node = entry.ptr_inner;
|
||||||
for (i = 0; i < SIDTAB_SIZE; i++) {
|
|
||||||
cur = h->htable[i];
|
|
||||||
if (cur) {
|
|
||||||
slots_used++;
|
|
||||||
chain_len = 0;
|
|
||||||
while (cur) {
|
|
||||||
chain_len++;
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain_len > max_chain_len)
|
if (!node)
|
||||||
max_chain_len = chain_len;
|
return;
|
||||||
}
|
|
||||||
|
for (i = 0; i < SIDTAB_INNER_ENTRIES; i++)
|
||||||
|
sidtab_destroy_tree(node->entries[i], level - 1);
|
||||||
|
kfree(node);
|
||||||
|
} else {
|
||||||
|
struct sidtab_node_leaf *node = entry.ptr_leaf;
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++)
|
||||||
|
context_destroy(&node->entries[i].context);
|
||||||
|
kfree(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_debug("%s: %d entries and %d/%d buckets used, longest "
|
|
||||||
"chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE,
|
|
||||||
max_chain_len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sidtab_destroy(struct sidtab *s)
|
void sidtab_destroy(struct sidtab *s)
|
||||||
{
|
{
|
||||||
int i;
|
u32 i, level;
|
||||||
struct sidtab_node *cur, *temp;
|
|
||||||
|
|
||||||
if (!s)
|
for (i = 0; i < SECINITSID_NUM; i++)
|
||||||
return;
|
if (s->isids[i].set)
|
||||||
|
context_destroy(&s->isids[i].context);
|
||||||
|
|
||||||
for (i = 0; i < SIDTAB_SIZE; i++) {
|
level = SIDTAB_MAX_LEVEL;
|
||||||
cur = s->htable[i];
|
while (level && !s->roots[level].ptr_inner)
|
||||||
while (cur) {
|
--level;
|
||||||
temp = cur;
|
|
||||||
cur = cur->next;
|
sidtab_destroy_tree(s->roots[level], level);
|
||||||
context_destroy(&temp->context);
|
|
||||||
kfree(temp);
|
|
||||||
}
|
|
||||||
s->htable[i] = NULL;
|
|
||||||
}
|
|
||||||
kfree(s->htable);
|
|
||||||
s->htable = NULL;
|
|
||||||
s->nel = 0;
|
|
||||||
s->next_sid = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sidtab_set(struct sidtab *dst, struct sidtab *src)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&src->lock, flags);
|
|
||||||
dst->htable = src->htable;
|
|
||||||
dst->nel = src->nel;
|
|
||||||
dst->next_sid = src->next_sid;
|
|
||||||
dst->shutdown = 0;
|
|
||||||
for (i = 0; i < SIDTAB_CACHE_LEN; i++)
|
|
||||||
dst->cache[i] = NULL;
|
|
||||||
spin_unlock_irqrestore(&src->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sidtab_shutdown(struct sidtab *s)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&s->lock, flags);
|
|
||||||
s->shutdown = 1;
|
|
||||||
spin_unlock_irqrestore(&s->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +1,96 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
/*
|
/*
|
||||||
* A security identifier table (sidtab) is a hash table
|
* A security identifier table (sidtab) is a lookup table
|
||||||
* of security context structures indexed by SID value.
|
* of security context structures indexed by SID value.
|
||||||
*
|
*
|
||||||
* Author : Stephen Smalley, <sds@tycho.nsa.gov>
|
* Original author: Stephen Smalley, <sds@tycho.nsa.gov>
|
||||||
|
* Author: Ondrej Mosnacek, <omosnacek@gmail.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Red Hat, Inc.
|
||||||
*/
|
*/
|
||||||
#ifndef _SS_SIDTAB_H_
|
#ifndef _SS_SIDTAB_H_
|
||||||
#define _SS_SIDTAB_H_
|
#define _SS_SIDTAB_H_
|
||||||
|
|
||||||
|
#include <linux/spinlock_types.h>
|
||||||
|
#include <linux/log2.h>
|
||||||
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
|
|
||||||
struct sidtab_node {
|
struct sidtab_entry_leaf {
|
||||||
u32 sid; /* security identifier */
|
struct context context;
|
||||||
struct context context; /* security context structure */
|
|
||||||
struct sidtab_node *next;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SIDTAB_HASH_BITS 7
|
struct sidtab_node_inner;
|
||||||
#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
|
struct sidtab_node_leaf;
|
||||||
#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1)
|
|
||||||
|
|
||||||
#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS
|
union sidtab_entry_inner {
|
||||||
|
struct sidtab_node_inner *ptr_inner;
|
||||||
|
struct sidtab_node_leaf *ptr_leaf;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* align node size to page boundary */
|
||||||
|
#define SIDTAB_NODE_ALLOC_SHIFT PAGE_SHIFT
|
||||||
|
#define SIDTAB_NODE_ALLOC_SIZE PAGE_SIZE
|
||||||
|
|
||||||
|
#define size_to_shift(size) ((size) == 1 ? 1 : (const_ilog2((size) - 1) + 1))
|
||||||
|
|
||||||
|
#define SIDTAB_INNER_SHIFT \
|
||||||
|
(SIDTAB_NODE_ALLOC_SHIFT - size_to_shift(sizeof(union sidtab_entry_inner)))
|
||||||
|
#define SIDTAB_INNER_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT)
|
||||||
|
#define SIDTAB_LEAF_ENTRIES \
|
||||||
|
(SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf))
|
||||||
|
|
||||||
|
#define SIDTAB_MAX_BITS 31 /* limited to INT_MAX due to atomic_t range */
|
||||||
|
#define SIDTAB_MAX (((u32)1 << SIDTAB_MAX_BITS) - 1)
|
||||||
|
/* ensure enough tree levels for SIDTAB_MAX entries */
|
||||||
|
#define SIDTAB_MAX_LEVEL \
|
||||||
|
DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
|
||||||
|
SIDTAB_INNER_SHIFT)
|
||||||
|
|
||||||
|
struct sidtab_node_leaf {
|
||||||
|
struct sidtab_entry_leaf entries[SIDTAB_LEAF_ENTRIES];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sidtab_node_inner {
|
||||||
|
union sidtab_entry_inner entries[SIDTAB_INNER_ENTRIES];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sidtab_isid_entry {
|
||||||
|
int set;
|
||||||
|
struct context context;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sidtab_convert_params {
|
||||||
|
int (*func)(struct context *oldc, struct context *newc, void *args);
|
||||||
|
void *args;
|
||||||
|
struct sidtab *target;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SIDTAB_RCACHE_SIZE 3
|
||||||
|
|
||||||
struct sidtab {
|
struct sidtab {
|
||||||
struct sidtab_node **htable;
|
union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
|
||||||
unsigned int nel; /* number of elements */
|
atomic_t count;
|
||||||
unsigned int next_sid; /* next SID to allocate */
|
struct sidtab_convert_params *convert;
|
||||||
unsigned char shutdown;
|
|
||||||
#define SIDTAB_CACHE_LEN 3
|
|
||||||
struct sidtab_node *cache[SIDTAB_CACHE_LEN];
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
|
/* reverse lookup cache */
|
||||||
|
atomic_t rcache[SIDTAB_RCACHE_SIZE];
|
||||||
|
|
||||||
|
/* index == SID - 1 (no entry for SECSID_NULL) */
|
||||||
|
struct sidtab_isid_entry isids[SECINITSID_NUM];
|
||||||
};
|
};
|
||||||
|
|
||||||
int sidtab_init(struct sidtab *s);
|
int sidtab_init(struct sidtab *s);
|
||||||
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context);
|
int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context);
|
||||||
struct context *sidtab_search(struct sidtab *s, u32 sid);
|
struct context *sidtab_search(struct sidtab *s, u32 sid);
|
||||||
struct context *sidtab_search_force(struct sidtab *s, u32 sid);
|
struct context *sidtab_search_force(struct sidtab *s, u32 sid);
|
||||||
|
|
||||||
int sidtab_map(struct sidtab *s,
|
int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
|
||||||
int (*apply) (u32 sid,
|
|
||||||
struct context *context,
|
|
||||||
void *args),
|
|
||||||
void *args);
|
|
||||||
|
|
||||||
int sidtab_context_to_sid(struct sidtab *s,
|
int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
|
||||||
struct context *context,
|
|
||||||
u32 *sid);
|
|
||||||
|
|
||||||
void sidtab_hash_eval(struct sidtab *h, char *tag);
|
|
||||||
void sidtab_destroy(struct sidtab *s);
|
void sidtab_destroy(struct sidtab *s);
|
||||||
void sidtab_set(struct sidtab *dst, struct sidtab *src);
|
|
||||||
void sidtab_shutdown(struct sidtab *s);
|
|
||||||
|
|
||||||
#endif /* _SS_SIDTAB_H_ */
|
#endif /* _SS_SIDTAB_H_ */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user