selinux: support deferred mapping of contexts
Introduce SELinux support for deferred mapping of security contexts in the SID table upon policy reload, and use this support for inode security contexts when the context is not yet valid under the current policy. Only processes with CAP_MAC_ADMIN + mac_admin permission in policy can set undefined security contexts on inodes. Inodes with such undefined contexts are treated as having the unlabeled context until the context becomes valid upon a policy reload that defines the context. Context invalidation upon policy reload also uses this support to save the context information in the SID table and later recover it upon a subsequent policy reload that defines the context again. This support is to enable package managers and similar programs to set down file contexts unknown to the system policy at the time the file is created in order to better support placing loadable policy modules in packages and to support build systems that need to create images of different distro releases with different policies w/o requiring all of the contexts to be defined or legal in the build host policy. With this patch applied, the following sequence is possible, although in practice it is recommended that this permission only be allowed to specific program domains such as the package manager. # rmdir baz # rm bar # touch bar # chcon -t foo_exec_t bar # foo_exec_t is not yet defined chcon: failed to change context of `bar' to `system_u:object_r:foo_exec_t': Invalid argument # mkdir -Z system_u:object_r:foo_exec_t baz mkdir: failed to set default file creation context to `system_u:object_r:foo_exec_t': Invalid argument # cat setundefined.te policy_module(setundefined, 1.0) require { type unconfined_t; type unlabeled_t; } files_type(unlabeled_t) allow unconfined_t self:capability2 mac_admin; # make -f /usr/share/selinux/devel/Makefile setundefined.pp # semodule -i setundefined.pp # chcon -t foo_exec_t bar # foo_exec_t is not yet defined # mkdir -Z system_u:object_r:foo_exec_t baz # ls -Zd bar baz -rw-r--r-- root root system_u:object_r:unlabeled_t bar drwxr-xr-x root root system_u:object_r:unlabeled_t baz # cat foo.te policy_module(foo, 1.0) type foo_exec_t; files_type(foo_exec_t) # make -f /usr/share/selinux/devel/Makefile foo.pp # semodule -i foo.pp # defines foo_exec_t # ls -Zd bar baz -rw-r--r-- root root user_u:object_r:foo_exec_t bar drwxr-xr-x root root system_u:object_r:foo_exec_t baz # semodule -r foo # ls -Zd bar baz -rw-r--r-- root root system_u:object_r:unlabeled_t bar drwxr-xr-x root root system_u:object_r:unlabeled_t baz # semodule -i foo.pp # ls -Zd bar baz -rw-r--r-- root root user_u:object_r:foo_exec_t bar drwxr-xr-x root root system_u:object_r:foo_exec_t baz # semodule -r setundefined foo # chcon -t foo_exec_t bar # no longer defined and not allowed chcon: failed to change context of `bar' to `system_u:object_r:foo_exec_t': Invalid argument # rmdir baz # mkdir -Z system_u:object_r:foo_exec_t baz mkdir: failed to set default file creation context to `system_u:object_r:foo_exec_t': Invalid argument Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: James Morris <jmorris@namei.org>
This commit is contained in:
parent
bce7f793da
commit
12b29f3455
|
@ -2495,7 +2495,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value && len) {
|
if (value && len) {
|
||||||
rc = security_sid_to_context(newsid, &context, &clen);
|
rc = security_sid_to_context_force(newsid, &context, &clen);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kfree(namep);
|
kfree(namep);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -2669,6 +2669,11 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = security_context_to_sid(value, size, &newsid);
|
rc = security_context_to_sid(value, size, &newsid);
|
||||||
|
if (rc == -EINVAL) {
|
||||||
|
if (!capable(CAP_MAC_ADMIN))
|
||||||
|
return rc;
|
||||||
|
rc = security_context_to_sid_force(value, size, &newsid);
|
||||||
|
}
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
@ -2703,10 +2708,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = security_context_to_sid(value, size, &newsid);
|
rc = security_context_to_sid_force(value, size, &newsid);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
printk(KERN_WARNING "%s: unable to obtain SID for context "
|
printk(KERN_ERR "SELinux: unable to map context to SID"
|
||||||
"%s, rc=%d\n", __func__, (char *)value, -rc);
|
"for (%s, %lu), rc=%d\n",
|
||||||
|
inode->i_sb->s_id, inode->i_ino, -rc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5153,6 +5159,12 @@ static int selinux_setprocattr(struct task_struct *p,
|
||||||
size--;
|
size--;
|
||||||
}
|
}
|
||||||
error = security_context_to_sid(value, size, &sid);
|
error = security_context_to_sid(value, size, &sid);
|
||||||
|
if (error == -EINVAL && !strcmp(name, "fscreate")) {
|
||||||
|
if (!capable(CAP_MAC_ADMIN))
|
||||||
|
return error;
|
||||||
|
error = security_context_to_sid_force(value, size,
|
||||||
|
&sid);
|
||||||
|
}
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,12 +93,17 @@ int security_change_sid(u32 ssid, u32 tsid,
|
||||||
int security_sid_to_context(u32 sid, char **scontext,
|
int security_sid_to_context(u32 sid, char **scontext,
|
||||||
u32 *scontext_len);
|
u32 *scontext_len);
|
||||||
|
|
||||||
|
int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);
|
||||||
|
|
||||||
int security_context_to_sid(const char *scontext, u32 scontext_len,
|
int security_context_to_sid(const char *scontext, u32 scontext_len,
|
||||||
u32 *out_sid);
|
u32 *out_sid);
|
||||||
|
|
||||||
int security_context_to_sid_default(const char *scontext, u32 scontext_len,
|
int security_context_to_sid_default(const char *scontext, u32 scontext_len,
|
||||||
u32 *out_sid, u32 def_sid, gfp_t gfp_flags);
|
u32 *out_sid, u32 def_sid, gfp_t gfp_flags);
|
||||||
|
|
||||||
|
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
|
||||||
|
u32 *sid);
|
||||||
|
|
||||||
int security_get_user_sids(u32 callsid, char *username,
|
int security_get_user_sids(u32 callsid, char *username,
|
||||||
u32 **sids, u32 *nel);
|
u32 **sids, u32 *nel);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ struct context {
|
||||||
u32 role;
|
u32 role;
|
||||||
u32 type;
|
u32 type;
|
||||||
struct mls_range range;
|
struct mls_range range;
|
||||||
|
char *str; /* string representation if context cannot be mapped. */
|
||||||
|
u32 len; /* length of string in bytes */
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void mls_context_init(struct context *c)
|
static inline void mls_context_init(struct context *c)
|
||||||
|
@ -106,20 +108,43 @@ static inline void context_init(struct context *c)
|
||||||
|
|
||||||
static inline int context_cpy(struct context *dst, struct context *src)
|
static inline int context_cpy(struct context *dst, struct context *src)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
dst->user = src->user;
|
dst->user = src->user;
|
||||||
dst->role = src->role;
|
dst->role = src->role;
|
||||||
dst->type = src->type;
|
dst->type = src->type;
|
||||||
return mls_context_cpy(dst, src);
|
if (src->str) {
|
||||||
|
dst->str = kstrdup(src->str, GFP_ATOMIC);
|
||||||
|
if (!dst->str)
|
||||||
|
return -ENOMEM;
|
||||||
|
dst->len = src->len;
|
||||||
|
} else {
|
||||||
|
dst->str = NULL;
|
||||||
|
dst->len = 0;
|
||||||
|
}
|
||||||
|
rc = mls_context_cpy(dst, src);
|
||||||
|
if (rc) {
|
||||||
|
kfree(dst->str);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void context_destroy(struct context *c)
|
static inline void context_destroy(struct context *c)
|
||||||
{
|
{
|
||||||
c->user = c->role = c->type = 0;
|
c->user = c->role = c->type = 0;
|
||||||
|
kfree(c->str);
|
||||||
|
c->str = NULL;
|
||||||
|
c->len = 0;
|
||||||
mls_context_destroy(c);
|
mls_context_destroy(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int context_cmp(struct context *c1, struct context *c2)
|
static inline int context_cmp(struct context *c1, struct context *c2)
|
||||||
{
|
{
|
||||||
|
if (c1->len && c2->len)
|
||||||
|
return (c1->len == c2->len && !strcmp(c1->str, c2->str));
|
||||||
|
if (c1->len || c2->len)
|
||||||
|
return 0;
|
||||||
return ((c1->user == c2->user) &&
|
return ((c1->user == c2->user) &&
|
||||||
(c1->role == c2->role) &&
|
(c1->role == c2->role) &&
|
||||||
(c1->type == c2->type) &&
|
(c1->type == c2->type) &&
|
||||||
|
|
|
@ -239,7 +239,8 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
|
||||||
* Policy read-lock must be held for sidtab lookup.
|
* Policy read-lock must be held for sidtab lookup.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int mls_context_to_sid(char oldc,
|
int mls_context_to_sid(struct policydb *pol,
|
||||||
|
char oldc,
|
||||||
char **scontext,
|
char **scontext,
|
||||||
struct context *context,
|
struct context *context,
|
||||||
struct sidtab *s,
|
struct sidtab *s,
|
||||||
|
@ -286,7 +287,7 @@ int mls_context_to_sid(char oldc,
|
||||||
*p++ = 0;
|
*p++ = 0;
|
||||||
|
|
||||||
for (l = 0; l < 2; l++) {
|
for (l = 0; l < 2; l++) {
|
||||||
levdatum = hashtab_search(policydb.p_levels.table, scontextp);
|
levdatum = hashtab_search(pol->p_levels.table, scontextp);
|
||||||
if (!levdatum) {
|
if (!levdatum) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -311,7 +312,7 @@ int mls_context_to_sid(char oldc,
|
||||||
*rngptr++ = 0;
|
*rngptr++ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
catdatum = hashtab_search(policydb.p_cats.table,
|
catdatum = hashtab_search(pol->p_cats.table,
|
||||||
scontextp);
|
scontextp);
|
||||||
if (!catdatum) {
|
if (!catdatum) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
@ -327,7 +328,7 @@ int mls_context_to_sid(char oldc,
|
||||||
if (rngptr) {
|
if (rngptr) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
|
rngdatum = hashtab_search(pol->p_cats.table, rngptr);
|
||||||
if (!rngdatum) {
|
if (!rngdatum) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -395,7 +396,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
|
||||||
if (!tmpstr) {
|
if (!tmpstr) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
} else {
|
} else {
|
||||||
rc = mls_context_to_sid(':', &tmpstr, context,
|
rc = mls_context_to_sid(&policydb, ':', &tmpstr, context,
|
||||||
NULL, SECSID_NULL);
|
NULL, SECSID_NULL);
|
||||||
kfree(freestr);
|
kfree(freestr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ int mls_context_isvalid(struct policydb *p, struct context *c);
|
||||||
int mls_range_isvalid(struct policydb *p, struct mls_range *r);
|
int mls_range_isvalid(struct policydb *p, struct mls_range *r);
|
||||||
int mls_level_isvalid(struct policydb *p, struct mls_level *l);
|
int mls_level_isvalid(struct policydb *p, struct mls_level *l);
|
||||||
|
|
||||||
int mls_context_to_sid(char oldc,
|
int mls_context_to_sid(struct policydb *p,
|
||||||
|
char oldc,
|
||||||
char **scontext,
|
char **scontext,
|
||||||
struct context *context,
|
struct context *context,
|
||||||
struct sidtab *s,
|
struct sidtab *s,
|
||||||
|
|
|
@ -616,6 +616,14 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
|
||||||
*scontext = NULL;
|
*scontext = NULL;
|
||||||
*scontext_len = 0;
|
*scontext_len = 0;
|
||||||
|
|
||||||
|
if (context->len) {
|
||||||
|
*scontext_len = context->len;
|
||||||
|
*scontext = kstrdup(context->str, GFP_ATOMIC);
|
||||||
|
if (!(*scontext))
|
||||||
|
return -ENOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Compute the size of the context. */
|
/* Compute the size of the context. */
|
||||||
*scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
|
*scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
|
||||||
*scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
|
*scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
|
||||||
|
@ -655,17 +663,8 @@ const char *security_get_initial_sid_context(u32 sid)
|
||||||
return initial_sid_to_string[sid];
|
return initial_sid_to_string[sid];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int security_sid_to_context_core(u32 sid, char **scontext,
|
||||||
* security_sid_to_context - Obtain a context for a given SID.
|
u32 *scontext_len, int force)
|
||||||
* @sid: security identifier, SID
|
|
||||||
* @scontext: security context
|
|
||||||
* @scontext_len: length in bytes
|
|
||||||
*
|
|
||||||
* Write the string representation of the context associated with @sid
|
|
||||||
* into a dynamically allocated string of the correct size. Set @scontext
|
|
||||||
* to point to this string and set @scontext_len to the length of the string.
|
|
||||||
*/
|
|
||||||
int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
|
|
||||||
{
|
{
|
||||||
struct context *context;
|
struct context *context;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
@ -693,7 +692,10 @@ int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
POLICY_RDLOCK;
|
POLICY_RDLOCK;
|
||||||
context = sidtab_search(&sidtab, sid);
|
if (force)
|
||||||
|
context = sidtab_search_force(&sidtab, sid);
|
||||||
|
else
|
||||||
|
context = sidtab_search(&sidtab, sid);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||||
__func__, sid);
|
__func__, sid);
|
||||||
|
@ -708,17 +710,129 @@ out:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
|
/**
|
||||||
u32 *sid, u32 def_sid, gfp_t gfp_flags)
|
* security_sid_to_context - Obtain a context for a given SID.
|
||||||
|
* @sid: security identifier, SID
|
||||||
|
* @scontext: security context
|
||||||
|
* @scontext_len: length in bytes
|
||||||
|
*
|
||||||
|
* Write the string representation of the context associated with @sid
|
||||||
|
* into a dynamically allocated string of the correct size. Set @scontext
|
||||||
|
* to point to this string and set @scontext_len to the length of the string.
|
||||||
|
*/
|
||||||
|
int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
|
||||||
{
|
{
|
||||||
char *scontext2;
|
return security_sid_to_context_core(sid, scontext, scontext_len, 0);
|
||||||
struct context context;
|
}
|
||||||
|
|
||||||
|
int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len)
|
||||||
|
{
|
||||||
|
return security_sid_to_context_core(sid, scontext, scontext_len, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int string_to_context_struct(struct policydb *pol,
|
||||||
|
struct sidtab *sidtabp,
|
||||||
|
const char *scontext,
|
||||||
|
u32 scontext_len,
|
||||||
|
struct context *ctx,
|
||||||
|
u32 def_sid,
|
||||||
|
gfp_t gfp_flags)
|
||||||
|
{
|
||||||
|
char *scontext2 = NULL;
|
||||||
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 *scontextp, *p, oldc;
|
char *scontextp, *p, oldc;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
context_init(ctx);
|
||||||
|
|
||||||
|
/* Copy the string so that we can modify the copy as we parse it. */
|
||||||
|
scontext2 = kmalloc(scontext_len+1, gfp_flags);
|
||||||
|
if (!scontext2) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy(scontext2, scontext, scontext_len);
|
||||||
|
scontext2[scontext_len] = 0;
|
||||||
|
|
||||||
|
/* Parse the security context. */
|
||||||
|
|
||||||
|
rc = -EINVAL;
|
||||||
|
scontextp = (char *) scontext2;
|
||||||
|
|
||||||
|
/* Extract the user. */
|
||||||
|
p = scontextp;
|
||||||
|
while (*p && *p != ':')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (*p == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*p++ = 0;
|
||||||
|
|
||||||
|
usrdatum = hashtab_search(pol->p_users.table, scontextp);
|
||||||
|
if (!usrdatum)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ctx->user = usrdatum->value;
|
||||||
|
|
||||||
|
/* Extract role. */
|
||||||
|
scontextp = p;
|
||||||
|
while (*p && *p != ':')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (*p == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*p++ = 0;
|
||||||
|
|
||||||
|
role = hashtab_search(pol->p_roles.table, scontextp);
|
||||||
|
if (!role)
|
||||||
|
goto out;
|
||||||
|
ctx->role = role->value;
|
||||||
|
|
||||||
|
/* Extract type. */
|
||||||
|
scontextp = p;
|
||||||
|
while (*p && *p != ':')
|
||||||
|
p++;
|
||||||
|
oldc = *p;
|
||||||
|
*p++ = 0;
|
||||||
|
|
||||||
|
typdatum = hashtab_search(pol->p_types.table, scontextp);
|
||||||
|
if (!typdatum)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ctx->type = typdatum->value;
|
||||||
|
|
||||||
|
rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid);
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if ((p - scontext2) < scontext_len) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the validity of the new context. */
|
||||||
|
if (!policydb_context_isvalid(pol, ctx)) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
context_destroy(ctx);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
rc = 0;
|
||||||
|
out:
|
||||||
|
kfree(scontext2);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
|
||||||
|
u32 *sid, u32 def_sid, gfp_t gfp_flags,
|
||||||
|
int force)
|
||||||
|
{
|
||||||
|
struct context context;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
if (!ss_initialized) {
|
if (!ss_initialized) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -733,94 +847,26 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
|
||||||
}
|
}
|
||||||
*sid = SECSID_NULL;
|
*sid = SECSID_NULL;
|
||||||
|
|
||||||
/* Copy the string so that we can modify the copy as we parse it.
|
|
||||||
The string should already by null terminated, but we append a
|
|
||||||
null suffix to the copy to avoid problems with the existing
|
|
||||||
attr package, which doesn't view the null terminator as part
|
|
||||||
of the attribute value. */
|
|
||||||
scontext2 = kmalloc(scontext_len+1, gfp_flags);
|
|
||||||
if (!scontext2) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
memcpy(scontext2, scontext, scontext_len);
|
|
||||||
scontext2[scontext_len] = 0;
|
|
||||||
|
|
||||||
context_init(&context);
|
|
||||||
*sid = SECSID_NULL;
|
|
||||||
|
|
||||||
POLICY_RDLOCK;
|
POLICY_RDLOCK;
|
||||||
|
rc = string_to_context_struct(&policydb, &sidtab,
|
||||||
/* Parse the security context. */
|
scontext, scontext_len,
|
||||||
|
&context, def_sid, gfp_flags);
|
||||||
rc = -EINVAL;
|
if (rc == -EINVAL && force) {
|
||||||
scontextp = (char *) scontext2;
|
context.str = kmalloc(scontext_len+1, gfp_flags);
|
||||||
|
if (!context.str) {
|
||||||
/* Extract the user. */
|
rc = -ENOMEM;
|
||||||
p = scontextp;
|
goto out;
|
||||||
while (*p && *p != ':')
|
}
|
||||||
p++;
|
memcpy(context.str, scontext, scontext_len);
|
||||||
|
context.str[scontext_len] = 0;
|
||||||
if (*p == 0)
|
context.len = scontext_len;
|
||||||
goto out_unlock;
|
} else if (rc)
|
||||||
|
goto out;
|
||||||
*p++ = 0;
|
|
||||||
|
|
||||||
usrdatum = hashtab_search(policydb.p_users.table, scontextp);
|
|
||||||
if (!usrdatum)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
context.user = usrdatum->value;
|
|
||||||
|
|
||||||
/* Extract role. */
|
|
||||||
scontextp = p;
|
|
||||||
while (*p && *p != ':')
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (*p == 0)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
*p++ = 0;
|
|
||||||
|
|
||||||
role = hashtab_search(policydb.p_roles.table, scontextp);
|
|
||||||
if (!role)
|
|
||||||
goto out_unlock;
|
|
||||||
context.role = role->value;
|
|
||||||
|
|
||||||
/* Extract type. */
|
|
||||||
scontextp = p;
|
|
||||||
while (*p && *p != ':')
|
|
||||||
p++;
|
|
||||||
oldc = *p;
|
|
||||||
*p++ = 0;
|
|
||||||
|
|
||||||
typdatum = hashtab_search(policydb.p_types.table, scontextp);
|
|
||||||
if (!typdatum)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
context.type = typdatum->value;
|
|
||||||
|
|
||||||
rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid);
|
|
||||||
if (rc)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
if ((p - scontext2) < scontext_len) {
|
|
||||||
rc = -EINVAL;
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the validity of the new context. */
|
|
||||||
if (!policydb_context_isvalid(&policydb, &context)) {
|
|
||||||
rc = -EINVAL;
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
/* Obtain the new sid. */
|
|
||||||
rc = sidtab_context_to_sid(&sidtab, &context, sid);
|
rc = sidtab_context_to_sid(&sidtab, &context, sid);
|
||||||
out_unlock:
|
if (rc)
|
||||||
POLICY_RDUNLOCK;
|
context_destroy(&context);
|
||||||
context_destroy(&context);
|
|
||||||
kfree(scontext2);
|
|
||||||
out:
|
out:
|
||||||
|
POLICY_RDUNLOCK;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,7 +884,7 @@ out:
|
||||||
int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
|
int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
|
||||||
{
|
{
|
||||||
return security_context_to_sid_core(scontext, scontext_len,
|
return security_context_to_sid_core(scontext, scontext_len,
|
||||||
sid, SECSID_NULL, GFP_KERNEL);
|
sid, SECSID_NULL, GFP_KERNEL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -855,6 +901,7 @@ int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
|
||||||
* The default SID is passed to the MLS layer to be used to allow
|
* The default SID is passed to the MLS layer to be used to allow
|
||||||
* kernel labeling of the MLS field if the MLS field is not present
|
* kernel labeling of the MLS field if the MLS field is not present
|
||||||
* (for upgrading to MLS without full relabel).
|
* (for upgrading to MLS without full relabel).
|
||||||
|
* Implicitly forces adding of the context even if it cannot be mapped yet.
|
||||||
* Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
|
* Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
|
||||||
* memory is available, or 0 on success.
|
* memory is available, or 0 on success.
|
||||||
*/
|
*/
|
||||||
|
@ -862,7 +909,14 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len,
|
||||||
u32 *sid, u32 def_sid, gfp_t gfp_flags)
|
u32 *sid, u32 def_sid, gfp_t gfp_flags)
|
||||||
{
|
{
|
||||||
return security_context_to_sid_core(scontext, scontext_len,
|
return security_context_to_sid_core(scontext, scontext_len,
|
||||||
sid, def_sid, gfp_flags);
|
sid, def_sid, gfp_flags, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
|
||||||
|
u32 *sid)
|
||||||
|
{
|
||||||
|
return security_context_to_sid_core(scontext, scontext_len,
|
||||||
|
sid, SECSID_NULL, GFP_KERNEL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compute_sid_handle_invalid_context(
|
static int compute_sid_handle_invalid_context(
|
||||||
|
@ -1246,9 +1300,12 @@ static inline int convert_context_handle_invalid_context(struct context *context
|
||||||
char *s;
|
char *s;
|
||||||
u32 len;
|
u32 len;
|
||||||
|
|
||||||
context_struct_to_string(context, &s, &len);
|
if (!context_struct_to_string(context, &s, &len)) {
|
||||||
printk(KERN_ERR "SELinux: context %s is invalid\n", s);
|
printk(KERN_WARNING
|
||||||
kfree(s);
|
"SELinux: Context %s would be invalid if enforcing\n",
|
||||||
|
s);
|
||||||
|
kfree(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1280,6 +1337,32 @@ static int convert_context(u32 key,
|
||||||
|
|
||||||
args = p;
|
args = p;
|
||||||
|
|
||||||
|
if (c->str) {
|
||||||
|
struct context ctx;
|
||||||
|
rc = string_to_context_struct(args->newp, NULL, c->str,
|
||||||
|
c->len, &ctx, SECSID_NULL,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!rc) {
|
||||||
|
printk(KERN_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. */
|
||||||
|
rc = 0;
|
||||||
|
goto out;
|
||||||
|
} else {
|
||||||
|
/* Other error condition, e.g. ENOMEM. */
|
||||||
|
printk(KERN_ERR
|
||||||
|
"SELinux: Unable to map context %s, rc = %d.\n",
|
||||||
|
c->str, -rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = context_cpy(&oldc, c);
|
rc = context_cpy(&oldc, c);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1319,13 +1402,21 @@ static int convert_context(u32 key,
|
||||||
}
|
}
|
||||||
|
|
||||||
context_destroy(&oldc);
|
context_destroy(&oldc);
|
||||||
|
rc = 0;
|
||||||
out:
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
bad:
|
bad:
|
||||||
context_struct_to_string(&oldc, &s, &len);
|
/* Map old representation to string and save it. */
|
||||||
|
if (context_struct_to_string(&oldc, &s, &len))
|
||||||
|
return -ENOMEM;
|
||||||
context_destroy(&oldc);
|
context_destroy(&oldc);
|
||||||
printk(KERN_ERR "SELinux: invalidating context %s\n", s);
|
context_destroy(c);
|
||||||
kfree(s);
|
c->str = s;
|
||||||
|
c->len = len;
|
||||||
|
printk(KERN_INFO
|
||||||
|
"SELinux: Context %s became invalid (unmapped).\n",
|
||||||
|
c->str);
|
||||||
|
rc = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1406,7 +1497,11 @@ int security_load_policy(void *data, size_t len)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sidtab_init(&newsidtab);
|
if (sidtab_init(&newsidtab)) {
|
||||||
|
LOAD_UNLOCK;
|
||||||
|
policydb_destroy(&newpolicydb);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
/* Verify that the kernel defined classes are correct. */
|
/* Verify that the kernel defined classes are correct. */
|
||||||
if (validate_classes(&newpolicydb)) {
|
if (validate_classes(&newpolicydb)) {
|
||||||
|
@ -1429,11 +1524,15 @@ int security_load_policy(void *data, size_t len)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert the internal representations of contexts
|
/*
|
||||||
in the new SID table and remove invalid SIDs. */
|
* Convert the internal representations of contexts
|
||||||
|
* in the new SID table.
|
||||||
|
*/
|
||||||
args.oldp = &policydb;
|
args.oldp = &policydb;
|
||||||
args.newp = &newpolicydb;
|
args.newp = &newpolicydb;
|
||||||
sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
|
rc = sidtab_map(&newsidtab, convert_context, &args);
|
||||||
|
if (rc)
|
||||||
|
goto err;
|
||||||
|
|
||||||
/* 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);
|
||||||
|
@ -1673,6 +1772,8 @@ int security_get_user_sids(u32 fromsid,
|
||||||
|
|
||||||
POLICY_RDLOCK;
|
POLICY_RDLOCK;
|
||||||
|
|
||||||
|
context_init(&usercon);
|
||||||
|
|
||||||
fromcon = sidtab_search(&sidtab, fromsid);
|
fromcon = sidtab_search(&sidtab, fromsid);
|
||||||
if (!fromcon) {
|
if (!fromcon) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
|
|
@ -86,7 +86,7 @@ out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct context *sidtab_search(struct sidtab *s, u32 sid)
|
static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
|
||||||
{
|
{
|
||||||
int hvalue;
|
int hvalue;
|
||||||
struct sidtab_node *cur;
|
struct sidtab_node *cur;
|
||||||
|
@ -99,7 +99,10 @@ struct context *sidtab_search(struct sidtab *s, u32 sid)
|
||||||
while (cur != NULL && sid > cur->sid)
|
while (cur != NULL && sid > cur->sid)
|
||||||
cur = cur->next;
|
cur = cur->next;
|
||||||
|
|
||||||
if (cur == NULL || sid != cur->sid) {
|
if (force && cur && sid == cur->sid && cur->context.len)
|
||||||
|
return &cur->context;
|
||||||
|
|
||||||
|
if (cur == NULL || sid != cur->sid || cur->context.len) {
|
||||||
/* Remap invalid SIDs to the unlabeled SID. */
|
/* Remap invalid SIDs to the unlabeled SID. */
|
||||||
sid = SECINITSID_UNLABELED;
|
sid = SECINITSID_UNLABELED;
|
||||||
hvalue = SIDTAB_HASH(sid);
|
hvalue = SIDTAB_HASH(sid);
|
||||||
|
@ -113,6 +116,16 @@ struct context *sidtab_search(struct sidtab *s, u32 sid)
|
||||||
return &cur->context;
|
return &cur->context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct context *sidtab_search(struct sidtab *s, u32 sid)
|
||||||
|
{
|
||||||
|
return sidtab_search_core(s, sid, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct context *sidtab_search_force(struct sidtab *s, u32 sid)
|
||||||
|
{
|
||||||
|
return sidtab_search_core(s, sid, 1);
|
||||||
|
}
|
||||||
|
|
||||||
int sidtab_map(struct sidtab *s,
|
int sidtab_map(struct sidtab *s,
|
||||||
int (*apply) (u32 sid,
|
int (*apply) (u32 sid,
|
||||||
struct context *context,
|
struct context *context,
|
||||||
|
@ -138,43 +151,6 @@ out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sidtab_map_remove_on_error(struct sidtab *s,
|
|
||||||
int (*apply) (u32 sid,
|
|
||||||
struct context *context,
|
|
||||||
void *args),
|
|
||||||
void *args)
|
|
||||||
{
|
|
||||||
int i, ret;
|
|
||||||
struct sidtab_node *last, *cur, *temp;
|
|
||||||
|
|
||||||
if (!s)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 0; i < SIDTAB_SIZE; i++) {
|
|
||||||
last = NULL;
|
|
||||||
cur = s->htable[i];
|
|
||||||
while (cur != NULL) {
|
|
||||||
ret = apply(cur->sid, &cur->context, args);
|
|
||||||
if (ret) {
|
|
||||||
if (last)
|
|
||||||
last->next = cur->next;
|
|
||||||
else
|
|
||||||
s->htable[i] = cur->next;
|
|
||||||
temp = cur;
|
|
||||||
cur = cur->next;
|
|
||||||
context_destroy(&temp->context);
|
|
||||||
kfree(temp);
|
|
||||||
s->nel--;
|
|
||||||
} else {
|
|
||||||
last = cur;
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 sidtab_search_context(struct sidtab *s,
|
static inline u32 sidtab_search_context(struct sidtab *s,
|
||||||
struct context *context)
|
struct context *context)
|
||||||
{
|
{
|
||||||
|
@ -215,6 +191,10 @@ int sidtab_context_to_sid(struct sidtab *s,
|
||||||
goto unlock_out;
|
goto unlock_out;
|
||||||
}
|
}
|
||||||
sid = s->next_sid++;
|
sid = s->next_sid++;
|
||||||
|
if (context->len)
|
||||||
|
printk(KERN_INFO
|
||||||
|
"SELinux: Context %s is not valid (left unmapped).\n",
|
||||||
|
context->str);
|
||||||
ret = sidtab_insert(s, sid, context);
|
ret = sidtab_insert(s, sid, context);
|
||||||
if (ret)
|
if (ret)
|
||||||
s->next_sid--;
|
s->next_sid--;
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct sidtab {
|
||||||
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_insert(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);
|
||||||
|
|
||||||
int sidtab_map(struct sidtab *s,
|
int sidtab_map(struct sidtab *s,
|
||||||
int (*apply) (u32 sid,
|
int (*apply) (u32 sid,
|
||||||
|
@ -39,12 +40,6 @@ int sidtab_map(struct sidtab *s,
|
||||||
void *args),
|
void *args),
|
||||||
void *args);
|
void *args);
|
||||||
|
|
||||||
void sidtab_map_remove_on_error(struct sidtab *s,
|
|
||||||
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,
|
struct context *context,
|
||||||
u32 *sid);
|
u32 *sid);
|
||||||
|
|
Loading…
Reference in a new issue