Merge branch 'stable-4.13' of git://git.infradead.org/users/pcmoore/selinux into next
This commit is contained in:
@@ -54,6 +54,15 @@ config SECURITY_NETWORK
|
||||
implement socket and networking access controls.
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_INFINIBAND
|
||||
bool "Infiniband Security Hooks"
|
||||
depends on SECURITY && INFINIBAND
|
||||
help
|
||||
This enables the Infiniband security hooks.
|
||||
If enabled, a security module can use these hooks to
|
||||
implement Infiniband access controls.
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_NETWORK_XFRM
|
||||
bool "XFRM (IPSec) Networking Security Hooks"
|
||||
depends on XFRM && SECURITY_NETWORK
|
||||
|
||||
@@ -410,6 +410,22 @@ static void dump_common_audit_data(struct audit_buffer *ab,
|
||||
audit_log_format(ab, " kmod=");
|
||||
audit_log_untrustedstring(ab, a->u.kmod_name);
|
||||
break;
|
||||
case LSM_AUDIT_DATA_IBPKEY: {
|
||||
struct in6_addr sbn_pfx;
|
||||
|
||||
memset(&sbn_pfx.s6_addr, 0,
|
||||
sizeof(sbn_pfx.s6_addr));
|
||||
memcpy(&sbn_pfx.s6_addr, &a->u.ibpkey->subnet_prefix,
|
||||
sizeof(a->u.ibpkey->subnet_prefix));
|
||||
audit_log_format(ab, " pkey=0x%x subnet_prefix=%pI6c",
|
||||
a->u.ibpkey->pkey, &sbn_pfx);
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_IBENDPORT:
|
||||
audit_log_format(ab, " device=%s port_num=%u",
|
||||
a->u.ibendport->dev_name,
|
||||
a->u.ibendport->port);
|
||||
break;
|
||||
} /* switch (a->type) */
|
||||
}
|
||||
|
||||
|
||||
+53
-2
@@ -4,6 +4,7 @@
|
||||
* Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
|
||||
* Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
|
||||
* Copyright (C) 2016 Mellanox Technologies
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -34,6 +35,8 @@
|
||||
#define SECURITY_NAME_MAX 10
|
||||
|
||||
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
|
||||
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
|
||||
|
||||
char *lsm_names;
|
||||
/* Boot-time LSM user choice */
|
||||
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
|
||||
@@ -165,6 +168,24 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
|
||||
panic("%s - Cannot get early memory.\n", __func__);
|
||||
}
|
||||
|
||||
int call_lsm_notifier(enum lsm_event event, void *data)
|
||||
{
|
||||
return atomic_notifier_call_chain(&lsm_notifier_chain, event, data);
|
||||
}
|
||||
EXPORT_SYMBOL(call_lsm_notifier);
|
||||
|
||||
int register_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_register(&lsm_notifier_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(register_lsm_notifier);
|
||||
|
||||
int unregister_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_unregister(&lsm_notifier_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_lsm_notifier);
|
||||
|
||||
/*
|
||||
* Hook list operation macros.
|
||||
*
|
||||
@@ -399,9 +420,12 @@ int security_sb_set_mnt_opts(struct super_block *sb,
|
||||
EXPORT_SYMBOL(security_sb_set_mnt_opts);
|
||||
|
||||
int security_sb_clone_mnt_opts(const struct super_block *oldsb,
|
||||
struct super_block *newsb)
|
||||
struct super_block *newsb,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags)
|
||||
{
|
||||
return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb);
|
||||
return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb,
|
||||
kern_flags, set_kern_flags);
|
||||
}
|
||||
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
|
||||
|
||||
@@ -1515,6 +1539,33 @@ EXPORT_SYMBOL(security_tun_dev_open);
|
||||
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
#ifdef CONFIG_SECURITY_INFINIBAND
|
||||
|
||||
int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey)
|
||||
{
|
||||
return call_int_hook(ib_pkey_access, 0, sec, subnet_prefix, pkey);
|
||||
}
|
||||
EXPORT_SYMBOL(security_ib_pkey_access);
|
||||
|
||||
int security_ib_endport_manage_subnet(void *sec, const char *dev_name, u8 port_num)
|
||||
{
|
||||
return call_int_hook(ib_endport_manage_subnet, 0, sec, dev_name, port_num);
|
||||
}
|
||||
EXPORT_SYMBOL(security_ib_endport_manage_subnet);
|
||||
|
||||
int security_ib_alloc_security(void **sec)
|
||||
{
|
||||
return call_int_hook(ib_alloc_security, 0, sec);
|
||||
}
|
||||
EXPORT_SYMBOL(security_ib_alloc_security);
|
||||
|
||||
void security_ib_free_security(void *sec)
|
||||
{
|
||||
call_void_hook(ib_free_security, sec);
|
||||
}
|
||||
EXPORT_SYMBOL(security_ib_free_security);
|
||||
#endif /* CONFIG_SECURITY_INFINIBAND */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
|
||||
int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
|
||||
|
||||
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
|
||||
netnode.o netport.o exports.o \
|
||||
netnode.o netport.o ibpkey.o exports.o \
|
||||
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
|
||||
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
|
||||
|
||||
|
||||
+176
-36
@@ -17,6 +17,7 @@
|
||||
* Paul Moore <paul@paul-moore.com>
|
||||
* Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
|
||||
* Yuichi Nakamura <ynakam@hitachisoft.jp>
|
||||
* Copyright (C) 2016 Mellanox Technologies
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2,
|
||||
@@ -90,6 +91,7 @@
|
||||
#include "netif.h"
|
||||
#include "netnode.h"
|
||||
#include "netport.h"
|
||||
#include "ibpkey.h"
|
||||
#include "xfrm.h"
|
||||
#include "netlabel.h"
|
||||
#include "audit.h"
|
||||
@@ -171,6 +173,16 @@ static int selinux_netcache_avc_callback(u32 event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_lsm_notifier_avc_callback(u32 event)
|
||||
{
|
||||
if (event == AVC_CALLBACK_RESET) {
|
||||
sel_ib_pkey_flush();
|
||||
call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialise the security for the init task
|
||||
*/
|
||||
@@ -398,18 +410,6 @@ static void superblock_free_security(struct super_block *sb)
|
||||
kfree(sbsec);
|
||||
}
|
||||
|
||||
/* The file system's label must be initialized prior to use. */
|
||||
|
||||
static const char *labeling_behaviors[7] = {
|
||||
"uses xattr",
|
||||
"uses transition SIDs",
|
||||
"uses task SIDs",
|
||||
"uses genfs_contexts",
|
||||
"not configured for labeling",
|
||||
"uses mountpoint labeling",
|
||||
"uses native labeling",
|
||||
};
|
||||
|
||||
static inline int inode_doinit(struct inode *inode)
|
||||
{
|
||||
return inode_doinit_with_dentry(inode, NULL);
|
||||
@@ -524,13 +524,17 @@ static int sb_finish_set_opts(struct super_block *sb)
|
||||
}
|
||||
}
|
||||
|
||||
if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
|
||||
printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
|
||||
sb->s_id, sb->s_type->name);
|
||||
|
||||
sbsec->flags |= SE_SBINITIALIZED;
|
||||
|
||||
/*
|
||||
* Explicitly set or clear SBLABEL_MNT. It's not sufficient to simply
|
||||
* leave the flag untouched because sb_clone_mnt_opts might be handing
|
||||
* us a superblock that needs the flag to be cleared.
|
||||
*/
|
||||
if (selinux_is_sblabel_mnt(sb))
|
||||
sbsec->flags |= SBLABEL_MNT;
|
||||
else
|
||||
sbsec->flags &= ~SBLABEL_MNT;
|
||||
|
||||
/* Initialize the root inode. */
|
||||
rc = inode_doinit_with_dentry(root_inode, root);
|
||||
@@ -809,6 +813,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
||||
sbsec->flags |= SE_SBPROC | SE_SBGENFS;
|
||||
|
||||
if (!strcmp(sb->s_type->name, "debugfs") ||
|
||||
!strcmp(sb->s_type->name, "tracefs") ||
|
||||
!strcmp(sb->s_type->name, "sysfs") ||
|
||||
!strcmp(sb->s_type->name, "pstore"))
|
||||
sbsec->flags |= SE_SBGENFS;
|
||||
@@ -963,8 +968,11 @@ mismatch:
|
||||
}
|
||||
|
||||
static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
|
||||
struct super_block *newsb)
|
||||
struct super_block *newsb,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags)
|
||||
{
|
||||
int rc = 0;
|
||||
const struct superblock_security_struct *oldsbsec = oldsb->s_security;
|
||||
struct superblock_security_struct *newsbsec = newsb->s_security;
|
||||
|
||||
@@ -979,6 +987,13 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
|
||||
if (!ss_initialized)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Specifying internal flags without providing a place to
|
||||
* place the results is not allowed.
|
||||
*/
|
||||
if (kern_flags && !set_kern_flags)
|
||||
return -EINVAL;
|
||||
|
||||
/* how can we clone if the old one wasn't set up?? */
|
||||
BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
|
||||
|
||||
@@ -994,6 +1009,18 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
|
||||
newsbsec->def_sid = oldsbsec->def_sid;
|
||||
newsbsec->behavior = oldsbsec->behavior;
|
||||
|
||||
if (newsbsec->behavior == SECURITY_FS_USE_NATIVE &&
|
||||
!(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) {
|
||||
rc = security_fs_use(newsb);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !set_context) {
|
||||
newsbsec->behavior = SECURITY_FS_USE_NATIVE;
|
||||
*set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
|
||||
}
|
||||
|
||||
if (set_context) {
|
||||
u32 sid = oldsbsec->mntpoint_sid;
|
||||
|
||||
@@ -1013,8 +1040,9 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
|
||||
}
|
||||
|
||||
sb_finish_set_opts(newsb);
|
||||
out:
|
||||
mutex_unlock(&newsbsec->lock);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_parse_opts_str(char *options,
|
||||
@@ -2063,8 +2091,9 @@ static inline u32 file_to_av(struct file *file)
|
||||
static inline u32 open_file_to_av(struct file *file)
|
||||
{
|
||||
u32 av = file_to_av(file);
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
if (selinux_policycap_openperm)
|
||||
if (selinux_policycap_openperm && inode->i_sb->s_magic != SOCKFS_MAGIC)
|
||||
av |= FILE__OPEN;
|
||||
|
||||
return av;
|
||||
@@ -3059,6 +3088,7 @@ static int selinux_inode_permission(struct inode *inode, int mask)
|
||||
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
unsigned int ia_valid = iattr->ia_valid;
|
||||
__u32 av = FILE__WRITE;
|
||||
|
||||
@@ -3074,8 +3104,10 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
|
||||
return dentry_has_perm(cred, dentry, FILE__SETATTR);
|
||||
|
||||
if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE)
|
||||
&& !(ia_valid & ATTR_FILE))
|
||||
if (selinux_policycap_openperm &&
|
||||
inode->i_sb->s_magic != SOCKFS_MAGIC &&
|
||||
(ia_valid & ATTR_SIZE) &&
|
||||
!(ia_valid & ATTR_FILE))
|
||||
av |= FILE__OPEN;
|
||||
|
||||
return dentry_has_perm(cred, dentry, av);
|
||||
@@ -3107,6 +3139,18 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
|
||||
return dentry_has_perm(cred, dentry, FILE__SETATTR);
|
||||
}
|
||||
|
||||
static bool has_cap_mac_admin(bool audit)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int cap_audit = audit ? SECURITY_CAP_AUDIT : SECURITY_CAP_NOAUDIT;
|
||||
|
||||
if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, cap_audit))
|
||||
return false;
|
||||
if (cred_has_capability(cred, CAP_MAC_ADMIN, cap_audit, true))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
@@ -3138,7 +3182,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
|
||||
rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL);
|
||||
if (rc == -EINVAL) {
|
||||
if (!capable(CAP_MAC_ADMIN)) {
|
||||
if (!has_cap_mac_admin(true)) {
|
||||
struct audit_buffer *ab;
|
||||
size_t audit_size;
|
||||
const char *str;
|
||||
@@ -3264,13 +3308,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
|
||||
* and lack of permission just means that we fall back to the
|
||||
* in-core context value, not a denial.
|
||||
*/
|
||||
error = cap_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
|
||||
SECURITY_CAP_NOAUDIT);
|
||||
if (!error)
|
||||
error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
|
||||
SECURITY_CAP_NOAUDIT, true);
|
||||
isec = inode_security(inode);
|
||||
if (!error)
|
||||
if (has_cap_mac_admin(false))
|
||||
error = security_sid_to_context_force(isec->sid, &context,
|
||||
&size);
|
||||
else
|
||||
@@ -3550,6 +3589,18 @@ static int selinux_mmap_addr(unsigned long addr)
|
||||
static int selinux_mmap_file(struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
if (file) {
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
rc = inode_has_perm(current_cred(), file_inode(file),
|
||||
FILE__MAP, &ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (selinux_checkreqprot)
|
||||
prot = reqprot;
|
||||
|
||||
@@ -3710,7 +3761,8 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
|
||||
|
||||
/* task security operations */
|
||||
|
||||
static int selinux_task_create(unsigned long clone_flags)
|
||||
static int selinux_task_alloc(struct task_struct *task,
|
||||
unsigned long clone_flags)
|
||||
{
|
||||
u32 sid = current_sid();
|
||||
|
||||
@@ -5918,7 +5970,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
|
||||
}
|
||||
error = security_context_to_sid(value, size, &sid, GFP_KERNEL);
|
||||
if (error == -EINVAL && !strcmp(name, "fscreate")) {
|
||||
if (!capable(CAP_MAC_ADMIN)) {
|
||||
if (!has_cap_mac_admin(true)) {
|
||||
struct audit_buffer *ab;
|
||||
size_t audit_size;
|
||||
|
||||
@@ -6128,7 +6180,70 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
|
||||
*_buffer = context;
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY_INFINIBAND
|
||||
static int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
int err;
|
||||
u32 sid = 0;
|
||||
struct ib_security_struct *sec = ib_sec;
|
||||
struct lsm_ibpkey_audit ibpkey;
|
||||
|
||||
err = sel_ib_pkey_sid(subnet_prefix, pkey_val, &sid);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IBPKEY;
|
||||
ibpkey.subnet_prefix = subnet_prefix;
|
||||
ibpkey.pkey = pkey_val;
|
||||
ad.u.ibpkey = &ibpkey;
|
||||
return avc_has_perm(sec->sid, sid,
|
||||
SECCLASS_INFINIBAND_PKEY,
|
||||
INFINIBAND_PKEY__ACCESS, &ad);
|
||||
}
|
||||
|
||||
static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name,
|
||||
u8 port_num)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
int err;
|
||||
u32 sid = 0;
|
||||
struct ib_security_struct *sec = ib_sec;
|
||||
struct lsm_ibendport_audit ibendport;
|
||||
|
||||
err = security_ib_endport_sid(dev_name, port_num, &sid);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IBENDPORT;
|
||||
strncpy(ibendport.dev_name, dev_name, sizeof(ibendport.dev_name));
|
||||
ibendport.port = port_num;
|
||||
ad.u.ibendport = &ibendport;
|
||||
return avc_has_perm(sec->sid, sid,
|
||||
SECCLASS_INFINIBAND_ENDPORT,
|
||||
INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad);
|
||||
}
|
||||
|
||||
static int selinux_ib_alloc_security(void **ib_sec)
|
||||
{
|
||||
struct ib_security_struct *sec;
|
||||
|
||||
sec = kzalloc(sizeof(*sec), GFP_KERNEL);
|
||||
if (!sec)
|
||||
return -ENOMEM;
|
||||
sec->sid = current_sid();
|
||||
|
||||
*ib_sec = sec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void selinux_ib_free_security(void *ib_sec)
|
||||
{
|
||||
kfree(ib_sec);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
@@ -6213,7 +6328,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
|
||||
LSM_HOOK_INIT(file_open, selinux_file_open),
|
||||
|
||||
LSM_HOOK_INIT(task_create, selinux_task_create),
|
||||
LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
|
||||
LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
|
||||
LSM_HOOK_INIT(cred_free, selinux_cred_free),
|
||||
LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
|
||||
@@ -6315,7 +6430,13 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(tun_dev_attach_queue, selinux_tun_dev_attach_queue),
|
||||
LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach),
|
||||
LSM_HOOK_INIT(tun_dev_open, selinux_tun_dev_open),
|
||||
|
||||
#ifdef CONFIG_SECURITY_INFINIBAND
|
||||
LSM_HOOK_INIT(ib_pkey_access, selinux_ib_pkey_access),
|
||||
LSM_HOOK_INIT(ib_endport_manage_subnet,
|
||||
selinux_ib_endport_manage_subnet),
|
||||
LSM_HOOK_INIT(ib_alloc_security, selinux_ib_alloc_security),
|
||||
LSM_HOOK_INIT(ib_free_security, selinux_ib_free_security),
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
LSM_HOOK_INIT(xfrm_policy_alloc_security, selinux_xfrm_policy_alloc),
|
||||
LSM_HOOK_INIT(xfrm_policy_clone_security, selinux_xfrm_policy_clone),
|
||||
@@ -6379,6 +6500,9 @@ static __init int selinux_init(void)
|
||||
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
|
||||
panic("SELinux: Unable to register AVC netcache callback\n");
|
||||
|
||||
if (avc_add_callback(selinux_lsm_notifier_avc_callback, AVC_CALLBACK_RESET))
|
||||
panic("SELinux: Unable to register AVC LSM notifier callback\n");
|
||||
|
||||
if (selinux_enforcing)
|
||||
printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n");
|
||||
else
|
||||
@@ -6448,6 +6572,23 @@ static struct nf_hook_ops selinux_nf_ops[] = {
|
||||
#endif /* IPV6 */
|
||||
};
|
||||
|
||||
static int __net_init selinux_nf_register(struct net *net)
|
||||
{
|
||||
return nf_register_net_hooks(net, selinux_nf_ops,
|
||||
ARRAY_SIZE(selinux_nf_ops));
|
||||
}
|
||||
|
||||
static void __net_exit selinux_nf_unregister(struct net *net)
|
||||
{
|
||||
nf_unregister_net_hooks(net, selinux_nf_ops,
|
||||
ARRAY_SIZE(selinux_nf_ops));
|
||||
}
|
||||
|
||||
static struct pernet_operations selinux_net_ops = {
|
||||
.init = selinux_nf_register,
|
||||
.exit = selinux_nf_unregister,
|
||||
};
|
||||
|
||||
static int __init selinux_nf_ip_init(void)
|
||||
{
|
||||
int err;
|
||||
@@ -6457,13 +6598,12 @@ static int __init selinux_nf_ip_init(void)
|
||||
|
||||
printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n");
|
||||
|
||||
err = nf_register_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops));
|
||||
err = register_pernet_subsys(&selinux_net_ops);
|
||||
if (err)
|
||||
panic("SELinux: nf_register_hooks: error %d\n", err);
|
||||
panic("SELinux: register_pernet_subsys: error %d\n", err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(selinux_nf_ip_init);
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
@@ -6471,7 +6611,7 @@ static void selinux_nf_ip_exit(void)
|
||||
{
|
||||
printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n");
|
||||
|
||||
nf_unregister_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops));
|
||||
unregister_pernet_subsys(&selinux_net_ops);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Pkey table
|
||||
*
|
||||
* SELinux must keep a mapping of Infinband PKEYs to labels/SIDs. This
|
||||
* mapping is maintained as part of the normal policy but a fast cache is
|
||||
* needed to reduce the lookup overhead.
|
||||
*
|
||||
* This code is heavily based on the "netif" and "netport" concept originally
|
||||
* developed by
|
||||
* James Morris <jmorris@redhat.com> and
|
||||
* Paul Moore <paul@paul-moore.com>
|
||||
* (see security/selinux/netif.c and security/selinux/netport.c for more
|
||||
* information)
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Mellanox Technologies, 2016
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "ibpkey.h"
|
||||
#include "objsec.h"
|
||||
|
||||
#define SEL_PKEY_HASH_SIZE 256
|
||||
#define SEL_PKEY_HASH_BKT_LIMIT 16
|
||||
|
||||
struct sel_ib_pkey_bkt {
|
||||
int size;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct sel_ib_pkey {
|
||||
struct pkey_security_struct psec;
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
static LIST_HEAD(sel_ib_pkey_list);
|
||||
static DEFINE_SPINLOCK(sel_ib_pkey_lock);
|
||||
static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE];
|
||||
|
||||
/**
|
||||
* sel_ib_pkey_hashfn - Hashing function for the pkey table
|
||||
* @pkey: pkey number
|
||||
*
|
||||
* Description:
|
||||
* This is the hashing function for the pkey table, it returns the bucket
|
||||
* number for the given pkey.
|
||||
*
|
||||
*/
|
||||
static unsigned int sel_ib_pkey_hashfn(u16 pkey)
|
||||
{
|
||||
return (pkey & (SEL_PKEY_HASH_SIZE - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* sel_ib_pkey_find - Search for a pkey record
|
||||
* @subnet_prefix: subnet_prefix
|
||||
* @pkey_num: pkey_num
|
||||
*
|
||||
* Description:
|
||||
* Search the pkey table and return the matching record. If an entry
|
||||
* can not be found in the table return NULL.
|
||||
*
|
||||
*/
|
||||
static struct sel_ib_pkey *sel_ib_pkey_find(u64 subnet_prefix, u16 pkey_num)
|
||||
{
|
||||
unsigned int idx;
|
||||
struct sel_ib_pkey *pkey;
|
||||
|
||||
idx = sel_ib_pkey_hashfn(pkey_num);
|
||||
list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) {
|
||||
if (pkey->psec.pkey == pkey_num &&
|
||||
pkey->psec.subnet_prefix == subnet_prefix)
|
||||
return pkey;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sel_ib_pkey_insert - Insert a new pkey into the table
|
||||
* @pkey: the new pkey record
|
||||
*
|
||||
* Description:
|
||||
* Add a new pkey record to the hash table.
|
||||
*
|
||||
*/
|
||||
static void sel_ib_pkey_insert(struct sel_ib_pkey *pkey)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
/* we need to impose a limit on the growth of the hash table so check
|
||||
* this bucket to make sure it is within the specified bounds
|
||||
*/
|
||||
idx = sel_ib_pkey_hashfn(pkey->psec.pkey);
|
||||
list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list);
|
||||
if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) {
|
||||
struct sel_ib_pkey *tail;
|
||||
|
||||
tail = list_entry(
|
||||
rcu_dereference_protected(
|
||||
sel_ib_pkey_hash[idx].list.prev,
|
||||
lockdep_is_held(&sel_ib_pkey_lock)),
|
||||
struct sel_ib_pkey, list);
|
||||
list_del_rcu(&tail->list);
|
||||
kfree_rcu(tail, rcu);
|
||||
} else {
|
||||
sel_ib_pkey_hash[idx].size++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy
|
||||
* @subnet_prefix: subnet prefix
|
||||
* @pkey_num: pkey number
|
||||
* @sid: pkey SID
|
||||
*
|
||||
* Description:
|
||||
* This function determines the SID of a pkey by querying the security
|
||||
* policy. The result is added to the pkey table to speedup future
|
||||
* queries. Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
|
||||
{
|
||||
int ret;
|
||||
struct sel_ib_pkey *pkey;
|
||||
struct sel_ib_pkey *new = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sel_ib_pkey_lock, flags);
|
||||
pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
|
||||
if (pkey) {
|
||||
*sid = pkey->psec.sid;
|
||||
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = security_ib_pkey_sid(subnet_prefix, pkey_num, sid);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* If this memory allocation fails still return 0. The SID
|
||||
* is valid, it just won't be added to the cache.
|
||||
*/
|
||||
new = kzalloc(sizeof(*new), GFP_ATOMIC);
|
||||
if (!new)
|
||||
goto out;
|
||||
|
||||
new->psec.subnet_prefix = subnet_prefix;
|
||||
new->psec.pkey = pkey_num;
|
||||
new->psec.sid = *sid;
|
||||
sel_ib_pkey_insert(new);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sel_ib_pkey_sid - Lookup the SID of a PKEY
|
||||
* @subnet_prefix: subnet_prefix
|
||||
* @pkey_num: pkey number
|
||||
* @sid: pkey SID
|
||||
*
|
||||
* Description:
|
||||
* This function determines the SID of a PKEY using the fastest method
|
||||
* possible. First the pkey table is queried, but if an entry can't be found
|
||||
* then the policy is queried and the result is added to the table to speedup
|
||||
* future queries. Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)
|
||||
{
|
||||
struct sel_ib_pkey *pkey;
|
||||
|
||||
rcu_read_lock();
|
||||
pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
|
||||
if (pkey) {
|
||||
*sid = pkey->psec.sid;
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);
|
||||
}
|
||||
|
||||
/**
|
||||
* sel_ib_pkey_flush - Flush the entire pkey table
|
||||
*
|
||||
* Description:
|
||||
* Remove all entries from the pkey table
|
||||
*
|
||||
*/
|
||||
void sel_ib_pkey_flush(void)
|
||||
{
|
||||
unsigned int idx;
|
||||
struct sel_ib_pkey *pkey, *pkey_tmp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sel_ib_pkey_lock, flags);
|
||||
for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
|
||||
list_for_each_entry_safe(pkey, pkey_tmp,
|
||||
&sel_ib_pkey_hash[idx].list, list) {
|
||||
list_del_rcu(&pkey->list);
|
||||
kfree_rcu(pkey, rcu);
|
||||
}
|
||||
sel_ib_pkey_hash[idx].size = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
|
||||
}
|
||||
|
||||
static __init int sel_ib_pkey_init(void)
|
||||
{
|
||||
int iter;
|
||||
|
||||
if (!selinux_enabled)
|
||||
return 0;
|
||||
|
||||
for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {
|
||||
INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);
|
||||
sel_ib_pkey_hash[iter].size = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
subsys_initcall(sel_ib_pkey_init);
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <linux/capability.h>
|
||||
|
||||
#define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \
|
||||
"getattr", "setattr", "lock", "relabelfrom", "relabelto", "append"
|
||||
"getattr", "setattr", "lock", "relabelfrom", "relabelto", "append", "map"
|
||||
|
||||
#define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \
|
||||
"rename", "execute", "quotaon", "mounton", "audit_access", \
|
||||
@@ -231,6 +231,10 @@ struct security_class_mapping secclass_map[] = {
|
||||
{ COMMON_SOCK_PERMS, NULL } },
|
||||
{ "smc_socket",
|
||||
{ COMMON_SOCK_PERMS, NULL } },
|
||||
{ "infiniband_pkey",
|
||||
{ "access", NULL } },
|
||||
{ "infiniband_endport",
|
||||
{ "manage_subnet", NULL } },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* pkey table
|
||||
*
|
||||
* SELinux must keep a mapping of pkeys to labels/SIDs. This
|
||||
* mapping is maintained as part of the normal policy but a fast cache is
|
||||
* needed to reduce the lookup overhead.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* (c) Mellanox Technologies, 2016
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SELINUX_IB_PKEY_H
|
||||
#define _SELINUX_IB_PKEY_H
|
||||
|
||||
void sel_ib_pkey_flush(void);
|
||||
|
||||
int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey, u32 *sid);
|
||||
|
||||
#endif
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
* Copyright (C) 2001,2002 Networks Associates Technology, Inc.
|
||||
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||
* Copyright (C) 2016 Mellanox Technologies
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2,
|
||||
@@ -139,6 +140,16 @@ struct key_security_struct {
|
||||
u32 sid; /* SID of key */
|
||||
};
|
||||
|
||||
struct ib_security_struct {
|
||||
u32 sid; /* SID of the queue pair or MAD agent */
|
||||
};
|
||||
|
||||
struct pkey_security_struct {
|
||||
u64 subnet_prefix; /* Port subnet prefix */
|
||||
u16 pkey; /* PKey number */
|
||||
u32 sid; /* SID of pkey */
|
||||
};
|
||||
|
||||
extern unsigned int selinux_checkreqprot;
|
||||
|
||||
#endif /* _SELINUX_OBJSEC_H_ */
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
#define POLICYDB_VERSION_DEFAULT_TYPE 28
|
||||
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29
|
||||
#define POLICYDB_VERSION_XPERMS_IOCTL 30
|
||||
#define POLICYDB_VERSION_INFINIBAND 31
|
||||
|
||||
/* Range of policy versions we understand*/
|
||||
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_INFINIBAND
|
||||
|
||||
/* Mask for just the mount related flags */
|
||||
#define SE_MNTMASK 0x0f
|
||||
@@ -76,6 +77,8 @@ enum {
|
||||
};
|
||||
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
|
||||
|
||||
extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
|
||||
|
||||
extern int selinux_policycap_netpeer;
|
||||
extern int selinux_policycap_openperm;
|
||||
extern int selinux_policycap_extsockclass;
|
||||
@@ -178,6 +181,10 @@ int security_get_user_sids(u32 callsid, char *username,
|
||||
|
||||
int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
|
||||
|
||||
int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
|
||||
|
||||
int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid);
|
||||
|
||||
int security_netif_sid(char *name, u32 *if_sid);
|
||||
|
||||
int security_node_sid(u16 domain, void *addr, u32 addrlen,
|
||||
|
||||
@@ -41,15 +41,6 @@
|
||||
#include "objsec.h"
|
||||
#include "conditional.h"
|
||||
|
||||
/* Policy capability filenames */
|
||||
static char *policycap_names[] = {
|
||||
"network_peer_controls",
|
||||
"open_perms",
|
||||
"extended_socket_class",
|
||||
"always_check_network",
|
||||
"cgroup_seclabel"
|
||||
};
|
||||
|
||||
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
|
||||
|
||||
static int __init checkreqprot_setup(char *str)
|
||||
@@ -163,6 +154,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
|
||||
avc_ss_reset(0);
|
||||
selnl_notify_setenforce(selinux_enforcing);
|
||||
selinux_status_update_setenforce(selinux_enforcing);
|
||||
if (!selinux_enforcing)
|
||||
call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
|
||||
}
|
||||
length = count;
|
||||
out:
|
||||
@@ -1750,9 +1743,9 @@ static int sel_make_policycap(void)
|
||||
sel_remove_entries(policycap_dir);
|
||||
|
||||
for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
|
||||
if (iter < ARRAY_SIZE(policycap_names))
|
||||
if (iter < ARRAY_SIZE(selinux_policycap_names))
|
||||
dentry = d_alloc_name(policycap_dir,
|
||||
policycap_names[iter]);
|
||||
selinux_policycap_names[iter]);
|
||||
else
|
||||
dentry = d_alloc_name(policycap_dir, "unknown");
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#define BITS_PER_U64 (sizeof(u64) * 8)
|
||||
|
||||
static struct kmem_cache *ebitmap_node_cachep;
|
||||
|
||||
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
|
||||
{
|
||||
struct ebitmap_node *n1, *n2;
|
||||
@@ -54,7 +56,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
|
||||
n = src->node;
|
||||
prev = NULL;
|
||||
while (n) {
|
||||
new = kzalloc(sizeof(*new), GFP_ATOMIC);
|
||||
new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
|
||||
if (!new) {
|
||||
ebitmap_destroy(dst);
|
||||
return -ENOMEM;
|
||||
@@ -162,7 +164,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
|
||||
if (e_iter == NULL ||
|
||||
offset >= e_iter->startbit + EBITMAP_SIZE) {
|
||||
e_prev = e_iter;
|
||||
e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
|
||||
e_iter = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
|
||||
if (e_iter == NULL)
|
||||
goto netlbl_import_failure;
|
||||
e_iter->startbit = offset - (offset % EBITMAP_SIZE);
|
||||
@@ -288,7 +290,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
|
||||
prev->next = n->next;
|
||||
else
|
||||
e->node = n->next;
|
||||
kfree(n);
|
||||
kmem_cache_free(ebitmap_node_cachep, n);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -299,7 +301,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
new = kzalloc(sizeof(*new), GFP_ATOMIC);
|
||||
new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -332,7 +334,7 @@ void ebitmap_destroy(struct ebitmap *e)
|
||||
while (n) {
|
||||
temp = n;
|
||||
n = n->next;
|
||||
kfree(temp);
|
||||
kmem_cache_free(ebitmap_node_cachep, temp);
|
||||
}
|
||||
|
||||
e->highbit = 0;
|
||||
@@ -400,7 +402,7 @@ int ebitmap_read(struct ebitmap *e, void *fp)
|
||||
|
||||
if (!n || startbit >= n->startbit + EBITMAP_SIZE) {
|
||||
struct ebitmap_node *tmp;
|
||||
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
|
||||
tmp = kmem_cache_zalloc(ebitmap_node_cachep, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
printk(KERN_ERR
|
||||
"SELinux: ebitmap: out of memory\n");
|
||||
@@ -519,3 +521,15 @@ int ebitmap_write(struct ebitmap *e, void *fp)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ebitmap_cache_init(void)
|
||||
{
|
||||
ebitmap_node_cachep = kmem_cache_create("ebitmap_node",
|
||||
sizeof(struct ebitmap_node),
|
||||
0, SLAB_PANIC, NULL);
|
||||
}
|
||||
|
||||
void ebitmap_cache_destroy(void)
|
||||
{
|
||||
kmem_cache_destroy(ebitmap_node_cachep);
|
||||
}
|
||||
|
||||
@@ -130,6 +130,9 @@ void ebitmap_destroy(struct ebitmap *e);
|
||||
int ebitmap_read(struct ebitmap *e, void *fp);
|
||||
int ebitmap_write(struct ebitmap *e, void *fp);
|
||||
|
||||
void ebitmap_cache_init(void);
|
||||
void ebitmap_cache_destroy(void);
|
||||
|
||||
#ifdef CONFIG_NETLABEL
|
||||
int ebitmap_netlbl_export(struct ebitmap *ebmap,
|
||||
struct netlbl_lsm_catmap **catmap);
|
||||
|
||||
+102
-25
@@ -17,6 +17,11 @@
|
||||
*
|
||||
* Added support for the policy capability bitmap
|
||||
*
|
||||
* Update: Mellanox Techonologies
|
||||
*
|
||||
* Added Infiniband support
|
||||
*
|
||||
* Copyright (C) 2016 Mellanox Techonologies
|
||||
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
|
||||
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
|
||||
@@ -76,81 +81,86 @@ static struct policydb_compat_info policydb_compat[] = {
|
||||
{
|
||||
.version = POLICYDB_VERSION_BASE,
|
||||
.sym_num = SYM_NUM - 3,
|
||||
.ocon_num = OCON_NUM - 1,
|
||||
.ocon_num = OCON_NUM - 3,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_BOOL,
|
||||
.sym_num = SYM_NUM - 2,
|
||||
.ocon_num = OCON_NUM - 1,
|
||||
.ocon_num = OCON_NUM - 3,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_IPV6,
|
||||
.sym_num = SYM_NUM - 2,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_NLCLASS,
|
||||
.sym_num = SYM_NUM - 2,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_MLS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_AVTAB,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_RANGETRANS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_POLCAP,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_PERMISSIVE,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_BOUNDARY,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_FILENAME_TRANS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_ROLETRANS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_DEFAULT_TYPE,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_CONSTRAINT_NAMES,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_XPERMS_IOCTL,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM - 2,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_INFINIBAND,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
},
|
||||
};
|
||||
@@ -538,34 +548,30 @@ static int policydb_index(struct policydb *p)
|
||||
symtab_hash_eval(p->symtab);
|
||||
#endif
|
||||
|
||||
rc = -ENOMEM;
|
||||
p->class_val_to_struct = kcalloc(p->p_classes.nprim,
|
||||
sizeof(*p->class_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!p->class_val_to_struct)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = -ENOMEM;
|
||||
p->role_val_to_struct = kcalloc(p->p_roles.nprim,
|
||||
sizeof(*p->role_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!p->role_val_to_struct)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = -ENOMEM;
|
||||
p->user_val_to_struct = kcalloc(p->p_users.nprim,
|
||||
sizeof(*p->user_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!p->user_val_to_struct)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
/* Yes, I want the sizeof the pointer, not the structure */
|
||||
rc = -ENOMEM;
|
||||
p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *),
|
||||
p->p_types.nprim,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
if (!p->type_val_to_struct_array)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
|
||||
p->p_types.nprim, GFP_KERNEL | __GFP_ZERO);
|
||||
@@ -577,12 +583,11 @@ static int policydb_index(struct policydb *p)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < SYM_NUM; i++) {
|
||||
rc = -ENOMEM;
|
||||
p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *),
|
||||
p->symtab[i].nprim,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
if (!p->sym_val_to_name[i])
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = flex_array_prealloc(p->sym_val_to_name[i],
|
||||
0, p->symtab[i].nprim,
|
||||
@@ -2211,6 +2216,51 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
case OCON_IBPKEY:
|
||||
rc = next_entry(nodebuf, fp, sizeof(u32) * 4);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
c->u.ibpkey.subnet_prefix = be64_to_cpu(*((__be64 *)nodebuf));
|
||||
|
||||
if (nodebuf[2] > 0xffff ||
|
||||
nodebuf[3] > 0xffff) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->u.ibpkey.low_pkey = le32_to_cpu(nodebuf[2]);
|
||||
c->u.ibpkey.high_pkey = le32_to_cpu(nodebuf[3]);
|
||||
|
||||
rc = context_read_and_validate(&c->context[0],
|
||||
p,
|
||||
fp);
|
||||
if (rc)
|
||||
goto out;
|
||||
break;
|
||||
case OCON_IBENDPORT:
|
||||
rc = next_entry(buf, fp, sizeof(u32) * 2);
|
||||
if (rc)
|
||||
goto out;
|
||||
len = le32_to_cpu(buf[0]);
|
||||
|
||||
rc = str_read(&c->u.ibendport.dev_name, GFP_KERNEL, fp, len);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (buf[1] > 0xff || buf[1] == 0) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->u.ibendport.port = le32_to_cpu(buf[1]);
|
||||
|
||||
rc = context_read_and_validate(&c->context[0],
|
||||
p,
|
||||
fp);
|
||||
if (rc)
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3140,6 +3190,33 @@ static int ocontext_write(struct policydb *p, struct policydb_compat_info *info,
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
case OCON_IBPKEY:
|
||||
*((__be64 *)nodebuf) = cpu_to_be64(c->u.ibpkey.subnet_prefix);
|
||||
|
||||
nodebuf[2] = cpu_to_le32(c->u.ibpkey.low_pkey);
|
||||
nodebuf[3] = cpu_to_le32(c->u.ibpkey.high_pkey);
|
||||
|
||||
rc = put_entry(nodebuf, sizeof(u32), 4, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = context_write(p, &c->context[0], fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
case OCON_IBENDPORT:
|
||||
len = strlen(c->u.ibendport.dev_name);
|
||||
buf[0] = cpu_to_le32(len);
|
||||
buf[1] = cpu_to_le32(c->u.ibendport.port);
|
||||
rc = put_entry(buf, sizeof(u32), 2, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = put_entry(c->u.ibendport.dev_name, 1, len, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = context_write(p, &c->context[0], fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,15 @@ struct ocontext {
|
||||
u32 addr[4];
|
||||
u32 mask[4];
|
||||
} node6; /* IPv6 node information */
|
||||
struct {
|
||||
u64 subnet_prefix;
|
||||
u16 low_pkey;
|
||||
u16 high_pkey;
|
||||
} ibpkey;
|
||||
struct {
|
||||
char *dev_name;
|
||||
u8 port;
|
||||
} ibendport;
|
||||
} u;
|
||||
union {
|
||||
u32 sclass; /* security class for genfs */
|
||||
@@ -215,14 +224,16 @@ struct genfs {
|
||||
#define SYM_NUM 8
|
||||
|
||||
/* object context array indices */
|
||||
#define OCON_ISID 0 /* initial SIDs */
|
||||
#define OCON_FS 1 /* unlabeled file systems */
|
||||
#define OCON_PORT 2 /* TCP and UDP port numbers */
|
||||
#define OCON_NETIF 3 /* network interfaces */
|
||||
#define OCON_NODE 4 /* nodes */
|
||||
#define OCON_FSUSE 5 /* fs_use */
|
||||
#define OCON_NODE6 6 /* IPv6 nodes */
|
||||
#define OCON_NUM 7
|
||||
#define OCON_ISID 0 /* initial SIDs */
|
||||
#define OCON_FS 1 /* unlabeled file systems */
|
||||
#define OCON_PORT 2 /* TCP and UDP port numbers */
|
||||
#define OCON_NETIF 3 /* network interfaces */
|
||||
#define OCON_NODE 4 /* nodes */
|
||||
#define OCON_FSUSE 5 /* fs_use */
|
||||
#define OCON_NODE6 6 /* IPv6 nodes */
|
||||
#define OCON_IBPKEY 7 /* Infiniband PKeys */
|
||||
#define OCON_IBENDPORT 8 /* Infiniband end ports */
|
||||
#define OCON_NUM 9
|
||||
|
||||
/* The policy database */
|
||||
struct policydb {
|
||||
|
||||
@@ -70,6 +70,15 @@
|
||||
#include "ebitmap.h"
|
||||
#include "audit.h"
|
||||
|
||||
/* Policy capability names */
|
||||
char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
|
||||
"network_peer_controls",
|
||||
"open_perms",
|
||||
"extended_socket_class",
|
||||
"always_check_network",
|
||||
"cgroup_seclabel"
|
||||
};
|
||||
|
||||
int selinux_policycap_netpeer;
|
||||
int selinux_policycap_openperm;
|
||||
int selinux_policycap_extsockclass;
|
||||
@@ -1986,6 +1995,9 @@ bad:
|
||||
|
||||
static void security_load_policycaps(void)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ebitmap_node *node;
|
||||
|
||||
selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
|
||||
POLICYDB_CAPABILITY_NETPEER);
|
||||
selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
|
||||
@@ -1997,6 +2009,17 @@ static void security_load_policycaps(void)
|
||||
selinux_policycap_cgroupseclabel =
|
||||
ebitmap_get_bit(&policydb.policycaps,
|
||||
POLICYDB_CAPABILITY_CGROUPSECLABEL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++)
|
||||
pr_info("SELinux: policy capability %s=%d\n",
|
||||
selinux_policycap_names[i],
|
||||
ebitmap_get_bit(&policydb.policycaps, i));
|
||||
|
||||
ebitmap_for_each_positive_bit(&policydb.policycaps, node, i) {
|
||||
if (i >= ARRAY_SIZE(selinux_policycap_names))
|
||||
pr_info("SELinux: unknown policy capability %u\n",
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
static int security_preserve_bools(struct policydb *p);
|
||||
@@ -2031,9 +2054,11 @@ int security_load_policy(void *data, size_t len)
|
||||
|
||||
if (!ss_initialized) {
|
||||
avtab_cache_init();
|
||||
ebitmap_cache_init();
|
||||
rc = policydb_read(&policydb, fp);
|
||||
if (rc) {
|
||||
avtab_cache_destroy();
|
||||
ebitmap_cache_destroy();
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2044,6 +2069,7 @@ int security_load_policy(void *data, size_t len)
|
||||
if (rc) {
|
||||
policydb_destroy(&policydb);
|
||||
avtab_cache_destroy();
|
||||
ebitmap_cache_destroy();
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2051,6 +2077,7 @@ int security_load_policy(void *data, size_t len)
|
||||
if (rc) {
|
||||
policydb_destroy(&policydb);
|
||||
avtab_cache_destroy();
|
||||
ebitmap_cache_destroy();
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2209,6 +2236,87 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_pkey_sid - Obtain the SID for a pkey.
|
||||
* @subnet_prefix: Subnet Prefix
|
||||
* @pkey_num: pkey number
|
||||
* @out_sid: security identifier
|
||||
*/
|
||||
int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid)
|
||||
{
|
||||
struct ocontext *c;
|
||||
int rc = 0;
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
|
||||
c = policydb.ocontexts[OCON_IBPKEY];
|
||||
while (c) {
|
||||
if (c->u.ibpkey.low_pkey <= pkey_num &&
|
||||
c->u.ibpkey.high_pkey >= pkey_num &&
|
||||
c->u.ibpkey.subnet_prefix == subnet_prefix)
|
||||
break;
|
||||
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
if (c) {
|
||||
if (!c->sid[0]) {
|
||||
rc = sidtab_context_to_sid(&sidtab,
|
||||
&c->context[0],
|
||||
&c->sid[0]);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
*out_sid = c->sid[0];
|
||||
} else
|
||||
*out_sid = SECINITSID_UNLABELED;
|
||||
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_ib_endport_sid - Obtain the SID for a subnet management interface.
|
||||
* @dev_name: device name
|
||||
* @port: port number
|
||||
* @out_sid: security identifier
|
||||
*/
|
||||
int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid)
|
||||
{
|
||||
struct ocontext *c;
|
||||
int rc = 0;
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
|
||||
c = policydb.ocontexts[OCON_IBENDPORT];
|
||||
while (c) {
|
||||
if (c->u.ibendport.port == port_num &&
|
||||
!strncmp(c->u.ibendport.dev_name,
|
||||
dev_name,
|
||||
IB_DEVICE_NAME_MAX))
|
||||
break;
|
||||
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
if (c) {
|
||||
if (!c->sid[0]) {
|
||||
rc = sidtab_context_to_sid(&sidtab,
|
||||
&c->context[0],
|
||||
&c->sid[0]);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
*out_sid = c->sid[0];
|
||||
} else
|
||||
*out_sid = SECINITSID_UNLABELED;
|
||||
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_netif_sid - Obtain the SID for a network interface.
|
||||
* @name: interface name
|
||||
|
||||
@@ -32,13 +32,11 @@ int sidtab_init(struct sidtab *s)
|
||||
|
||||
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
|
||||
{
|
||||
int hvalue, rc = 0;
|
||||
int hvalue;
|
||||
struct sidtab_node *prev, *cur, *newnode;
|
||||
|
||||
if (!s) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
hvalue = SIDTAB_HASH(sid);
|
||||
prev = NULL;
|
||||
@@ -48,21 +46,17 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
if (cur && sid == cur->sid) {
|
||||
rc = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
if (cur && sid == cur->sid)
|
||||
return -EEXIST;
|
||||
|
||||
newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
|
||||
if (!newnode) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!newnode)
|
||||
return -ENOMEM;
|
||||
|
||||
newnode->sid = sid;
|
||||
if (context_cpy(&newnode->context, context)) {
|
||||
kfree(newnode);
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
@@ -78,8 +72,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
|
||||
s->nel++;
|
||||
if (sid >= s->next_sid)
|
||||
s->next_sid = sid + 1;
|
||||
out:
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
|
||||
|
||||
Reference in New Issue
Block a user