Merge git://git.infradead.org/users/eparis/selinux
Conflicts: security/selinux/hooks.c Pull Eric's existing SELinux tree as there are a number of patches in there that are not yet upstream. There was some minor fixup needed to resolve a conflict in security/selinux/hooks.c:selinux_set_mnt_opts() between the labeled NFS patches and Eric's security_fs_use() simplification patch.
This commit is contained in:
commit
98f700f317
16 changed files with 449 additions and 397 deletions
|
@ -1052,17 +1052,25 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
|||
* @xfrm_policy_delete_security:
|
||||
* @ctx contains the xfrm_sec_ctx.
|
||||
* Authorize deletion of xp->security.
|
||||
* @xfrm_state_alloc_security:
|
||||
* @xfrm_state_alloc:
|
||||
* @x contains the xfrm_state being added to the Security Association
|
||||
* Database by the XFRM system.
|
||||
* @sec_ctx contains the security context information being provided by
|
||||
* the user-level SA generation program (e.g., setkey or racoon).
|
||||
* @secid contains the secid from which to take the mls portion of the context.
|
||||
* Allocate a security structure to the x->security field; the security
|
||||
* field is initialized to NULL when the xfrm_state is allocated. Set the
|
||||
* context to correspond to either sec_ctx or polsec, with the mls portion
|
||||
* taken from secid in the latter case.
|
||||
* Return 0 if operation was successful (memory to allocate, legal context).
|
||||
* context to correspond to sec_ctx. Return 0 if operation was successful
|
||||
* (memory to allocate, legal context).
|
||||
* @xfrm_state_alloc_acquire:
|
||||
* @x contains the xfrm_state being added to the Security Association
|
||||
* Database by the XFRM system.
|
||||
* @polsec contains the policy's security context.
|
||||
* @secid contains the secid from which to take the mls portion of the
|
||||
* context.
|
||||
* Allocate a security structure to the x->security field; the security
|
||||
* field is initialized to NULL when the xfrm_state is allocated. Set the
|
||||
* context to correspond to secid. Return 0 if operation was successful
|
||||
* (memory to allocate, legal context).
|
||||
* @xfrm_state_free_security:
|
||||
* @x contains the xfrm_state.
|
||||
* Deallocate x->security.
|
||||
|
@ -1679,9 +1687,11 @@ struct security_operations {
|
|||
int (*xfrm_policy_clone_security) (struct xfrm_sec_ctx *old_ctx, struct xfrm_sec_ctx **new_ctx);
|
||||
void (*xfrm_policy_free_security) (struct xfrm_sec_ctx *ctx);
|
||||
int (*xfrm_policy_delete_security) (struct xfrm_sec_ctx *ctx);
|
||||
int (*xfrm_state_alloc_security) (struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx,
|
||||
u32 secid);
|
||||
int (*xfrm_state_alloc) (struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx);
|
||||
int (*xfrm_state_alloc_acquire) (struct xfrm_state *x,
|
||||
struct xfrm_sec_ctx *polsec,
|
||||
u32 secid);
|
||||
void (*xfrm_state_free_security) (struct xfrm_state *x);
|
||||
int (*xfrm_state_delete_security) (struct xfrm_state *x);
|
||||
int (*xfrm_policy_lookup) (struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);
|
||||
|
|
|
@ -777,9 +777,15 @@ static int cap_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cap_xfrm_state_alloc_security(struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx,
|
||||
u32 secid)
|
||||
static int cap_xfrm_state_alloc(struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_xfrm_state_alloc_acquire(struct xfrm_state *x,
|
||||
struct xfrm_sec_ctx *polsec,
|
||||
u32 secid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -1101,7 +1107,8 @@ void __init security_fixup_ops(struct security_operations *ops)
|
|||
set_to_cap_if_null(ops, xfrm_policy_clone_security);
|
||||
set_to_cap_if_null(ops, xfrm_policy_free_security);
|
||||
set_to_cap_if_null(ops, xfrm_policy_delete_security);
|
||||
set_to_cap_if_null(ops, xfrm_state_alloc_security);
|
||||
set_to_cap_if_null(ops, xfrm_state_alloc);
|
||||
set_to_cap_if_null(ops, xfrm_state_alloc_acquire);
|
||||
set_to_cap_if_null(ops, xfrm_state_free_security);
|
||||
set_to_cap_if_null(ops, xfrm_state_delete_security);
|
||||
set_to_cap_if_null(ops, xfrm_policy_lookup);
|
||||
|
|
|
@ -1342,22 +1342,17 @@ int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
|
|||
return security_ops->xfrm_policy_delete_security(ctx);
|
||||
}
|
||||
|
||||
int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
|
||||
int security_xfrm_state_alloc(struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx)
|
||||
{
|
||||
return security_ops->xfrm_state_alloc_security(x, sec_ctx, 0);
|
||||
return security_ops->xfrm_state_alloc(x, sec_ctx);
|
||||
}
|
||||
EXPORT_SYMBOL(security_xfrm_state_alloc);
|
||||
|
||||
int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
|
||||
struct xfrm_sec_ctx *polsec, u32 secid)
|
||||
{
|
||||
if (!polsec)
|
||||
return 0;
|
||||
/*
|
||||
* We want the context to be taken from secid which is usually
|
||||
* from the sock.
|
||||
*/
|
||||
return security_ops->xfrm_state_alloc_security(x, NULL, secid);
|
||||
return security_ops->xfrm_state_alloc_acquire(x, polsec, secid);
|
||||
}
|
||||
|
||||
int security_xfrm_state_delete(struct xfrm_state *x)
|
||||
|
|
|
@ -95,7 +95,9 @@
|
|||
#include "audit.h"
|
||||
#include "avc_ss.h"
|
||||
|
||||
#define NUM_SEL_MNT_OPTS 5
|
||||
#define SB_TYPE_FMT "%s%s%s"
|
||||
#define SB_SUBTYPE(sb) (sb->s_subtype && sb->s_subtype[0])
|
||||
#define SB_TYPE_ARGS(sb) sb->s_type->name, SB_SUBTYPE(sb) ? "." : "", SB_SUBTYPE(sb) ? sb->s_subtype : ""
|
||||
|
||||
extern struct security_operations *security_ops;
|
||||
|
||||
|
@ -139,12 +141,28 @@ static struct kmem_cache *sel_inode_cache;
|
|||
* This function checks the SECMARK reference counter to see if any SECMARK
|
||||
* targets are currently configured, if the reference counter is greater than
|
||||
* zero SECMARK is considered to be enabled. Returns true (1) if SECMARK is
|
||||
* enabled, false (0) if SECMARK is disabled.
|
||||
* enabled, false (0) if SECMARK is disabled. If the always_check_network
|
||||
* policy capability is enabled, SECMARK is always considered enabled.
|
||||
*
|
||||
*/
|
||||
static int selinux_secmark_enabled(void)
|
||||
{
|
||||
return (atomic_read(&selinux_secmark_refcount) > 0);
|
||||
return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount));
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_peerlbl_enabled - Check to see if peer labeling is currently enabled
|
||||
*
|
||||
* Description:
|
||||
* This function checks if NetLabel or labeled IPSEC is enabled. Returns true
|
||||
* (1) if any are enabled or false (0) if neither are enabled. If the
|
||||
* always_check_network policy capability is enabled, peer labeling
|
||||
* is always considered enabled.
|
||||
*
|
||||
*/
|
||||
static int selinux_peerlbl_enabled(void)
|
||||
{
|
||||
return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -309,8 +327,11 @@ enum {
|
|||
Opt_defcontext = 3,
|
||||
Opt_rootcontext = 4,
|
||||
Opt_labelsupport = 5,
|
||||
Opt_nextmntopt = 6,
|
||||
};
|
||||
|
||||
#define NUM_SEL_MNT_OPTS (Opt_nextmntopt - 1)
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{Opt_context, CONTEXT_STR "%s"},
|
||||
{Opt_fscontext, FSCONTEXT_STR "%s"},
|
||||
|
@ -355,6 +376,29 @@ static int may_context_mount_inode_relabel(u32 sid,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_is_sblabel_mnt(struct super_block *sb)
|
||||
{
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
|
||||
if (sbsec->behavior == SECURITY_FS_USE_XATTR ||
|
||||
sbsec->behavior == SECURITY_FS_USE_TRANS ||
|
||||
sbsec->behavior == SECURITY_FS_USE_TASK)
|
||||
return 1;
|
||||
|
||||
/* Special handling for sysfs. Is genfs but also has setxattr handler*/
|
||||
if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Special handling for rootfs. Is genfs but supports
|
||||
* setting SELinux context on in-core inodes.
|
||||
*/
|
||||
if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sb_finish_set_opts(struct super_block *sb)
|
||||
{
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
|
@ -369,8 +413,8 @@ static int sb_finish_set_opts(struct super_block *sb)
|
|||
the first boot of the SELinux kernel before we have
|
||||
assigned xattr values to the filesystem. */
|
||||
if (!root_inode->i_op->getxattr) {
|
||||
printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
|
||||
"xattr support\n", sb->s_id, sb->s_type->name);
|
||||
printk(KERN_WARNING "SELinux: (dev %s, type "SB_TYPE_FMT") has no "
|
||||
"xattr support\n", sb->s_id, SB_TYPE_ARGS(sb));
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
@ -378,35 +422,27 @@ static int sb_finish_set_opts(struct super_block *sb)
|
|||
if (rc < 0 && rc != -ENODATA) {
|
||||
if (rc == -EOPNOTSUPP)
|
||||
printk(KERN_WARNING "SELinux: (dev %s, type "
|
||||
"%s) has no security xattr handler\n",
|
||||
sb->s_id, sb->s_type->name);
|
||||
SB_TYPE_FMT") has no security xattr handler\n",
|
||||
sb->s_id, SB_TYPE_ARGS(sb));
|
||||
else
|
||||
printk(KERN_WARNING "SELinux: (dev %s, type "
|
||||
"%s) getxattr errno %d\n", sb->s_id,
|
||||
sb->s_type->name, -rc);
|
||||
SB_TYPE_FMT") getxattr errno %d\n", sb->s_id,
|
||||
SB_TYPE_ARGS(sb), -rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP);
|
||||
|
||||
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);
|
||||
printk(KERN_ERR "SELinux: initialized (dev %s, type "SB_TYPE_FMT"), unknown behavior\n",
|
||||
sb->s_id, SB_TYPE_ARGS(sb));
|
||||
else
|
||||
printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
|
||||
sb->s_id, sb->s_type->name,
|
||||
printk(KERN_DEBUG "SELinux: initialized (dev %s, type "SB_TYPE_FMT"), %s\n",
|
||||
sb->s_id, SB_TYPE_ARGS(sb),
|
||||
labeling_behaviors[sbsec->behavior-1]);
|
||||
|
||||
if (sbsec->behavior == SECURITY_FS_USE_GENFS ||
|
||||
sbsec->behavior == SECURITY_FS_USE_MNTPOINT ||
|
||||
sbsec->behavior == SECURITY_FS_USE_NONE ||
|
||||
sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
|
||||
sbsec->flags &= ~SE_SBLABELSUPP;
|
||||
|
||||
/* Special handling for sysfs. Is genfs but also has setxattr handler*/
|
||||
if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
|
||||
sbsec->flags |= SE_SBLABELSUPP;
|
||||
sbsec->flags |= SE_SBINITIALIZED;
|
||||
if (selinux_is_sblabel_mnt(sb))
|
||||
sbsec->flags |= SBLABEL_MNT;
|
||||
|
||||
/* Initialize the root inode. */
|
||||
rc = inode_doinit_with_dentry(root_inode, root);
|
||||
|
@ -460,15 +496,18 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
|
|||
if (!ss_initialized)
|
||||
return -EINVAL;
|
||||
|
||||
/* make sure we always check enough bits to cover the mask */
|
||||
BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS));
|
||||
|
||||
tmp = sbsec->flags & SE_MNTMASK;
|
||||
/* count the number of mount options for this sb */
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (i = 0; i < NUM_SEL_MNT_OPTS; i++) {
|
||||
if (tmp & 0x01)
|
||||
opts->num_mnt_opts++;
|
||||
tmp >>= 1;
|
||||
}
|
||||
/* Check if the Label support flag is set */
|
||||
if (sbsec->flags & SE_SBLABELSUPP)
|
||||
if (sbsec->flags & SBLABEL_MNT)
|
||||
opts->num_mnt_opts++;
|
||||
|
||||
opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
|
||||
|
@ -515,9 +554,9 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
|
|||
opts->mnt_opts[i] = context;
|
||||
opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
|
||||
}
|
||||
if (sbsec->flags & SE_SBLABELSUPP) {
|
||||
if (sbsec->flags & SBLABEL_MNT) {
|
||||
opts->mnt_opts[i] = NULL;
|
||||
opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
|
||||
opts->mnt_opts_flags[i++] = SBLABEL_MNT;
|
||||
}
|
||||
|
||||
BUG_ON(i != opts->num_mnt_opts);
|
||||
|
@ -561,7 +600,6 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
|||
const struct cred *cred = current_cred();
|
||||
int rc = 0, i;
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
const char *name = sb->s_type->name;
|
||||
struct inode *inode = sbsec->sb->s_root->d_inode;
|
||||
struct inode_security_struct *root_isec = inode->i_security;
|
||||
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
|
||||
|
@ -614,14 +652,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
|||
for (i = 0; i < num_opts; i++) {
|
||||
u32 sid;
|
||||
|
||||
if (flags[i] == SE_SBLABELSUPP)
|
||||
if (flags[i] == SBLABEL_MNT)
|
||||
continue;
|
||||
rc = security_context_to_sid(mount_options[i],
|
||||
strlen(mount_options[i]), &sid);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING "SELinux: security_context_to_sid"
|
||||
"(%s) failed for (dev %s, type %s) errno=%d\n",
|
||||
mount_options[i], sb->s_id, name, rc);
|
||||
"(%s) failed for (dev %s, type "SB_TYPE_FMT") errno=%d\n",
|
||||
mount_options[i], sb->s_id, SB_TYPE_ARGS(sb), rc);
|
||||
goto out;
|
||||
}
|
||||
switch (flags[i]) {
|
||||
|
@ -685,9 +723,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
|||
* Determine the labeling behavior to use for this
|
||||
* filesystem type.
|
||||
*/
|
||||
rc = security_fs_use((sbsec->flags & SE_SBPROC) ?
|
||||
"proc" : sb->s_type->name,
|
||||
&sbsec->behavior, &sbsec->sid);
|
||||
rc = security_fs_use(sb);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING
|
||||
"%s: security_fs_use(%s) returned %d\n",
|
||||
|
@ -770,7 +806,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
|||
out_double_mount:
|
||||
rc = -EINVAL;
|
||||
printk(KERN_WARNING "SELinux: mount invalid. Same superblock, different "
|
||||
"security settings for (dev %s, type %s)\n", sb->s_id, name);
|
||||
"security settings for (dev %s, type "SB_TYPE_FMT")\n", sb->s_id,
|
||||
SB_TYPE_ARGS(sb));
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1037,7 +1074,7 @@ static void selinux_write_opts(struct seq_file *m,
|
|||
case DEFCONTEXT_MNT:
|
||||
prefix = DEFCONTEXT_STR;
|
||||
break;
|
||||
case SE_SBLABELSUPP:
|
||||
case SBLABEL_MNT:
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, LABELSUPP_STR);
|
||||
continue;
|
||||
|
@ -1650,7 +1687,7 @@ static int may_create(struct inode *dir,
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
|
||||
if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
|
||||
rc = security_transition_sid(sid, dsec->sid, tclass,
|
||||
&dentry->d_name, &newsid);
|
||||
if (rc)
|
||||
|
@ -2438,14 +2475,14 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
|
|||
u32 sid;
|
||||
size_t len;
|
||||
|
||||
if (flags[i] == SE_SBLABELSUPP)
|
||||
if (flags[i] == SBLABEL_MNT)
|
||||
continue;
|
||||
len = strlen(mount_options[i]);
|
||||
rc = security_context_to_sid(mount_options[i], len, &sid);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING "SELinux: security_context_to_sid"
|
||||
"(%s) failed for (dev %s, type %s) errno=%d\n",
|
||||
mount_options[i], sb->s_id, sb->s_type->name, rc);
|
||||
"(%s) failed for (dev %s, type "SB_TYPE_FMT") errno=%d\n",
|
||||
mount_options[i], sb->s_id, SB_TYPE_ARGS(sb), rc);
|
||||
goto out_free_opts;
|
||||
}
|
||||
rc = -EINVAL;
|
||||
|
@ -2483,8 +2520,8 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
|
|||
return rc;
|
||||
out_bad_option:
|
||||
printk(KERN_WARNING "SELinux: unable to change security options "
|
||||
"during remount (dev %s, type=%s)\n", sb->s_id,
|
||||
sb->s_type->name);
|
||||
"during remount (dev %s, type "SB_TYPE_FMT")\n", sb->s_id,
|
||||
SB_TYPE_ARGS(sb));
|
||||
goto out_free_opts;
|
||||
}
|
||||
|
||||
|
@ -2606,7 +2643,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
|||
if ((sbsec->flags & SE_SBINITIALIZED) &&
|
||||
(sbsec->behavior == SECURITY_FS_USE_MNTPOINT))
|
||||
newsid = sbsec->mntpoint_sid;
|
||||
else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
|
||||
else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
|
||||
rc = security_transition_sid(sid, dsec->sid,
|
||||
inode_mode_to_security_class(inode->i_mode),
|
||||
qstr, &newsid);
|
||||
|
@ -2628,7 +2665,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
|||
isec->initialized = 1;
|
||||
}
|
||||
|
||||
if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP))
|
||||
if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (name) {
|
||||
|
@ -2836,7 +2873,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
|||
return selinux_inode_setotherxattr(dentry, name);
|
||||
|
||||
sbsec = inode->i_sb->s_security;
|
||||
if (!(sbsec->flags & SE_SBLABELSUPP))
|
||||
if (!(sbsec->flags & SBLABEL_MNT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
|
@ -3797,8 +3834,12 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
|
|||
u32 nlbl_sid;
|
||||
u32 nlbl_type;
|
||||
|
||||
selinux_skb_xfrm_sid(skb, &xfrm_sid);
|
||||
selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
|
||||
err = selinux_skb_xfrm_sid(skb, &xfrm_sid);
|
||||
if (unlikely(err))
|
||||
return -EACCES;
|
||||
err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
|
||||
if (unlikely(err))
|
||||
return -EACCES;
|
||||
|
||||
err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);
|
||||
if (unlikely(err)) {
|
||||
|
@ -4252,7 +4293,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
|||
return selinux_sock_rcv_skb_compat(sk, skb, family);
|
||||
|
||||
secmark_active = selinux_secmark_enabled();
|
||||
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
|
||||
peerlbl_active = selinux_peerlbl_enabled();
|
||||
if (!secmark_active && !peerlbl_active)
|
||||
return 0;
|
||||
|
||||
|
@ -4634,7 +4675,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
|
|||
|
||||
secmark_active = selinux_secmark_enabled();
|
||||
netlbl_active = netlbl_enabled();
|
||||
peerlbl_active = netlbl_active || selinux_xfrm_enabled();
|
||||
peerlbl_active = selinux_peerlbl_enabled();
|
||||
if (!secmark_active && !peerlbl_active)
|
||||
return NF_ACCEPT;
|
||||
|
||||
|
@ -4786,7 +4827,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
|
|||
return NF_ACCEPT;
|
||||
#endif
|
||||
secmark_active = selinux_secmark_enabled();
|
||||
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
|
||||
peerlbl_active = selinux_peerlbl_enabled();
|
||||
if (!secmark_active && !peerlbl_active)
|
||||
return NF_ACCEPT;
|
||||
|
||||
|
@ -5790,7 +5831,8 @@ static struct security_operations selinux_ops = {
|
|||
.xfrm_policy_clone_security = selinux_xfrm_policy_clone,
|
||||
.xfrm_policy_free_security = selinux_xfrm_policy_free,
|
||||
.xfrm_policy_delete_security = selinux_xfrm_policy_delete,
|
||||
.xfrm_state_alloc_security = selinux_xfrm_state_alloc,
|
||||
.xfrm_state_alloc = selinux_xfrm_state_alloc,
|
||||
.xfrm_state_alloc_acquire = selinux_xfrm_state_alloc_acquire,
|
||||
.xfrm_state_free_security = selinux_xfrm_state_free,
|
||||
.xfrm_state_delete_security = selinux_xfrm_state_delete,
|
||||
.xfrm_policy_lookup = selinux_xfrm_policy_lookup,
|
||||
|
|
|
@ -58,8 +58,8 @@ struct superblock_security_struct {
|
|||
u32 sid; /* SID of file system superblock */
|
||||
u32 def_sid; /* default SID for labeling */
|
||||
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
|
||||
unsigned int behavior; /* labeling behavior */
|
||||
unsigned char flags; /* which mount options were specified */
|
||||
unsigned short behavior; /* labeling behavior */
|
||||
unsigned short flags; /* which mount options were specified */
|
||||
struct mutex lock;
|
||||
struct list_head isec_head;
|
||||
spinlock_t isec_lock;
|
||||
|
|
|
@ -45,14 +45,15 @@
|
|||
/* Mask for just the mount related flags */
|
||||
#define SE_MNTMASK 0x0f
|
||||
/* Super block security struct flags for mount options */
|
||||
/* BE CAREFUL, these need to be the low order bits for selinux_get_mnt_opts */
|
||||
#define CONTEXT_MNT 0x01
|
||||
#define FSCONTEXT_MNT 0x02
|
||||
#define ROOTCONTEXT_MNT 0x04
|
||||
#define DEFCONTEXT_MNT 0x08
|
||||
#define SBLABEL_MNT 0x10
|
||||
/* Non-mount related flags */
|
||||
#define SE_SBINITIALIZED 0x10
|
||||
#define SE_SBPROC 0x20
|
||||
#define SE_SBLABELSUPP 0x40
|
||||
#define SE_SBINITIALIZED 0x0100
|
||||
#define SE_SBPROC 0x0200
|
||||
|
||||
#define CONTEXT_STR "context="
|
||||
#define FSCONTEXT_STR "fscontext="
|
||||
|
@ -68,12 +69,15 @@ extern int selinux_enabled;
|
|||
enum {
|
||||
POLICYDB_CAPABILITY_NETPEER,
|
||||
POLICYDB_CAPABILITY_OPENPERM,
|
||||
POLICYDB_CAPABILITY_REDHAT1,
|
||||
POLICYDB_CAPABILITY_ALWAYSNETWORK,
|
||||
__POLICYDB_CAPABILITY_MAX
|
||||
};
|
||||
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
|
||||
|
||||
extern int selinux_policycap_netpeer;
|
||||
extern int selinux_policycap_openperm;
|
||||
extern int selinux_policycap_alwaysnetwork;
|
||||
|
||||
/*
|
||||
* type_datum properties
|
||||
|
@ -172,8 +176,7 @@ int security_get_allow_unknown(void);
|
|||
#define SECURITY_FS_USE_NATIVE 7 /* use native label support */
|
||||
#define SECURITY_FS_USE_MAX 7 /* Highest SECURITY_FS_USE_XXX */
|
||||
|
||||
int security_fs_use(const char *fstype, unsigned int *behavior,
|
||||
u32 *sid);
|
||||
int security_fs_use(struct super_block *sb);
|
||||
|
||||
int security_genfs_sid(const char *fstype, char *name, u16 sclass,
|
||||
u32 *sid);
|
||||
|
|
|
@ -10,29 +10,21 @@
|
|||
#include <net/flow.h>
|
||||
|
||||
int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
|
||||
struct xfrm_user_sec_ctx *sec_ctx);
|
||||
struct xfrm_user_sec_ctx *uctx);
|
||||
int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
|
||||
struct xfrm_sec_ctx **new_ctxp);
|
||||
void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx);
|
||||
int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx);
|
||||
int selinux_xfrm_state_alloc(struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx, u32 secid);
|
||||
struct xfrm_user_sec_ctx *uctx);
|
||||
int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
|
||||
struct xfrm_sec_ctx *polsec, u32 secid);
|
||||
void selinux_xfrm_state_free(struct xfrm_state *x);
|
||||
int selinux_xfrm_state_delete(struct xfrm_state *x);
|
||||
int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);
|
||||
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
|
||||
struct xfrm_policy *xp, const struct flowi *fl);
|
||||
|
||||
/*
|
||||
* Extract the security blob from the sock (it's actually on the socket)
|
||||
*/
|
||||
static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
|
||||
{
|
||||
if (!sk->sk_socket)
|
||||
return NULL;
|
||||
|
||||
return SOCK_INODE(sk->sk_socket)->i_security;
|
||||
}
|
||||
struct xfrm_policy *xp,
|
||||
const struct flowi *fl);
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
extern atomic_t selinux_xfrm_refcount;
|
||||
|
@ -42,10 +34,10 @@ static inline int selinux_xfrm_enabled(void)
|
|||
return (atomic_read(&selinux_xfrm_refcount) > 0);
|
||||
}
|
||||
|
||||
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad);
|
||||
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 proto);
|
||||
int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad);
|
||||
int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 proto);
|
||||
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
|
||||
|
||||
static inline void selinux_xfrm_notify_policyload(void)
|
||||
|
@ -59,19 +51,21 @@ static inline int selinux_xfrm_enabled(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad)
|
||||
static inline int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 proto)
|
||||
static inline int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad,
|
||||
u8 proto)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
|
||||
static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid,
|
||||
int ckall)
|
||||
{
|
||||
*sid = SECSID_NULL;
|
||||
return 0;
|
||||
|
@ -82,10 +76,9 @@ static inline void selinux_xfrm_notify_policyload(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
|
||||
static inline int selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
|
||||
{
|
||||
int err = selinux_xfrm_decode_session(skb, sid, 0);
|
||||
BUG_ON(err);
|
||||
return selinux_xfrm_decode_session(skb, sid, 0);
|
||||
}
|
||||
|
||||
#endif /* _SELINUX_XFRM_H_ */
|
||||
|
|
|
@ -166,6 +166,7 @@ static void sel_netnode_insert(struct sel_netnode *node)
|
|||
break;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
/* we need to impose a limit on the growth of the hash table so check
|
||||
|
@ -225,6 +226,7 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
|
|||
break;
|
||||
default:
|
||||
BUG();
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
|
|
@ -44,7 +44,9 @@
|
|||
/* Policy capability filenames */
|
||||
static char *policycap_names[] = {
|
||||
"network_peer_controls",
|
||||
"open_perms"
|
||||
"open_perms",
|
||||
"redhat1",
|
||||
"always_check_network"
|
||||
};
|
||||
|
||||
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
|
||||
|
|
|
@ -213,7 +213,12 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
|
|||
}
|
||||
#endif /* CONFIG_NETLABEL */
|
||||
|
||||
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
|
||||
/*
|
||||
* Check to see if all the bits set in e2 are also set in e1. Optionally,
|
||||
* if last_e2bit is non-zero, the highest set bit in e2 cannot exceed
|
||||
* last_e2bit.
|
||||
*/
|
||||
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit)
|
||||
{
|
||||
struct ebitmap_node *n1, *n2;
|
||||
int i;
|
||||
|
@ -223,14 +228,25 @@ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
|
|||
|
||||
n1 = e1->node;
|
||||
n2 = e2->node;
|
||||
|
||||
while (n1 && n2 && (n1->startbit <= n2->startbit)) {
|
||||
if (n1->startbit < n2->startbit) {
|
||||
n1 = n1->next;
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
|
||||
for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i]; )
|
||||
i--; /* Skip trailing NULL map entries */
|
||||
if (last_e2bit && (i >= 0)) {
|
||||
u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE +
|
||||
__fls(n2->maps[i]);
|
||||
if (lastsetbit > last_e2bit)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (i >= 0) {
|
||||
if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])
|
||||
return 0;
|
||||
i--;
|
||||
}
|
||||
|
||||
n1 = n1->next;
|
||||
|
|
|
@ -16,7 +16,13 @@
|
|||
|
||||
#include <net/netlabel.h>
|
||||
|
||||
#define EBITMAP_UNIT_NUMS ((32 - sizeof(void *) - sizeof(u32)) \
|
||||
#ifdef CONFIG_64BIT
|
||||
#define EBITMAP_NODE_SIZE 64
|
||||
#else
|
||||
#define EBITMAP_NODE_SIZE 32
|
||||
#endif
|
||||
|
||||
#define EBITMAP_UNIT_NUMS ((EBITMAP_NODE_SIZE-sizeof(void *)-sizeof(u32))\
|
||||
/ sizeof(unsigned long))
|
||||
#define EBITMAP_UNIT_SIZE BITS_PER_LONG
|
||||
#define EBITMAP_SIZE (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE)
|
||||
|
@ -117,7 +123,7 @@ static inline void ebitmap_node_clr_bit(struct ebitmap_node *n,
|
|||
|
||||
int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
|
||||
int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
|
||||
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
|
||||
int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit);
|
||||
int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);
|
||||
int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
|
||||
void ebitmap_destroy(struct ebitmap *e);
|
||||
|
|
|
@ -160,8 +160,6 @@ void mls_sid_to_context(struct context *context,
|
|||
int mls_level_isvalid(struct policydb *p, struct mls_level *l)
|
||||
{
|
||||
struct level_datum *levdatum;
|
||||
struct ebitmap_node *node;
|
||||
int i;
|
||||
|
||||
if (!l->sens || l->sens > p->p_levels.nprim)
|
||||
return 0;
|
||||
|
@ -170,19 +168,13 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)
|
|||
if (!levdatum)
|
||||
return 0;
|
||||
|
||||
ebitmap_for_each_positive_bit(&l->cat, node, i) {
|
||||
if (i > p->p_cats.nprim)
|
||||
return 0;
|
||||
if (!ebitmap_get_bit(&levdatum->level->cat, i)) {
|
||||
/*
|
||||
* Category may not be associated with
|
||||
* sensitivity.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
/*
|
||||
* Return 1 iff all the bits set in l->cat are also be set in
|
||||
* levdatum->level->cat and no bit in l->cat is larger than
|
||||
* p->p_cats.nprim.
|
||||
*/
|
||||
return ebitmap_contains(&levdatum->level->cat, &l->cat,
|
||||
p->p_cats.nprim);
|
||||
}
|
||||
|
||||
int mls_range_isvalid(struct policydb *p, struct mls_range *r)
|
||||
|
|
|
@ -35,7 +35,7 @@ static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)
|
|||
static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)
|
||||
{
|
||||
return ((l1->sens >= l2->sens) &&
|
||||
ebitmap_contains(&l1->cat, &l2->cat));
|
||||
ebitmap_contains(&l1->cat, &l2->cat, 0));
|
||||
}
|
||||
|
||||
#define mls_level_incomp(l1, l2) \
|
||||
|
|
|
@ -3203,9 +3203,8 @@ static int range_write_helper(void *key, void *data, void *ptr)
|
|||
|
||||
static int range_write(struct policydb *p, void *fp)
|
||||
{
|
||||
size_t nel;
|
||||
__le32 buf[1];
|
||||
int rc;
|
||||
int rc, nel;
|
||||
struct policy_data pd;
|
||||
|
||||
pd.p = p;
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
|
||||
int selinux_policycap_netpeer;
|
||||
int selinux_policycap_openperm;
|
||||
int selinux_policycap_alwaysnetwork;
|
||||
|
||||
static DEFINE_RWLOCK(policy_rwlock);
|
||||
|
||||
|
@ -1812,6 +1813,8 @@ static void security_load_policycaps(void)
|
|||
POLICYDB_CAPABILITY_NETPEER);
|
||||
selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
|
||||
POLICYDB_CAPABILITY_OPENPERM);
|
||||
selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
|
||||
POLICYDB_CAPABILITY_ALWAYSNETWORK);
|
||||
}
|
||||
|
||||
static int security_preserve_bools(struct policydb *p);
|
||||
|
@ -2323,43 +2326,74 @@ int security_genfs_sid(const char *fstype,
|
|||
|
||||
/**
|
||||
* security_fs_use - Determine how to handle labeling for a filesystem.
|
||||
* @fstype: filesystem type
|
||||
* @behavior: labeling behavior
|
||||
* @sid: SID for filesystem (superblock)
|
||||
* @sb: superblock in question
|
||||
*/
|
||||
int security_fs_use(
|
||||
const char *fstype,
|
||||
unsigned int *behavior,
|
||||
u32 *sid)
|
||||
int security_fs_use(struct super_block *sb)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ocontext *c;
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
const char *fstype = sb->s_type->name;
|
||||
const char *subtype = (sb->s_subtype && sb->s_subtype[0]) ? sb->s_subtype : NULL;
|
||||
struct ocontext *base = NULL;
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
|
||||
c = policydb.ocontexts[OCON_FSUSE];
|
||||
while (c) {
|
||||
if (strcmp(fstype, c->u.name) == 0)
|
||||
for (c = policydb.ocontexts[OCON_FSUSE]; c; c = c->next) {
|
||||
char *sub;
|
||||
int baselen;
|
||||
|
||||
baselen = strlen(fstype);
|
||||
|
||||
/* if base does not match, this is not the one */
|
||||
if (strncmp(fstype, c->u.name, baselen))
|
||||
continue;
|
||||
|
||||
/* if there is no subtype, this is the one! */
|
||||
if (!subtype)
|
||||
break;
|
||||
|
||||
/* skip past the base in this entry */
|
||||
sub = c->u.name + baselen;
|
||||
|
||||
/* entry is only a base. save it. keep looking for subtype */
|
||||
if (sub[0] == '\0') {
|
||||
base = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* entry is not followed by a subtype, so it is not a match */
|
||||
if (sub[0] != '.')
|
||||
continue;
|
||||
|
||||
/* whew, we found a subtype of this fstype */
|
||||
sub++; /* move past '.' */
|
||||
|
||||
/* exact match of fstype AND subtype */
|
||||
if (!strcmp(subtype, sub))
|
||||
break;
|
||||
c = c->next;
|
||||
}
|
||||
|
||||
/* in case we had found an fstype match but no subtype match */
|
||||
if (!c)
|
||||
c = base;
|
||||
|
||||
if (c) {
|
||||
*behavior = c->v.behavior;
|
||||
sbsec->behavior = c->v.behavior;
|
||||
if (!c->sid[0]) {
|
||||
rc = sidtab_context_to_sid(&sidtab, &c->context[0],
|
||||
&c->sid[0]);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
*sid = c->sid[0];
|
||||
sbsec->sid = c->sid[0];
|
||||
} else {
|
||||
rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
|
||||
rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, &sbsec->sid);
|
||||
if (rc) {
|
||||
*behavior = SECURITY_FS_USE_NONE;
|
||||
sbsec->behavior = SECURITY_FS_USE_NONE;
|
||||
rc = 0;
|
||||
} else {
|
||||
*behavior = SECURITY_FS_USE_GENFS;
|
||||
sbsec->behavior = SECURITY_FS_USE_GENFS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);
|
||||
|
||||
/*
|
||||
* Returns true if an LSM/SELinux context
|
||||
* Returns true if the context is an LSM/SELinux context.
|
||||
*/
|
||||
static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
|
||||
{
|
||||
|
@ -66,7 +66,7 @@ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
|
|||
}
|
||||
|
||||
/*
|
||||
* Returns true if the xfrm contains a security blob for SELinux
|
||||
* Returns true if the xfrm contains a security blob for SELinux.
|
||||
*/
|
||||
static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
|
||||
{
|
||||
|
@ -74,48 +74,111 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
|
|||
}
|
||||
|
||||
/*
|
||||
* LSM hook implementation that authorizes that a flow can use
|
||||
* a xfrm policy rule.
|
||||
* Allocates a xfrm_sec_state and populates it using the supplied security
|
||||
* xfrm_user_sec_ctx context.
|
||||
*/
|
||||
static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
|
||||
struct xfrm_user_sec_ctx *uctx)
|
||||
{
|
||||
int rc;
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
struct xfrm_sec_ctx *ctx = NULL;
|
||||
u32 str_len;
|
||||
|
||||
if (ctxp == NULL || uctx == NULL ||
|
||||
uctx->ctx_doi != XFRM_SC_DOI_LSM ||
|
||||
uctx->ctx_alg != XFRM_SC_ALG_SELINUX)
|
||||
return -EINVAL;
|
||||
|
||||
str_len = uctx->ctx_len;
|
||||
if (str_len >= PAGE_SIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx = kmalloc(sizeof(*ctx) + str_len + 1, GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->ctx_doi = XFRM_SC_DOI_LSM;
|
||||
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
|
||||
ctx->ctx_len = str_len;
|
||||
memcpy(ctx->ctx_str, &uctx[1], str_len);
|
||||
ctx->ctx_str[str_len] = '\0';
|
||||
rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
*ctxp = ctx;
|
||||
atomic_inc(&selinux_xfrm_refcount);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the xfrm_sec_ctx structure.
|
||||
*/
|
||||
static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
atomic_dec(&selinux_xfrm_refcount);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Authorize the deletion of a labeled SA or policy rule.
|
||||
*/
|
||||
static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
return avc_has_perm(tsec->sid, ctx->ctx_sid,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* LSM hook implementation that authorizes that a flow can use a xfrm policy
|
||||
* rule.
|
||||
*/
|
||||
int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
|
||||
{
|
||||
int rc;
|
||||
u32 sel_sid;
|
||||
|
||||
/* Context sid is either set to label or ANY_ASSOC */
|
||||
if (ctx) {
|
||||
if (!selinux_authorizable_ctx(ctx))
|
||||
return -EINVAL;
|
||||
|
||||
sel_sid = ctx->ctx_sid;
|
||||
} else
|
||||
/*
|
||||
* All flows should be treated as polmatch'ing an
|
||||
* otherwise applicable "non-labeled" policy. This
|
||||
* would prevent inadvertent "leaks".
|
||||
*/
|
||||
/* All flows should be treated as polmatch'ing an otherwise applicable
|
||||
* "non-labeled" policy. This would prevent inadvertent "leaks". */
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
|
||||
ASSOCIATION__POLMATCH,
|
||||
NULL);
|
||||
/* Context sid is either set to label or ANY_ASSOC */
|
||||
if (!selinux_authorizable_ctx(ctx))
|
||||
return -EINVAL;
|
||||
|
||||
if (rc == -EACCES)
|
||||
return -ESRCH;
|
||||
|
||||
return rc;
|
||||
rc = avc_has_perm(fl_secid, ctx->ctx_sid,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL);
|
||||
return (rc == -EACCES ? -ESRCH : rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* LSM hook implementation that authorizes that a state matches
|
||||
* the given policy, flow combo.
|
||||
*/
|
||||
|
||||
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp,
|
||||
const struct flowi *fl)
|
||||
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
|
||||
struct xfrm_policy *xp,
|
||||
const struct flowi *fl)
|
||||
{
|
||||
u32 state_sid;
|
||||
int rc;
|
||||
|
||||
if (!xp->security)
|
||||
if (x->security)
|
||||
|
@ -138,187 +201,80 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *
|
|||
if (fl->flowi_secid != state_sid)
|
||||
return 0;
|
||||
|
||||
rc = avc_has_perm(fl->flowi_secid, state_sid, SECCLASS_ASSOCIATION,
|
||||
ASSOCIATION__SENDTO,
|
||||
NULL)? 0:1;
|
||||
|
||||
/*
|
||||
* We don't need a separate SA Vs. policy polmatch check
|
||||
* since the SA is now of the same label as the flow and
|
||||
* a flow Vs. policy polmatch check had already happened
|
||||
* in selinux_xfrm_policy_lookup() above.
|
||||
*/
|
||||
|
||||
return rc;
|
||||
/* We don't need a separate SA Vs. policy polmatch check since the SA
|
||||
* is now of the same label as the flow and a flow Vs. policy polmatch
|
||||
* check had already happened in selinux_xfrm_policy_lookup() above. */
|
||||
return (avc_has_perm(fl->flowi_secid, state_sid,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO,
|
||||
NULL) ? 0 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* LSM hook implementation that checks and/or returns the xfrm sid for the
|
||||
* incoming packet.
|
||||
*/
|
||||
|
||||
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
|
||||
{
|
||||
u32 sid_session = SECSID_NULL;
|
||||
struct sec_path *sp;
|
||||
|
||||
*sid = SECSID_NULL;
|
||||
|
||||
if (skb == NULL)
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
sp = skb->sp;
|
||||
if (sp) {
|
||||
int i, sid_set = 0;
|
||||
int i;
|
||||
|
||||
for (i = sp->len-1; i >= 0; i--) {
|
||||
for (i = sp->len - 1; i >= 0; i--) {
|
||||
struct xfrm_state *x = sp->xvec[i];
|
||||
if (selinux_authorizable_xfrm(x)) {
|
||||
struct xfrm_sec_ctx *ctx = x->security;
|
||||
|
||||
if (!sid_set) {
|
||||
*sid = ctx->ctx_sid;
|
||||
sid_set = 1;
|
||||
|
||||
if (sid_session == SECSID_NULL) {
|
||||
sid_session = ctx->ctx_sid;
|
||||
if (!ckall)
|
||||
break;
|
||||
} else if (*sid != ctx->ctx_sid)
|
||||
goto out;
|
||||
} else if (sid_session != ctx->ctx_sid) {
|
||||
*sid = SECSID_NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
*sid = sid_session;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Security blob allocation for xfrm_policy and xfrm_state
|
||||
* CTX does not have a meaningful value on input
|
||||
*/
|
||||
static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
|
||||
struct xfrm_user_sec_ctx *uctx, u32 sid)
|
||||
{
|
||||
int rc = 0;
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
struct xfrm_sec_ctx *ctx = NULL;
|
||||
char *ctx_str = NULL;
|
||||
u32 str_len;
|
||||
|
||||
BUG_ON(uctx && sid);
|
||||
|
||||
if (!uctx)
|
||||
goto not_from_user;
|
||||
|
||||
if (uctx->ctx_alg != XFRM_SC_ALG_SELINUX)
|
||||
return -EINVAL;
|
||||
|
||||
str_len = uctx->ctx_len;
|
||||
if (str_len >= PAGE_SIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
*ctxp = ctx = kmalloc(sizeof(*ctx) +
|
||||
str_len + 1,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->ctx_doi = uctx->ctx_doi;
|
||||
ctx->ctx_len = str_len;
|
||||
ctx->ctx_alg = uctx->ctx_alg;
|
||||
|
||||
memcpy(ctx->ctx_str,
|
||||
uctx+1,
|
||||
str_len);
|
||||
ctx->ctx_str[str_len] = 0;
|
||||
rc = security_context_to_sid(ctx->ctx_str,
|
||||
str_len,
|
||||
&ctx->ctx_sid);
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Does the subject have permission to set security context?
|
||||
*/
|
||||
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
|
||||
SECCLASS_ASSOCIATION,
|
||||
ASSOCIATION__SETCONTEXT, NULL);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
return rc;
|
||||
|
||||
not_from_user:
|
||||
rc = security_sid_to_context(sid, &ctx_str, &str_len);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
*ctxp = ctx = kmalloc(sizeof(*ctx) +
|
||||
str_len,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (!ctx) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->ctx_doi = XFRM_SC_DOI_LSM;
|
||||
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
|
||||
ctx->ctx_sid = sid;
|
||||
ctx->ctx_len = str_len;
|
||||
memcpy(ctx->ctx_str,
|
||||
ctx_str,
|
||||
str_len);
|
||||
|
||||
goto out2;
|
||||
|
||||
out:
|
||||
*ctxp = NULL;
|
||||
kfree(ctx);
|
||||
out2:
|
||||
kfree(ctx_str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* LSM hook implementation that allocs and transfers uctx spec to
|
||||
* xfrm_policy.
|
||||
* LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.
|
||||
*/
|
||||
int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
|
||||
struct xfrm_user_sec_ctx *uctx)
|
||||
{
|
||||
int err;
|
||||
|
||||
BUG_ON(!uctx);
|
||||
|
||||
err = selinux_xfrm_sec_ctx_alloc(ctxp, uctx, 0);
|
||||
if (err == 0)
|
||||
atomic_inc(&selinux_xfrm_refcount);
|
||||
|
||||
return err;
|
||||
return selinux_xfrm_alloc_user(ctxp, uctx);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* LSM hook implementation that copies security data structure from old to
|
||||
* new for policy cloning.
|
||||
* LSM hook implementation that copies security data structure from old to new
|
||||
* for policy cloning.
|
||||
*/
|
||||
int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
|
||||
struct xfrm_sec_ctx **new_ctxp)
|
||||
{
|
||||
struct xfrm_sec_ctx *new_ctx;
|
||||
|
||||
if (old_ctx) {
|
||||
new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len,
|
||||
GFP_ATOMIC);
|
||||
if (!new_ctx)
|
||||
return -ENOMEM;
|
||||
if (!old_ctx)
|
||||
return 0;
|
||||
|
||||
new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len, GFP_ATOMIC);
|
||||
if (!new_ctx)
|
||||
return -ENOMEM;
|
||||
memcpy(new_ctx, old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len);
|
||||
atomic_inc(&selinux_xfrm_refcount);
|
||||
*new_ctxp = new_ctx;
|
||||
|
||||
memcpy(new_ctx, old_ctx, sizeof(*new_ctx));
|
||||
memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len);
|
||||
atomic_inc(&selinux_xfrm_refcount);
|
||||
*new_ctxp = new_ctx;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -327,8 +283,7 @@ int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
|
|||
*/
|
||||
void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
|
||||
{
|
||||
atomic_dec(&selinux_xfrm_refcount);
|
||||
kfree(ctx);
|
||||
selinux_xfrm_free(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -336,31 +291,55 @@ void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
|
|||
*/
|
||||
int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
return avc_has_perm(tsec->sid, ctx->ctx_sid,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
|
||||
NULL);
|
||||
return selinux_xfrm_delete(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* LSM hook implementation that allocs and transfers sec_ctx spec to
|
||||
* xfrm_state.
|
||||
* LSM hook implementation that allocates a xfrm_sec_state, populates it using
|
||||
* the supplied security context, and assigns it to the xfrm_state.
|
||||
*/
|
||||
int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx,
|
||||
u32 secid)
|
||||
int selinux_xfrm_state_alloc(struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *uctx)
|
||||
{
|
||||
int err;
|
||||
return selinux_xfrm_alloc_user(&x->security, uctx);
|
||||
}
|
||||
|
||||
BUG_ON(!x);
|
||||
/*
|
||||
* LSM hook implementation that allocates a xfrm_sec_state and populates based
|
||||
* on a secid.
|
||||
*/
|
||||
int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
|
||||
struct xfrm_sec_ctx *polsec, u32 secid)
|
||||
{
|
||||
int rc;
|
||||
struct xfrm_sec_ctx *ctx;
|
||||
char *ctx_str = NULL;
|
||||
int str_len;
|
||||
|
||||
err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid);
|
||||
if (err == 0)
|
||||
atomic_inc(&selinux_xfrm_refcount);
|
||||
return err;
|
||||
if (!polsec)
|
||||
return 0;
|
||||
|
||||
if (secid == 0)
|
||||
return -EINVAL;
|
||||
|
||||
rc = security_sid_to_context(secid, &ctx_str, &str_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->ctx_doi = XFRM_SC_DOI_LSM;
|
||||
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
|
||||
ctx->ctx_sid = secid;
|
||||
ctx->ctx_len = str_len;
|
||||
memcpy(ctx->ctx_str, ctx_str, str_len);
|
||||
kfree(ctx_str);
|
||||
|
||||
x->security = ctx;
|
||||
atomic_inc(&selinux_xfrm_refcount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -368,24 +347,15 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct
|
|||
*/
|
||||
void selinux_xfrm_state_free(struct xfrm_state *x)
|
||||
{
|
||||
atomic_dec(&selinux_xfrm_refcount);
|
||||
kfree(x->security);
|
||||
selinux_xfrm_free(x->security);
|
||||
}
|
||||
|
||||
/*
|
||||
* LSM hook implementation that authorizes deletion of labeled SAs.
|
||||
*/
|
||||
/*
|
||||
* LSM hook implementation that authorizes deletion of labeled SAs.
|
||||
*/
|
||||
int selinux_xfrm_state_delete(struct xfrm_state *x)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
struct xfrm_sec_ctx *ctx = x->security;
|
||||
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
return avc_has_perm(tsec->sid, ctx->ctx_sid,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
|
||||
NULL);
|
||||
return selinux_xfrm_delete(x->security);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -395,14 +365,12 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
|
|||
* we need to check for unlabelled access since this may not have
|
||||
* gone thru the IPSec process.
|
||||
*/
|
||||
int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad)
|
||||
int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
int i, rc = 0;
|
||||
struct sec_path *sp;
|
||||
u32 sel_sid = SECINITSID_UNLABELED;
|
||||
|
||||
sp = skb->sp;
|
||||
int i;
|
||||
struct sec_path *sp = skb->sp;
|
||||
u32 peer_sid = SECINITSID_UNLABELED;
|
||||
|
||||
if (sp) {
|
||||
for (i = 0; i < sp->len; i++) {
|
||||
|
@ -410,23 +378,17 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
|||
|
||||
if (x && selinux_authorizable_xfrm(x)) {
|
||||
struct xfrm_sec_ctx *ctx = x->security;
|
||||
sel_sid = ctx->ctx_sid;
|
||||
peer_sid = ctx->ctx_sid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This check even when there's no association involved is
|
||||
* intended, according to Trent Jaeger, to make sure a
|
||||
* process can't engage in non-ipsec communication unless
|
||||
* explicitly allowed by policy.
|
||||
*/
|
||||
|
||||
rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
|
||||
ASSOCIATION__RECVFROM, ad);
|
||||
|
||||
return rc;
|
||||
/* This check even when there's no association involved is intended,
|
||||
* according to Trent Jaeger, to make sure a process can't engage in
|
||||
* non-IPsec communication unless explicitly allowed by policy. */
|
||||
return avc_has_perm(sk_sid, peer_sid,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -436,49 +398,38 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
|||
* If we do have a authorizable security association, then it has already been
|
||||
* checked in the selinux_xfrm_state_pol_flow_match hook above.
|
||||
*/
|
||||
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 proto)
|
||||
int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 proto)
|
||||
{
|
||||
struct dst_entry *dst;
|
||||
int rc = 0;
|
||||
|
||||
dst = skb_dst(skb);
|
||||
|
||||
if (dst) {
|
||||
struct dst_entry *dst_test;
|
||||
|
||||
for (dst_test = dst; dst_test != NULL;
|
||||
dst_test = dst_test->child) {
|
||||
struct xfrm_state *x = dst_test->xfrm;
|
||||
|
||||
if (x && selinux_authorizable_xfrm(x))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
switch (proto) {
|
||||
case IPPROTO_AH:
|
||||
case IPPROTO_ESP:
|
||||
case IPPROTO_COMP:
|
||||
/*
|
||||
* We should have already seen this packet once before
|
||||
* it underwent xfrm(s). No need to subject it to the
|
||||
* unlabeled check.
|
||||
*/
|
||||
goto out;
|
||||
/* We should have already seen this packet once before it
|
||||
* underwent xfrm(s). No need to subject it to the unlabeled
|
||||
* check. */
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* This check even when there's no association involved is
|
||||
* intended, according to Trent Jaeger, to make sure a
|
||||
* process can't engage in non-ipsec communication unless
|
||||
* explicitly allowed by policy.
|
||||
*/
|
||||
dst = skb_dst(skb);
|
||||
if (dst) {
|
||||
struct dst_entry *iter;
|
||||
|
||||
rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
|
||||
ASSOCIATION__SENDTO, ad);
|
||||
out:
|
||||
return rc;
|
||||
for (iter = dst; iter != NULL; iter = iter->child) {
|
||||
struct xfrm_state *x = iter->xfrm;
|
||||
|
||||
if (x && selinux_authorizable_xfrm(x))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This check even when there's no association involved is intended,
|
||||
* according to Trent Jaeger, to make sure a process can't engage in
|
||||
* non-IPsec communication unless explicitly allowed by policy. */
|
||||
return avc_has_perm(sk_sid, SECINITSID_UNLABELED,
|
||||
SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue