Commit 3f12070e authored by Eric Paris's avatar Eric Paris Committed by James Morris

SELinux: policy selectable handling of unknown classes and perms

Allow policy to select, in much the same way as it selects MLS support, how
the kernel should handle access decisions which contain either unknown
classes or unknown permissions in known classes.  The three choices for the
policy flags are

0 - Deny unknown security access. (default)
2 - reject loading policy if it does not contain all definitions
4 - allow unknown security access

The policy's choice is exported through 2 booleans in
selinuxfs.  /selinux/deny_unknown and /selinux/reject_unknown.
Signed-off-by: default avatarEric Paris <eparis@redhat.com>
Acked-by: default avatarStephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 788e7dd4
...@@ -90,6 +90,8 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); ...@@ -90,6 +90,8 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
int security_get_classes(char ***classes, int *nclasses); int security_get_classes(char ***classes, int *nclasses);
int security_get_permissions(char *class, char ***perms, int *nperms); int security_get_permissions(char *class, char ***perms, int *nperms);
int security_get_reject_unknown(void);
int security_get_allow_unknown(void);
#define SECURITY_FS_USE_XATTR 1 /* use xattr */ #define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */ #define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
......
...@@ -103,6 +103,8 @@ enum sel_inos { ...@@ -103,6 +103,8 @@ enum sel_inos {
SEL_MEMBER, /* compute polyinstantiation membership decision */ SEL_MEMBER, /* compute polyinstantiation membership decision */
SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */
SEL_COMPAT_NET, /* whether to use old compat network packet controls */ SEL_COMPAT_NET, /* whether to use old compat network packet controls */
SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */
SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */
SEL_INO_NEXT, /* The next inode number to use */ SEL_INO_NEXT, /* The next inode number to use */
}; };
...@@ -177,6 +179,23 @@ static const struct file_operations sel_enforce_ops = { ...@@ -177,6 +179,23 @@ static const struct file_operations sel_enforce_ops = {
.write = sel_write_enforce, .write = sel_write_enforce,
}; };
static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char tmpbuf[TMPBUFLEN];
ssize_t length;
ino_t ino = filp->f_path.dentry->d_inode->i_ino;
int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ?
security_get_reject_unknown() : !security_get_allow_unknown();
length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown);
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
static const struct file_operations sel_handle_unknown_ops = {
.read = sel_read_handle_unknown,
};
#ifdef CONFIG_SECURITY_SELINUX_DISABLE #ifdef CONFIG_SECURITY_SELINUX_DISABLE
static ssize_t sel_write_disable(struct file * file, const char __user * buf, static ssize_t sel_write_disable(struct file * file, const char __user * buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
...@@ -309,6 +328,11 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, ...@@ -309,6 +328,11 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
length = count; length = count;
out1: out1:
printk(KERN_INFO "SELinux: policy loaded with handle_unknown=%s\n",
(security_get_reject_unknown() ? "reject" :
(security_get_allow_unknown() ? "allow" : "deny")));
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
"policy loaded auid=%u", "policy loaded auid=%u",
audit_get_loginuid(current->audit_context)); audit_get_loginuid(current->audit_context));
...@@ -1575,6 +1599,8 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) ...@@ -1575,6 +1599,8 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
[SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO},
[SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
[SEL_COMPAT_NET] = {"compat_net", &sel_compat_net_ops, S_IRUGO|S_IWUSR}, [SEL_COMPAT_NET] = {"compat_net", &sel_compat_net_ops, S_IRUGO|S_IWUSR},
[SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
[SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
/* last one */ {""} /* last one */ {""}
}; };
ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
......
...@@ -674,6 +674,8 @@ void policydb_destroy(struct policydb *p) ...@@ -674,6 +674,8 @@ void policydb_destroy(struct policydb *p)
} }
kfree(p->type_attr_map); kfree(p->type_attr_map);
kfree(p->undefined_perms);
return; return;
} }
...@@ -1527,6 +1529,8 @@ int policydb_read(struct policydb *p, void *fp) ...@@ -1527,6 +1529,8 @@ int policydb_read(struct policydb *p, void *fp)
goto bad; goto bad;
} }
} }
p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
info = policydb_lookup_compat(p->policyvers); info = policydb_lookup_compat(p->policyvers);
if (!info) { if (!info) {
......
...@@ -242,6 +242,10 @@ struct policydb { ...@@ -242,6 +242,10 @@ struct policydb {
struct ebitmap *type_attr_map; struct ebitmap *type_attr_map;
unsigned int policyvers; unsigned int policyvers;
unsigned int reject_unknown : 1;
unsigned int allow_unknown : 1;
u32 *undefined_perms;
}; };
extern void policydb_destroy(struct policydb *p); extern void policydb_destroy(struct policydb *p);
...@@ -253,6 +257,10 @@ extern int policydb_read(struct policydb *p, void *fp); ...@@ -253,6 +257,10 @@ extern int policydb_read(struct policydb *p, void *fp);
#define POLICYDB_CONFIG_MLS 1 #define POLICYDB_CONFIG_MLS 1
/* the config flags related to unknown classes/perms are bits 2 and 3 */
#define REJECT_UNKNOWN 0x00000002
#define ALLOW_UNKNOWN 0x00000004
#define OBJECT_R "object_r" #define OBJECT_R "object_r"
#define OBJECT_R_VAL 1 #define OBJECT_R_VAL 1
......
...@@ -292,6 +292,7 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -292,6 +292,7 @@ static int context_struct_compute_av(struct context *scontext,
struct class_datum *tclass_datum; struct class_datum *tclass_datum;
struct ebitmap *sattr, *tattr; struct ebitmap *sattr, *tattr;
struct ebitmap_node *snode, *tnode; struct ebitmap_node *snode, *tnode;
const struct selinux_class_perm *kdefs = &selinux_class_perm;
unsigned int i, j; unsigned int i, j;
/* /*
...@@ -305,13 +306,6 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -305,13 +306,6 @@ static int context_struct_compute_av(struct context *scontext,
tclass <= SECCLASS_NETLINK_DNRT_SOCKET) tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
tclass = SECCLASS_NETLINK_SOCKET; tclass = SECCLASS_NETLINK_SOCKET;
if (!tclass || tclass > policydb.p_classes.nprim) {
printk(KERN_ERR "security_compute_av: unrecognized class %d\n",
tclass);
return -EINVAL;
}
tclass_datum = policydb.class_val_to_struct[tclass - 1];
/* /*
* Initialize the access vectors to the default values. * Initialize the access vectors to the default values.
*/ */
...@@ -321,6 +315,36 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -321,6 +315,36 @@ static int context_struct_compute_av(struct context *scontext,
avd->auditdeny = 0xffffffff; avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting; avd->seqno = latest_granting;
/*
* Check for all the invalid cases.
* - tclass 0
* - tclass > policy and > kernel
* - tclass > policy but is a userspace class
* - tclass > policy but we do not allow unknowns
*/
if (unlikely(!tclass))
goto inval_class;
if (unlikely(tclass > policydb.p_classes.nprim))
if (tclass > kdefs->cts_len ||
!kdefs->class_to_string[tclass - 1] ||
!policydb.allow_unknown)
goto inval_class;
/*
* Kernel class and we allow unknown so pad the allow decision
* the pad will be all 1 for unknown classes.
*/
if (tclass <= kdefs->cts_len && policydb.allow_unknown)
avd->allowed = policydb.undefined_perms[tclass - 1];
/*
* Not in policy. Since decision is completed (all 1 or all 0) return.
*/
if (unlikely(tclass > policydb.p_classes.nprim))
return 0;
tclass_datum = policydb.class_val_to_struct[tclass - 1];
/* /*
* If a specific type enforcement rule was defined for * If a specific type enforcement rule was defined for
* this permission check, then use it. * this permission check, then use it.
...@@ -387,6 +411,10 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -387,6 +411,10 @@ static int context_struct_compute_av(struct context *scontext,
} }
return 0; return 0;
inval_class:
printk(KERN_ERR "%s: unrecognized class %d\n", __FUNCTION__, tclass);
return -EINVAL;
} }
static int security_validtrans_handle_fail(struct context *ocontext, static int security_validtrans_handle_fail(struct context *ocontext,
...@@ -1054,6 +1082,13 @@ static int validate_classes(struct policydb *p) ...@@ -1054,6 +1082,13 @@ static int validate_classes(struct policydb *p)
const char *def_class, *def_perm, *pol_class; const char *def_class, *def_perm, *pol_class;
struct symtab *perms; struct symtab *perms;
if (p->allow_unknown) {
u32 num_classes = kdefs->cts_len;
p->undefined_perms = kcalloc(num_classes, sizeof(u32), GFP_KERNEL);
if (!p->undefined_perms)
return -ENOMEM;
}
for (i = 1; i < kdefs->cts_len; i++) { for (i = 1; i < kdefs->cts_len; i++) {
def_class = kdefs->class_to_string[i]; def_class = kdefs->class_to_string[i];
if (!def_class) if (!def_class)
...@@ -1062,6 +1097,10 @@ static int validate_classes(struct policydb *p) ...@@ -1062,6 +1097,10 @@ static int validate_classes(struct policydb *p)
printk(KERN_INFO printk(KERN_INFO
"security: class %s not defined in policy\n", "security: class %s not defined in policy\n",
def_class); def_class);
if (p->reject_unknown)
return -EINVAL;
if (p->allow_unknown)
p->undefined_perms[i-1] = ~0U;
continue; continue;
} }
pol_class = p->p_class_val_to_name[i-1]; pol_class = p->p_class_val_to_name[i-1];
...@@ -1087,12 +1126,16 @@ static int validate_classes(struct policydb *p) ...@@ -1087,12 +1126,16 @@ static int validate_classes(struct policydb *p)
printk(KERN_INFO printk(KERN_INFO
"security: permission %s in class %s not defined in policy\n", "security: permission %s in class %s not defined in policy\n",
def_perm, pol_class); def_perm, pol_class);
if (p->reject_unknown)
return -EINVAL;
if (p->allow_unknown)
p->undefined_perms[class_val-1] |= perm_val;
continue; continue;
} }
perdatum = hashtab_search(perms->table, def_perm); perdatum = hashtab_search(perms->table, def_perm);
if (perdatum == NULL) { if (perdatum == NULL) {
printk(KERN_ERR printk(KERN_ERR
"security: permission %s in class %s not found in policy\n", "security: permission %s in class %s not found in policy, bad policy\n",
def_perm, pol_class); def_perm, pol_class);
return -EINVAL; return -EINVAL;
} }
...@@ -1130,12 +1173,16 @@ static int validate_classes(struct policydb *p) ...@@ -1130,12 +1173,16 @@ static int validate_classes(struct policydb *p)
printk(KERN_INFO printk(KERN_INFO
"security: permission %s in class %s not defined in policy\n", "security: permission %s in class %s not defined in policy\n",
def_perm, pol_class); def_perm, pol_class);
if (p->reject_unknown)
return -EINVAL;
if (p->allow_unknown)
p->undefined_perms[class_val-1] |= (1 << j);
continue; continue;
} }
perdatum = hashtab_search(perms->table, def_perm); perdatum = hashtab_search(perms->table, def_perm);
if (perdatum == NULL) { if (perdatum == NULL) {
printk(KERN_ERR printk(KERN_ERR
"security: permission %s in class %s not found in policy\n", "security: permission %s in class %s not found in policy, bad policy\n",
def_perm, pol_class); def_perm, pol_class);
return -EINVAL; return -EINVAL;
} }
...@@ -2102,6 +2149,16 @@ int security_get_permissions(char *class, char ***perms, int *nperms) ...@@ -2102,6 +2149,16 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
return rc; return rc;
} }
int security_get_reject_unknown(void)
{
return policydb.reject_unknown;
}
int security_get_allow_unknown(void)
{
return policydb.allow_unknown;
}
struct selinux_audit_rule { struct selinux_audit_rule {
u32 au_seqno; u32 au_seqno;
struct context au_ctxt; struct context au_ctxt;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment