From 19c5fc198c369bb00f3ed9716ef40648865d8d94 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 19 Nov 2007 17:53:44 -0800 Subject: security/selinux: Add missing "space" Add missing space. Signed-off-by: Joe Perches Signed-off-by: James Morris --- security/selinux/ss/avtab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/selinux') diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 9e70a160d7da..cd10e27fc9e6 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -280,7 +280,7 @@ int avtab_alloc(struct avtab *h, u32 nrules) h->nel = 0; h->nslot = nslot; h->mask = mask; - printk(KERN_DEBUG "SELinux:%d avtab hash slots allocated." + printk(KERN_DEBUG "SELinux:%d avtab hash slots allocated. " "Num of rules:%d\n", h->nslot, nrules); return 0; } -- cgit v1.2.1 From c9180a57a9ab2d5525faf8815a332364ee9e89b7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 30 Nov 2007 13:00:35 -0500 Subject: Security: add get, set, and cloning of superblock security information Adds security_get_sb_mnt_opts, security_set_sb_mnt_opts, and security_clont_sb_mnt_opts to the LSM and to SELinux. This will allow filesystems to directly own and control all of their mount options if they so choose. This interface deals only with option identifiers and strings so it should generic enough for any LSM which may come in the future. Filesystems which pass text mount data around in the kernel (almost all of them) need not currently make use of this interface when dealing with SELinux since it will still parse those strings as it always has. I assume future LSM's would do the same. NFS is the primary FS which does not use text mount data and thus must make use of this interface. An LSM would need to implement these functions only if they had mount time options, such as selinux has context= or fscontext=. If the LSM has no mount time options they could simply not implement and let the dummy ops take care of things. An LSM other than SELinux would need to define new option numbers in security.h and any FS which decides to own there own security options would need to be patched to use this new interface for every possible LSM. This is because it was stated to me very clearly that LSM's should not attempt to understand FS mount data and the burdon to understand security should be in the FS which owns the options. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/hooks.c | 748 +++++++++++++++++++++++++------------- security/selinux/include/objsec.h | 1 + 2 files changed, 495 insertions(+), 254 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9f3124b08867..233c8b97462f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -82,6 +82,8 @@ #define XATTR_SELINUX_SUFFIX "selinux" #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX +#define NUM_SEL_MNT_OPTS 4 + extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); extern int selinux_compat_net; @@ -321,8 +323,8 @@ enum { Opt_error = -1, Opt_context = 1, Opt_fscontext = 2, - Opt_defcontext = 4, - Opt_rootcontext = 8, + Opt_defcontext = 3, + Opt_rootcontext = 4, }; static match_table_t tokens = { @@ -366,150 +368,317 @@ static int may_context_mount_inode_relabel(u32 sid, return rc; } -static int try_context_mount(struct super_block *sb, void *data) +static int sb_finish_set_opts(struct super_block *sb) { - char *context = NULL, *defcontext = NULL; - char *fscontext = NULL, *rootcontext = NULL; - const char *name; - u32 sid; - int alloc = 0, rc = 0, seen = 0; - struct task_security_struct *tsec = current->security; struct superblock_security_struct *sbsec = sb->s_security; + struct dentry *root = sb->s_root; + struct inode *root_inode = root->d_inode; + int rc = 0; - if (!data) - goto out; + if (sbsec->behavior == SECURITY_FS_USE_XATTR) { + /* Make sure that the xattr handler exists and that no + error other than -ENODATA is returned by getxattr on + the root directory. -ENODATA is ok, as this may be + 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); + rc = -EOPNOTSUPP; + goto out; + } + rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0); + 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); + else + printk(KERN_WARNING "SELinux: (dev %s, type " + "%s) getxattr errno %d\n", sb->s_id, + sb->s_type->name, -rc); + goto out; + } + } - name = sb->s_type->name; + sbsec->initialized = 1; - if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) { + 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); + else + printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n", + sb->s_id, sb->s_type->name, + labeling_behaviors[sbsec->behavior-1]); - /* NFS we understand. */ - if (!strcmp(name, "nfs")) { - struct nfs_mount_data *d = data; + /* Initialize the root inode. */ + rc = inode_doinit_with_dentry(root_inode, root); - if (d->version < NFS_MOUNT_VERSION) - goto out; + /* Initialize any other inodes associated with the superblock, e.g. + inodes created prior to initial policy load or inodes created + during get_sb by a pseudo filesystem that directly + populates itself. */ + spin_lock(&sbsec->isec_lock); +next_inode: + if (!list_empty(&sbsec->isec_head)) { + struct inode_security_struct *isec = + list_entry(sbsec->isec_head.next, + struct inode_security_struct, list); + struct inode *inode = isec->inode; + spin_unlock(&sbsec->isec_lock); + inode = igrab(inode); + if (inode) { + if (!IS_PRIVATE(inode)) + inode_doinit(inode); + iput(inode); + } + spin_lock(&sbsec->isec_lock); + list_del_init(&isec->list); + goto next_inode; + } + spin_unlock(&sbsec->isec_lock); +out: + return rc; +} - if (d->context[0]) { - context = d->context; - seen |= Opt_context; - } - } else - goto out; +/* + * This function should allow an FS to ask what it's mount security + * options were so it can use those later for submounts, displaying + * mount options, or whatever. + */ +static int selinux_get_mnt_opts(const struct super_block *sb, + char ***mount_options, int **mnt_opts_flags, + int *num_opts) +{ + int rc = 0, i; + struct superblock_security_struct *sbsec = sb->s_security; + char *context = NULL; + u32 len; + char tmp; - } else { - /* Standard string-based options. */ - char *p, *options = data; + *num_opts = 0; + *mount_options = NULL; + *mnt_opts_flags = NULL; - while ((p = strsep(&options, "|")) != NULL) { - int token; - substring_t args[MAX_OPT_ARGS]; + if (!sbsec->initialized) + return -EINVAL; - if (!*p) - continue; + if (!ss_initialized) + return -EINVAL; - token = match_token(p, tokens, args); + /* + * if we ever use sbsec flags for anything other than tracking mount + * settings this is going to need a mask + */ + tmp = sbsec->flags; + /* count the number of mount options for this sb */ + for (i = 0; i < 8; i++) { + if (tmp & 0x01) + (*num_opts)++; + tmp >>= 1; + } - switch (token) { - case Opt_context: - if (seen & (Opt_context|Opt_defcontext)) { - rc = -EINVAL; - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); - goto out_free; - } - context = match_strdup(&args[0]); - if (!context) { - rc = -ENOMEM; - goto out_free; - } - if (!alloc) - alloc = 1; - seen |= Opt_context; - break; + *mount_options = kcalloc(*num_opts, sizeof(char *), GFP_ATOMIC); + if (!*mount_options) { + rc = -ENOMEM; + goto out_free; + } - case Opt_fscontext: - if (seen & Opt_fscontext) { - rc = -EINVAL; - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); - goto out_free; - } - fscontext = match_strdup(&args[0]); - if (!fscontext) { - rc = -ENOMEM; - goto out_free; - } - if (!alloc) - alloc = 1; - seen |= Opt_fscontext; - break; + *mnt_opts_flags = kcalloc(*num_opts, sizeof(int), GFP_ATOMIC); + if (!*mnt_opts_flags) { + rc = -ENOMEM; + goto out_free; + } - case Opt_rootcontext: - if (seen & Opt_rootcontext) { - rc = -EINVAL; - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); - goto out_free; - } - rootcontext = match_strdup(&args[0]); - if (!rootcontext) { - rc = -ENOMEM; - goto out_free; - } - if (!alloc) - alloc = 1; - seen |= Opt_rootcontext; - break; + i = 0; + if (sbsec->flags & FSCONTEXT_MNT) { + rc = security_sid_to_context(sbsec->sid, &context, &len); + if (rc) + goto out_free; + (*mount_options)[i] = context; + (*mnt_opts_flags)[i++] = FSCONTEXT_MNT; + } + if (sbsec->flags & CONTEXT_MNT) { + rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len); + if (rc) + goto out_free; + (*mount_options)[i] = context; + (*mnt_opts_flags)[i++] = CONTEXT_MNT; + } + if (sbsec->flags & DEFCONTEXT_MNT) { + rc = security_sid_to_context(sbsec->def_sid, &context, &len); + if (rc) + goto out_free; + (*mount_options)[i] = context; + (*mnt_opts_flags)[i++] = DEFCONTEXT_MNT; + } + if (sbsec->flags & ROOTCONTEXT_MNT) { + struct inode *root = sbsec->sb->s_root->d_inode; + struct inode_security_struct *isec = root->i_security; - case Opt_defcontext: - if (sbsec->behavior != SECURITY_FS_USE_XATTR) { - rc = -EINVAL; - printk(KERN_WARNING "SELinux: " - "defcontext option is invalid " - "for this filesystem type\n"); - goto out_free; - } - if (seen & (Opt_context|Opt_defcontext)) { - rc = -EINVAL; - printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); - goto out_free; - } - defcontext = match_strdup(&args[0]); - if (!defcontext) { - rc = -ENOMEM; - goto out_free; - } - if (!alloc) - alloc = 1; - seen |= Opt_defcontext; - break; + rc = security_sid_to_context(isec->sid, &context, &len); + if (rc) + goto out_free; + (*mount_options)[i] = context; + (*mnt_opts_flags)[i++] = ROOTCONTEXT_MNT; + } - default: - rc = -EINVAL; - printk(KERN_WARNING "SELinux: unknown mount " - "option\n"); - goto out_free; + BUG_ON(i != *num_opts); - } - } - } + return 0; + +out_free: + /* don't leak context string if security_sid_to_context had an error */ + if (*mount_options && i) + for (; i > 0; i--) + kfree((*mount_options)[i-1]); + kfree(*mount_options); + *mount_options = NULL; + kfree(*mnt_opts_flags); + *mnt_opts_flags = NULL; + *num_opts = 0; + return rc; +} + +static int bad_option(struct superblock_security_struct *sbsec, char flag, + u32 old_sid, u32 new_sid) +{ + /* check if the old mount command had the same options */ + if (sbsec->initialized) + if (!(sbsec->flags & flag) || + (old_sid != new_sid)) + return 1; + + /* check if we were passed the same options twice, + * aka someone passed context=a,context=b + */ + if (!sbsec->initialized) + if (sbsec->flags & flag) + return 1; + return 0; +} +/* + * Allow filesystems with binary mount data to explicitly set mount point + * labeling information. + */ +int selinux_set_mnt_opts(struct super_block *sb, char **mount_options, + int *flags, int num_opts) +{ + int rc = 0, i; + struct task_security_struct *tsec = current->security; + 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; + u32 defcontext_sid = 0; - if (!seen) + mutex_lock(&sbsec->lock); + + if (!ss_initialized) { + if (!num_opts) { + /* Defer initialization until selinux_complete_init, + after the initial policy is loaded and the security + server is ready to handle calls. */ + spin_lock(&sb_security_lock); + if (list_empty(&sbsec->list)) + list_add(&sbsec->list, &superblock_security_head); + spin_unlock(&sb_security_lock); + goto out; + } + rc = -EINVAL; + printk(KERN_WARNING "Unable to set superblock options before " + "the security server is initialized\n"); goto out; + } - /* sets the context of the superblock for the fs being mounted. */ - if (fscontext) { - rc = security_context_to_sid(fscontext, strlen(fscontext), &sid); + /* + * parse the mount options, check if they are valid sids. + * also check if someone is trying to mount the same sb more + * than once with different security options. + */ + for (i = 0; i < num_opts; i++) { + u32 sid; + 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", - fscontext, sb->s_id, name, rc); - goto out_free; + mount_options[i], sb->s_id, name, rc); + goto out; + } + switch (flags[i]) { + case FSCONTEXT_MNT: + fscontext_sid = sid; + + if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, + fscontext_sid)) + goto out_double_mount; + + sbsec->flags |= FSCONTEXT_MNT; + break; + case CONTEXT_MNT: + context_sid = sid; + + if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, + context_sid)) + goto out_double_mount; + + sbsec->flags |= CONTEXT_MNT; + break; + case ROOTCONTEXT_MNT: + rootcontext_sid = sid; + + if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, + rootcontext_sid)) + goto out_double_mount; + + sbsec->flags |= ROOTCONTEXT_MNT; + + break; + case DEFCONTEXT_MNT: + defcontext_sid = sid; + + if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, + defcontext_sid)) + goto out_double_mount; + + sbsec->flags |= DEFCONTEXT_MNT; + + break; + default: + rc = -EINVAL; + goto out; } + } + + if (sbsec->initialized) { + /* previously mounted with options, but not on this attempt? */ + if (sbsec->flags && !num_opts) + goto out_double_mount; + rc = 0; + goto out; + } - rc = may_context_mount_sb_relabel(sid, sbsec, tsec); + if (strcmp(sb->s_type->name, "proc") == 0) + sbsec->proc = 1; + + /* Determine the labeling behavior to use for this filesystem type. */ + rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid); + if (rc) { + printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", + __FUNCTION__, sb->s_type->name, rc); + goto out; + } + + /* sets the context of the superblock for the fs being mounted. */ + if (fscontext_sid) { + + rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec); if (rc) - goto out_free; + goto out; - sbsec->sid = sid; + sbsec->sid = fscontext_sid; } /* @@ -517,182 +686,250 @@ static int try_context_mount(struct super_block *sb, void *data) * sets the label used on all file below the mountpoint, and will set * the superblock context if not already set. */ - if (context) { - rc = security_context_to_sid(context, strlen(context), &sid); - if (rc) { - printk(KERN_WARNING "SELinux: security_context_to_sid" - "(%s) failed for (dev %s, type %s) errno=%d\n", - context, sb->s_id, name, rc); - goto out_free; - } - - if (!fscontext) { - rc = may_context_mount_sb_relabel(sid, sbsec, tsec); + if (context_sid) { + if (!fscontext_sid) { + rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec); if (rc) - goto out_free; - sbsec->sid = sid; + goto out; + sbsec->sid = context_sid; } else { - rc = may_context_mount_inode_relabel(sid, sbsec, tsec); + rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec); if (rc) - goto out_free; + goto out; } - sbsec->mntpoint_sid = sid; + if (!rootcontext_sid) + rootcontext_sid = context_sid; + sbsec->mntpoint_sid = context_sid; sbsec->behavior = SECURITY_FS_USE_MNTPOINT; } - if (rootcontext) { - struct inode *inode = sb->s_root->d_inode; - struct inode_security_struct *isec = inode->i_security; - rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid); - if (rc) { - printk(KERN_WARNING "SELinux: security_context_to_sid" - "(%s) failed for (dev %s, type %s) errno=%d\n", - rootcontext, sb->s_id, name, rc); - goto out_free; - } - - rc = may_context_mount_inode_relabel(sid, sbsec, tsec); + if (rootcontext_sid) { + rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec); if (rc) - goto out_free; + goto out; - isec->sid = sid; - isec->initialized = 1; + root_isec->sid = rootcontext_sid; + root_isec->initialized = 1; } - if (defcontext) { - rc = security_context_to_sid(defcontext, strlen(defcontext), &sid); - if (rc) { - printk(KERN_WARNING "SELinux: security_context_to_sid" - "(%s) failed for (dev %s, type %s) errno=%d\n", - defcontext, sb->s_id, name, rc); - goto out_free; + if (defcontext_sid) { + if (sbsec->behavior != SECURITY_FS_USE_XATTR) { + rc = -EINVAL; + printk(KERN_WARNING "SELinux: defcontext option is " + "invalid for this filesystem type\n"); + goto out; } - if (sid == sbsec->def_sid) - goto out_free; - - rc = may_context_mount_inode_relabel(sid, sbsec, tsec); - if (rc) - goto out_free; + if (defcontext_sid != sbsec->def_sid) { + rc = may_context_mount_inode_relabel(defcontext_sid, + sbsec, tsec); + if (rc) + goto out; + } - sbsec->def_sid = sid; + sbsec->def_sid = defcontext_sid; } -out_free: - if (alloc) { - kfree(context); - kfree(defcontext); - kfree(fscontext); - kfree(rootcontext); - } + rc = sb_finish_set_opts(sb); out: + mutex_unlock(&sbsec->lock); return rc; +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); + goto out; } -static int superblock_doinit(struct super_block *sb, void *data) +static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, + struct super_block *newsb) { - struct superblock_security_struct *sbsec = sb->s_security; - struct dentry *root = sb->s_root; - struct inode *inode = root->d_inode; - int rc = 0; + const struct superblock_security_struct *oldsbsec = oldsb->s_security; + struct superblock_security_struct *newsbsec = newsb->s_security; - mutex_lock(&sbsec->lock); - if (sbsec->initialized) - goto out; + int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT); + int set_context = (oldsbsec->flags & CONTEXT_MNT); + int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT); - if (!ss_initialized) { - /* Defer initialization until selinux_complete_init, - after the initial policy is loaded and the security - server is ready to handle calls. */ - spin_lock(&sb_security_lock); - if (list_empty(&sbsec->list)) - list_add(&sbsec->list, &superblock_security_head); - spin_unlock(&sb_security_lock); - goto out; + /* we can't error, we can't save the info, this shouldn't get called + * this early in the boot process. */ + BUG_ON(!ss_initialized); + + /* this might go away sometime down the line if there is a new user + * of clone, but for now, nfs better not get here... */ + BUG_ON(newsbsec->initialized); + + /* how can we clone if the old one wasn't set up?? */ + BUG_ON(!oldsbsec->initialized); + + mutex_lock(&newsbsec->lock); + + newsbsec->flags = oldsbsec->flags; + + newsbsec->sid = oldsbsec->sid; + newsbsec->def_sid = oldsbsec->def_sid; + newsbsec->behavior = oldsbsec->behavior; + + if (set_context) { + u32 sid = oldsbsec->mntpoint_sid; + + if (!set_fscontext) + newsbsec->sid = sid; + if (!set_rootcontext) { + struct inode *newinode = newsb->s_root->d_inode; + struct inode_security_struct *newisec = newinode->i_security; + newisec->sid = sid; + } + newsbsec->mntpoint_sid = sid; } + if (set_rootcontext) { + const struct inode *oldinode = oldsb->s_root->d_inode; + const struct inode_security_struct *oldisec = oldinode->i_security; + struct inode *newinode = newsb->s_root->d_inode; + struct inode_security_struct *newisec = newinode->i_security; - /* Determine the labeling behavior to use for this filesystem type. */ - rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid); - if (rc) { - printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", - __FUNCTION__, sb->s_type->name, rc); - goto out; + newisec->sid = oldisec->sid; } - rc = try_context_mount(sb, data); - if (rc) + sb_finish_set_opts(newsb); + mutex_unlock(&newsbsec->lock); +} + +/* + * string mount options parsing and call set the sbsec + */ +static int superblock_doinit(struct super_block *sb, void *data) +{ + char *context = NULL, *defcontext = NULL; + char *fscontext = NULL, *rootcontext = NULL; + int rc = 0; + char *p, *options = data; + /* selinux only know about a fixed number of mount options */ + char *mnt_opts[NUM_SEL_MNT_OPTS]; + int mnt_opts_flags[NUM_SEL_MNT_OPTS], num_mnt_opts = 0; + + if (!data) goto out; - if (sbsec->behavior == SECURITY_FS_USE_XATTR) { - /* Make sure that the xattr handler exists and that no - error other than -ENODATA is returned by getxattr on - the root directory. -ENODATA is ok, as this may be - the first boot of the SELinux kernel before we have - assigned xattr values to the filesystem. */ - if (!inode->i_op->getxattr) { - printk(KERN_WARNING "SELinux: (dev %s, type %s) has no " - "xattr support\n", sb->s_id, sb->s_type->name); - rc = -EOPNOTSUPP; - goto out; - } - rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0); - 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); - else - printk(KERN_WARNING "SELinux: (dev %s, type " - "%s) getxattr errno %d\n", sb->s_id, - sb->s_type->name, -rc); + /* with the nfs patch this will become a goto out; */ + if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) { + const char *name = sb->s_type->name; + /* NFS we understand. */ + if (!strcmp(name, "nfs")) { + struct nfs_mount_data *d = data; + + if (d->version != NFS_MOUNT_VERSION) + goto out; + + if (d->context[0]) { + context = kstrdup(d->context, GFP_KERNEL); + if (!context) { + rc = -ENOMEM; + goto out; + } + } + goto build_flags; + } else goto out; - } } - if (strcmp(sb->s_type->name, "proc") == 0) - sbsec->proc = 1; + /* Standard string-based options. */ + while ((p = strsep(&options, "|")) != NULL) { + int token; + substring_t args[MAX_OPT_ARGS]; - sbsec->initialized = 1; + if (!*p) + continue; - 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); - } - else { - printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n", - sb->s_id, sb->s_type->name, - labeling_behaviors[sbsec->behavior-1]); - } + token = match_token(p, tokens, args); - /* Initialize the root inode. */ - rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root); + switch (token) { + case Opt_context: + if (context || defcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + context = match_strdup(&args[0]); + if (!context) { + rc = -ENOMEM; + goto out_err; + } + break; + + case Opt_fscontext: + if (fscontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + fscontext = match_strdup(&args[0]); + if (!fscontext) { + rc = -ENOMEM; + goto out_err; + } + break; + + case Opt_rootcontext: + if (rootcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + rootcontext = match_strdup(&args[0]); + if (!rootcontext) { + rc = -ENOMEM; + goto out_err; + } + break; + + case Opt_defcontext: + if (context || defcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_err; + } + defcontext = match_strdup(&args[0]); + if (!defcontext) { + rc = -ENOMEM; + goto out_err; + } + break; + + default: + rc = -EINVAL; + printk(KERN_WARNING "SELinux: unknown mount option\n"); + goto out_err; - /* Initialize any other inodes associated with the superblock, e.g. - inodes created prior to initial policy load or inodes created - during get_sb by a pseudo filesystem that directly - populates itself. */ - spin_lock(&sbsec->isec_lock); -next_inode: - if (!list_empty(&sbsec->isec_head)) { - struct inode_security_struct *isec = - list_entry(sbsec->isec_head.next, - struct inode_security_struct, list); - struct inode *inode = isec->inode; - spin_unlock(&sbsec->isec_lock); - inode = igrab(inode); - if (inode) { - if (!IS_PRIVATE (inode)) - inode_doinit(inode); - iput(inode); } - spin_lock(&sbsec->isec_lock); - list_del_init(&isec->list); - goto next_inode; } - spin_unlock(&sbsec->isec_lock); + +build_flags: + if (fscontext) { + mnt_opts[num_mnt_opts] = fscontext; + mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT; + } + if (context) { + mnt_opts[num_mnt_opts] = context; + mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT; + } + if (rootcontext) { + mnt_opts[num_mnt_opts] = rootcontext; + mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT; + } + if (defcontext) { + mnt_opts[num_mnt_opts] = defcontext; + mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT; + } + out: - mutex_unlock(&sbsec->lock); + rc = selinux_set_mnt_opts(sb, mnt_opts, mnt_opts_flags, num_mnt_opts); +out_err: + kfree(context); + kfree(defcontext); + kfree(fscontext); + kfree(rootcontext); return rc; } @@ -4800,6 +5037,9 @@ static struct security_operations selinux_ops = { .sb_statfs = selinux_sb_statfs, .sb_mount = selinux_mount, .sb_umount = selinux_umount, + .sb_get_mnt_opts = selinux_get_mnt_opts, + .sb_set_mnt_opts = selinux_set_mnt_opts, + .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, .inode_alloc_security = selinux_inode_alloc_security, .inode_free_security = selinux_inode_free_security, diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 642a9fd319ad..4138a80f8e27 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -65,6 +65,7 @@ struct superblock_security_struct { u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */ unsigned int behavior; /* labeling behavior */ unsigned char initialized; /* initialization flag */ + unsigned char flags; /* which mount options were specified */ unsigned char proc; /* proc fs */ struct mutex lock; struct list_head isec_head; -- cgit v1.2.1 From 63cb34492351078479b2d4bae6a881806a396286 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 15 Jan 2008 23:47:35 +0000 Subject: security: add a secctx_to_secid() hook Add a secctx_to_secid() LSM hook to go along with the existing secid_to_secctx() LSM hook. This patch also includes the SELinux implementation for this hook. Signed-off-by: Paul Moore Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/hooks.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 233c8b97462f..0396354fff95 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4947,6 +4947,11 @@ static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) return security_sid_to_context(secid, secdata, seclen); } +static int selinux_secctx_to_secid(char *secdata, u32 seclen, u32 *secid) +{ + return security_context_to_sid(secdata, seclen, secid); +} + static void selinux_release_secctx(char *secdata, u32 seclen) { kfree(secdata); @@ -5138,6 +5143,7 @@ static struct security_operations selinux_ops = { .setprocattr = selinux_setprocattr, .secid_to_secctx = selinux_secid_to_secctx, + .secctx_to_secid = selinux_secctx_to_secid, .release_secctx = selinux_release_secctx, .unix_stream_connect = selinux_socket_unix_stream_connect, -- cgit v1.2.1 From 1996a10948e50e546dc2b64276723c0b64d3173b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 23 Jan 2008 00:02:58 +0100 Subject: security/selinux: constify function pointer tables and fields Constify function pointer tables and fields. Signed-off-by: Jan Engelhardt Signed-off-by: James Morris --- security/selinux/selinuxfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/selinux') diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 2fa483f26113..397fd4955fe1 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1222,7 +1222,7 @@ static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) { } -static struct seq_operations sel_avc_cache_stats_seq_ops = { +static const struct seq_operations sel_avc_cache_stats_seq_ops = { .start = sel_avc_stats_seq_start, .next = sel_avc_stats_seq_next, .show = sel_avc_stats_seq_show, -- cgit v1.2.1 From 2e08c0c1c3977a5ddc88887dd3af1b26c433e9d0 Mon Sep 17 00:00:00 2001 From: Eamon Walsh Date: Thu, 24 Jan 2008 15:30:52 -0500 Subject: selinux: make mls_compute_sid always polyinstantiate This patch removes the requirement that the new and related object types differ in order to polyinstantiate by MLS level. This allows MLS polyinstantiation to occur in the absence of explicit type_member rules or when the type has not changed. Potential users of this support include pam_namespace.so (directory polyinstantiation) and the SELinux X support (property polyinstantiation). Signed-off-by: Eamon Walsh Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/mls.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index fb5d70a6628d..3bbcb5369af9 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -537,15 +537,8 @@ int mls_compute_sid(struct context *scontext, /* Use the process effective MLS attributes. */ return mls_context_cpy_low(newcontext, scontext); case AVTAB_MEMBER: - /* Only polyinstantiate the MLS attributes if - the type is being polyinstantiated */ - if (newcontext->type != tcontext->type) { - /* Use the process effective MLS attributes. */ - return mls_context_cpy_low(newcontext, scontext); - } else { - /* Use the related object MLS attributes. */ - return mls_context_cpy(newcontext, tcontext); - } + /* Use the process effective MLS attributes. */ + return mls_context_cpy_low(newcontext, scontext); default: return -EINVAL; } -- cgit v1.2.1 From b1aa5301b9f88a4891061650c591fb8fe1c1d1da Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Fri, 25 Jan 2008 13:03:42 -0500 Subject: selinux: fix labeling of /proc/net inodes The proc net rewrite had a side effect on selinux, leading it to mislabel the /proc/net inodes, thereby leading to incorrect denials. Fix security_genfs_sid to ignore extra leading / characters in the path supplied by selinux_proc_get_sid since we now get "//net/..." rather than "/net/...". Signed-off-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/services.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'security/selinux') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index f83b19daed16..4bf715d4cf29 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1744,6 +1744,9 @@ int security_genfs_sid(const char *fstype, struct ocontext *c; int rc = 0, cmp = 0; + while (path[0] == '/' && path[1] == '/') + path++; + POLICY_RDLOCK; for (genfs = policydb.genfs; genfs; genfs = genfs->next) { -- cgit v1.2.1 From 6e23ae2a48750bda407a4a58f52a4865d7308bf5 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 19 Nov 2007 18:53:30 -0800 Subject: [NETFILTER]: Introduce NF_INET_ hook values The IPv4 and IPv6 hook values are identical, yet some code tries to figure out the "correct" value by looking at the address family. Introduce NF_INET_* values for both IPv4 and IPv6. The old values are kept in a #ifndef __KERNEL__ section for userspace compatibility. Signed-off-by: Patrick McHardy Acked-by: Herbert Xu Signed-off-by: David S. Miller --- security/selinux/hooks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0396354fff95..64d414efb404 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5281,7 +5281,7 @@ static struct nf_hook_ops selinux_ipv4_op = { .hook = selinux_ipv4_postroute_last, .owner = THIS_MODULE, .pf = PF_INET, - .hooknum = NF_IP_POST_ROUTING, + .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_SELINUX_LAST, }; @@ -5291,7 +5291,7 @@ static struct nf_hook_ops selinux_ipv6_op = { .hook = selinux_ipv6_postroute_last, .owner = THIS_MODULE, .pf = PF_INET6, - .hooknum = NF_IP6_POST_ROUTING, + .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_SELINUX_LAST, }; -- cgit v1.2.1 From 16efd45435fa695b501b7f73c3259bd7c77cc12c Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:37:59 -0500 Subject: NetLabel: Add secid token support to the NetLabel secattr struct This patch adds support to the NetLabel LSM secattr struct for a secid token and a type field, paving the way for full LSM/SELinux context support and "static" or "fallback" labels. In addition, this patch adds a fair amount of documentation to the core NetLabel structures used as part of the NetLabel kernel API. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/ss/mls.c | 10 +++++----- security/selinux/ss/services.c | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 3bbcb5369af9..feaf0a5b828f 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -562,7 +562,7 @@ void mls_export_netlbl_lvl(struct context *context, if (!selinux_mls_enabled) return; - secattr->mls_lvl = context->range.level[0].sens - 1; + secattr->attr.mls.lvl = context->range.level[0].sens - 1; secattr->flags |= NETLBL_SECATTR_MLS_LVL; } @@ -582,7 +582,7 @@ void mls_import_netlbl_lvl(struct context *context, if (!selinux_mls_enabled) return; - context->range.level[0].sens = secattr->mls_lvl + 1; + context->range.level[0].sens = secattr->attr.mls.lvl + 1; context->range.level[1].sens = context->range.level[0].sens; } @@ -605,8 +605,8 @@ int mls_export_netlbl_cat(struct context *context, return 0; rc = ebitmap_netlbl_export(&context->range.level[0].cat, - &secattr->mls_cat); - if (rc == 0 && secattr->mls_cat != NULL) + &secattr->attr.mls.cat); + if (rc == 0 && secattr->attr.mls.cat != NULL) secattr->flags |= NETLBL_SECATTR_MLS_CAT; return rc; @@ -633,7 +633,7 @@ int mls_import_netlbl_cat(struct context *context, return 0; rc = ebitmap_netlbl_import(&context->range.level[0].cat, - secattr->mls_cat); + secattr->attr.mls.cat); if (rc != 0) goto import_netlbl_cat_failure; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 4bf715d4cf29..0f97ef578370 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2550,6 +2550,9 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, default: goto netlbl_secattr_to_sid_return; } + } else if (secattr->flags & NETLBL_SECATTR_SECID) { + *sid = secattr->attr.secid; + rc = 0; } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { ctx = sidtab_search(&sidtab, base_sid); if (ctx == NULL) @@ -2561,7 +2564,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, mls_import_netlbl_lvl(&ctx_new, secattr); if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat, - secattr->mls_cat) != 0) + secattr->attr.mls.cat) != 0) goto netlbl_secattr_to_sid_return; ctx_new.range.level[1].cat.highbit = ctx_new.range.level[0].cat.highbit; -- cgit v1.2.1 From 75e22910cf0c26802b09dac2e34c13e648d3ed02 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:38:04 -0500 Subject: NetLabel: Add IP address family information to the netlbl_skbuff_getattr() function In order to do any sort of IP header inspection of incoming packets we need to know which address family, AF_INET/AF_INET6/etc., it belongs to and since the sk_buff structure does not store this information we need to pass along the address family separate from the packet itself. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 33 ++++++++++++++++++++++----------- security/selinux/include/netlabel.h | 8 +++++++- security/selinux/netlabel.c | 12 +++++++++--- 3 files changed, 38 insertions(+), 15 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 64d414efb404..5df12072c8d5 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3429,6 +3429,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, /** * selinux_skb_extlbl_sid - Determine the external label of a packet * @skb: the packet + * @family: protocol family * @sid: the packet's SID * * Description: @@ -3441,13 +3442,16 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, * selinux_netlbl_skbuff_getsid(). * */ -static void selinux_skb_extlbl_sid(struct sk_buff *skb, u32 *sid) +static void selinux_skb_extlbl_sid(struct sk_buff *skb, + u16 family, + u32 *sid) { u32 xfrm_sid; u32 nlbl_sid; selinux_skb_xfrm_sid(skb, &xfrm_sid); if (selinux_netlbl_skbuff_getsid(skb, + family, (xfrm_sid == SECSID_NULL ? SECINITSID_NETMSG : xfrm_sid), &nlbl_sid) != 0) @@ -3940,7 +3944,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) goto out; - err = selinux_netlbl_sock_rcv_skb(sksec, skb, &ad); + err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad); if (err) goto out; @@ -3996,18 +4000,25 @@ out: static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid) { u32 peer_secid = SECSID_NULL; - int err = 0; + u16 family; - if (sock && sock->sk->sk_family == PF_UNIX) + if (sock) + family = sock->sk->sk_family; + else if (skb && skb->sk) + family = skb->sk->sk_family; + else + goto out; + + if (sock && family == PF_UNIX) selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); else if (skb) - selinux_skb_extlbl_sid(skb, &peer_secid); + selinux_skb_extlbl_sid(skb, family, &peer_secid); - if (peer_secid == SECSID_NULL) - err = -EINVAL; +out: *secid = peer_secid; - - return err; + if (peer_secid == SECSID_NULL) + return -EINVAL; + return 0; } static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) @@ -4062,7 +4073,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, u32 newsid; u32 peersid; - selinux_skb_extlbl_sid(skb, &peersid); + selinux_skb_extlbl_sid(skb, sk->sk_family, &peersid); if (peersid == SECSID_NULL) { req->secid = sksec->sid; req->peer_secid = SECSID_NULL; @@ -4100,7 +4111,7 @@ static void selinux_inet_conn_established(struct sock *sk, { struct sk_security_struct *sksec = sk->sk_security; - selinux_skb_extlbl_sid(skb, &sksec->peer_sid); + selinux_skb_extlbl_sid(skb, sk->sk_family, &sksec->peer_sid); } static void selinux_req_classify_flow(const struct request_sock *req, diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 218e3f77c350..272769a1cb96 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -46,13 +46,17 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, struct sk_security_struct *newssec); -int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid); +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u16 family, + u32 base_sid, + u32 *sid); void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); int selinux_netlbl_socket_post_create(struct socket *sock); int selinux_netlbl_inode_permission(struct inode *inode, int mask); int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct sk_buff *skb, + u16 family, struct avc_audit_data *ad); int selinux_netlbl_socket_setsockopt(struct socket *sock, int level, @@ -83,6 +87,7 @@ static inline void selinux_netlbl_sk_security_clone( } static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u16 family, u32 base_sid, u32 *sid) { @@ -106,6 +111,7 @@ static inline int selinux_netlbl_inode_permission(struct inode *inode, } static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct sk_buff *skb, + u16 family, struct avc_audit_data *ad) { return 0; diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 66e013d6f6f6..f4bcbf12a4c9 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -144,6 +144,7 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, /** * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel * @skb: the packet + * @family: protocol family * @base_sid: the SELinux SID to use as a context for MLS only attributes * @sid: the SID * @@ -153,7 +154,10 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, * assign to the packet. Returns zero on success, negative values on failure. * */ -int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) +int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, + u16 family, + u32 base_sid, + u32 *sid) { int rc; struct netlbl_lsm_secattr secattr; @@ -164,7 +168,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) } netlbl_secattr_init(&secattr); - rc = netlbl_skbuff_getattr(skb, &secattr); + rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) { rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid); if (rc == 0 && @@ -292,6 +296,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask) * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel * @sksec: the sock's sk_security_struct * @skb: the packet + * @family: protocol family * @ad: the audit data * * Description: @@ -302,6 +307,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask) */ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, struct sk_buff *skb, + u16 family, struct avc_audit_data *ad) { int rc; @@ -313,7 +319,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, return 0; netlbl_secattr_init(&secattr); - rc = netlbl_skbuff_getattr(skb, &secattr); + rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) { rc = security_netlbl_secattr_to_sid(&secattr, SECINITSID_NETMSG, -- cgit v1.2.1 From e8bfdb9d0dfc1231a6a71e849dfbd4447acdfff6 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:38:08 -0500 Subject: SELinux: Convert the netif code to use ifindex values The current SELinux netif code requires the caller have a valid net_device struct pointer to lookup network interface information. However, we don't always have a valid net_device pointer so convert the netif code to use the ifindex values we always have as part of the sk_buff. This patch also removes the default message SID from the network interface record, it is not being used and therefore is "dead code". Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 4 +- security/selinux/include/netif.h | 4 +- security/selinux/include/objsec.h | 5 +- security/selinux/include/security.h | 3 +- security/selinux/netif.c | 254 ++++++++++++++++++++---------------- security/selinux/ss/services.c | 10 +- 6 files changed, 155 insertions(+), 125 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5df12072c8d5..be544332214c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3853,7 +3853,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, if (!skb->dev) goto out; - err = sel_netif_sids(skb->dev, &if_sid, NULL); + err = sel_netif_sid(skb->iif, &if_sid); if (err) goto out; @@ -4178,7 +4178,7 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device * isec = inode->i_security; - err = sel_netif_sids(dev, &if_sid, NULL); + err = sel_netif_sid(dev->ifindex, &if_sid); if (err) goto out; diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h index 8bd6f9992d2b..ce23edd128b3 100644 --- a/security/selinux/include/netif.h +++ b/security/selinux/include/netif.h @@ -7,6 +7,8 @@ * Author: James Morris * * Copyright (C) 2003 Red Hat, Inc., James Morris + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. + * Paul Moore, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -15,7 +17,7 @@ #ifndef _SELINUX_NETIF_H_ #define _SELINUX_NETIF_H_ -int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid); +int sel_netif_sid(int ifindex, u32 *sid); #endif /* _SELINUX_NETIF_H_ */ diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 4138a80f8e27..2d0a92e97d5a 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -96,9 +96,8 @@ struct bprm_security_struct { }; struct netif_security_struct { - struct net_device *dev; /* back pointer */ - u32 if_sid; /* SID for this interface */ - u32 msg_sid; /* default SID for messages received on this interface */ + int ifindex; /* device index */ + u32 sid; /* SID for this interface */ }; struct sk_security_struct { diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 39337afffec2..a33437bba932 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -77,8 +77,7 @@ int security_get_user_sids(u32 callsid, char *username, int security_port_sid(u16 domain, u16 type, u8 protocol, u16 port, u32 *out_sid); -int security_netif_sid(char *name, u32 *if_sid, - u32 *msg_sid); +int security_netif_sid(char *name, u32 *if_sid); int security_node_sid(u16 domain, void *addr, u32 addrlen, u32 *out_sid); diff --git a/security/selinux/netif.c b/security/selinux/netif.c index e87ab948104c..ee49a7382875 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -7,6 +7,8 @@ * Author: James Morris * * Copyright (C) 2003 Red Hat, Inc., James Morris + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. + * Paul Moore * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -29,14 +31,6 @@ #define SEL_NETIF_HASH_SIZE 64 #define SEL_NETIF_HASH_MAX 1024 -#undef DEBUG - -#ifdef DEBUG -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - struct sel_netif { struct list_head list; @@ -49,174 +43,217 @@ static LIST_HEAD(sel_netif_list); static DEFINE_SPINLOCK(sel_netif_lock); static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; -static inline u32 sel_netif_hasfn(struct net_device *dev) +/** + * sel_netif_hashfn - Hashing function for the interface table + * @ifindex: the network interface + * + * Description: + * This is the hashing function for the network interface table, it returns the + * bucket number for the given interface. + * + */ +static inline u32 sel_netif_hashfn(int ifindex) { - return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1)); + return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); } -/* - * All of the devices should normally fit in the hash, so we optimize - * for that case. +/** + * sel_netif_find - Search for an interface record + * @ifindex: the network interface + * + * Description: + * Search the network interface table and return the record matching @ifindex. + * If an entry can not be found in the table return NULL. + * */ -static inline struct sel_netif *sel_netif_find(struct net_device *dev) +static inline struct sel_netif *sel_netif_find(int ifindex) { - struct list_head *pos; - int idx = sel_netif_hasfn(dev); + int idx = sel_netif_hashfn(ifindex); + struct sel_netif *netif; - __list_for_each_rcu(pos, &sel_netif_hash[idx]) { - struct sel_netif *netif = list_entry(pos, - struct sel_netif, list); - if (likely(netif->nsec.dev == dev)) + list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) + /* all of the devices should normally fit in the hash, so we + * optimize for that case */ + if (likely(netif->nsec.ifindex == ifindex)) return netif; - } + return NULL; } +/** + * sel_netif_insert - Insert a new interface into the table + * @netif: the new interface record + * + * Description: + * Add a new interface record to the network interface hash table. Returns + * zero on success, negative values on failure. + * + */ static int sel_netif_insert(struct sel_netif *netif) { - int idx, ret = 0; + int idx; - if (sel_netif_total >= SEL_NETIF_HASH_MAX) { - ret = -ENOSPC; - goto out; - } + if (sel_netif_total >= SEL_NETIF_HASH_MAX) + return -ENOSPC; - idx = sel_netif_hasfn(netif->nsec.dev); + idx = sel_netif_hashfn(netif->nsec.ifindex); list_add_rcu(&netif->list, &sel_netif_hash[idx]); sel_netif_total++; -out: - return ret; + + return 0; } +/** + * sel_netif_free - Frees an interface entry + * @p: the entry's RCU field + * + * Description: + * This function is designed to be used as a callback to the call_rcu() + * function so that memory allocated to a hash table interface entry can be + * released safely. + * + */ static void sel_netif_free(struct rcu_head *p) { struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head); - - DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); kfree(netif); } +/** + * sel_netif_destroy - Remove an interface record from the table + * @netif: the existing interface record + * + * Description: + * Remove an existing interface record from the network interface table. + * + */ static void sel_netif_destroy(struct sel_netif *netif) { - DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); - list_del_rcu(&netif->list); sel_netif_total--; call_rcu(&netif->rcu_head, sel_netif_free); } -static struct sel_netif *sel_netif_lookup(struct net_device *dev) +/** + * sel_netif_sid_slow - Lookup the SID of a network interface using the policy + * @ifindex: the network interface + * @sid: interface SID + * + * Description: + * This function determines the SID of a network interface by quering the + * security policy. The result is added to the network interface table to + * speedup future queries. Returns zero on success, negative values on + * failure. + * + */ +static int sel_netif_sid_slow(int ifindex, u32 *sid) { int ret; - struct sel_netif *netif, *new; - struct netif_security_struct *nsec; + struct sel_netif *netif; + struct sel_netif *new = NULL; + struct net_device *dev; - netif = sel_netif_find(dev); - if (likely(netif != NULL)) - goto out; - - new = kzalloc(sizeof(*new), GFP_ATOMIC); - if (!new) { - netif = ERR_PTR(-ENOMEM); - goto out; - } - - nsec = &new->nsec; + /* NOTE: we always use init's network namespace since we don't + * currently support containers */ - ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid); - if (ret < 0) { - kfree(new); - netif = ERR_PTR(ret); - goto out; - } + dev = dev_get_by_index(&init_net, ifindex); + if (dev == NULL) + return -ENOENT; - nsec->dev = dev; - spin_lock_bh(&sel_netif_lock); - - netif = sel_netif_find(dev); - if (netif) { - spin_unlock_bh(&sel_netif_lock); - kfree(new); + netif = sel_netif_find(ifindex); + if (netif != NULL) { + *sid = netif->nsec.sid; + ret = 0; goto out; } - - ret = sel_netif_insert(new); - spin_unlock_bh(&sel_netif_lock); - - if (ret) { - kfree(new); - netif = ERR_PTR(ret); + new = kzalloc(sizeof(*new), GFP_ATOMIC); + if (new == NULL) { + ret = -ENOMEM; goto out; } + ret = security_netif_sid(dev->name, &new->nsec.sid); + if (ret != 0) + goto out; + new->nsec.ifindex = ifindex; + ret = sel_netif_insert(new); + if (ret != 0) + goto out; + *sid = new->nsec.sid; - netif = new; - - DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name, - nsec->if_sid, nsec->msg_sid); out: - return netif; -} - -static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out) -{ - if (if_sid_out) - *if_sid_out = if_sid_in; - if (msg_sid_out) - *msg_sid_out = msg_sid_in; -} - -static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid) -{ - int ret = 0; - u32 tmp_if_sid, tmp_msg_sid; - - ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid); - if (!ret) - sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid); + spin_unlock_bh(&sel_netif_lock); + dev_put(dev); + if (ret != 0) + kfree(new); return ret; } -int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid) +/** + * sel_netif_sid - Lookup the SID of a network interface + * @ifindex: the network interface + * @sid: interface SID + * + * Description: + * This function determines the SID of a network interface using the fastest + * method possible. First the interface 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_netif_sid(int ifindex, u32 *sid) { - int ret = 0; struct sel_netif *netif; rcu_read_lock(); - netif = sel_netif_lookup(dev); - if (IS_ERR(netif)) { + netif = sel_netif_find(ifindex); + if (likely(netif != NULL)) { + *sid = netif->nsec.sid; rcu_read_unlock(); - ret = sel_netif_sids_slow(dev, if_sid, msg_sid); - goto out; + return 0; } - sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid); rcu_read_unlock(); -out: - return ret; + + return sel_netif_sid_slow(ifindex, sid); } -static void sel_netif_kill(struct net_device *dev) +/** + * sel_netif_kill - Remove an entry from the network interface table + * @ifindex: the network interface + * + * Description: + * This function removes the entry matching @ifindex from the network interface + * table if it exists. + * + */ +static void sel_netif_kill(int ifindex) { struct sel_netif *netif; spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(dev); + netif = sel_netif_find(ifindex); if (netif) sel_netif_destroy(netif); spin_unlock_bh(&sel_netif_lock); } +/** + * sel_netif_flush - Flush the entire network interface table + * + * Description: + * Remove all entries from the network interface table. + * + */ static void sel_netif_flush(void) { int idx; + struct sel_netif *netif; spin_lock_bh(&sel_netif_lock); - for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) { - struct sel_netif *netif; - + for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) list_for_each_entry(netif, &sel_netif_hash[idx], list) sel_netif_destroy(netif); - } spin_unlock_bh(&sel_netif_lock); } @@ -239,7 +276,7 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this, return NOTIFY_DONE; if (event == NETDEV_DOWN) - sel_netif_kill(dev); + sel_netif_kill(dev->ifindex); return NOTIFY_DONE; } @@ -250,10 +287,10 @@ static struct notifier_block sel_netif_netdev_notifier = { static __init int sel_netif_init(void) { - int i, err = 0; + int i, err; if (!selinux_enabled) - goto out; + return 0; for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) INIT_LIST_HEAD(&sel_netif_hash[i]); @@ -265,7 +302,6 @@ static __init int sel_netif_init(void) if (err) panic("avc_add_callback() failed, error %d\n", err); -out: return err; } diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 0f97ef578370..8dfaa3e7c26d 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1478,11 +1478,8 @@ out: * security_netif_sid - Obtain the SID for a network interface. * @name: interface name * @if_sid: interface SID - * @msg_sid: default SID for received packets */ -int security_netif_sid(char *name, - u32 *if_sid, - u32 *msg_sid) +int security_netif_sid(char *name, u32 *if_sid) { int rc = 0; struct ocontext *c; @@ -1510,11 +1507,8 @@ int security_netif_sid(char *name, goto out; } *if_sid = c->sid[0]; - *msg_sid = c->sid[1]; - } else { + } else *if_sid = SECINITSID_NETIF; - *msg_sid = SECINITSID_NETMSG; - } out: POLICY_RDUNLOCK; -- cgit v1.2.1 From da5645a28a15aed2e541a814ecf9f7ffcd4c4673 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:38:10 -0500 Subject: SELinux: Only store the network interface's ifindex Instead of storing the packet's network interface name store the ifindex. This allows us to defer the need to lookup the net_device structure until the audit record is generated meaning that in the majority of cases we never need to bother with this at all. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/avc.c | 15 ++++++++++++--- security/selinux/hooks.c | 4 ++-- security/selinux/include/avc.h | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 81b3dff3cbf0..e8529e2f51e5 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -661,9 +661,18 @@ void avc_audit(u32 ssid, u32 tsid, "daddr", "dest"); break; } - if (a->u.net.netif) - audit_log_format(ab, " netif=%s", - a->u.net.netif); + if (a->u.net.netif > 0) { + struct net_device *dev; + + /* NOTE: we always use init's namespace */ + dev = dev_get_by_index(&init_net, + a->u.net.netif); + if (dev) { + audit_log_format(ab, " netif=%s", + dev->name); + dev_put(dev); + } + } break; } } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index be544332214c..1a1fa3f20ef0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3928,7 +3928,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) family = PF_INET; AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]"; + ad.u.net.netif = skb->iif; ad.u.net.family = family; err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL); @@ -4259,7 +4259,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, sksec = sk->sk_security; AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; + ad.u.net.netif = dev->ifindex; ad.u.net.family = family; err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto); diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 553607a19e92..80c28fa6621c 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -51,7 +51,7 @@ struct avc_audit_data { struct inode *inode; } fs; struct { - char *netif; + int netif; struct sock *sk; u16 family; __be16 dport; -- cgit v1.2.1 From 224dfbd81e1ff672eb46e7695469c395bd531083 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:38:13 -0500 Subject: SELinux: Add a network node caching mechanism similar to the sel_netif_*() functions This patch adds a SELinux IP address/node SID caching mechanism similar to the sel_netif_*() functions. The node SID queries in the SELinux hooks files are also modified to take advantage of this new functionality. In addition, remove the address length information from the sk_buff parsing routines as it is redundant since we already have the address family. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/Makefile | 9 +- security/selinux/hooks.c | 33 ++-- security/selinux/include/netnode.h | 32 ++++ security/selinux/include/objsec.h | 9 + security/selinux/netnode.c | 350 +++++++++++++++++++++++++++++++++++++ 5 files changed, 416 insertions(+), 17 deletions(-) create mode 100644 security/selinux/include/netnode.h create mode 100644 security/selinux/netnode.c (limited to 'security/selinux') diff --git a/security/selinux/Makefile b/security/selinux/Makefile index dc3502e30b19..00afd85f1edb 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -4,7 +4,14 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/ -selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o +selinux-y := avc.o \ + hooks.o \ + selinuxfs.o \ + netlink.o \ + nlmsgtab.o \ + netif.o \ + netnode.o \ + exports.o selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1a1fa3f20ef0..4bca0af4f2af 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -76,6 +76,7 @@ #include "avc.h" #include "objsec.h" #include "netif.h" +#include "netnode.h" #include "xfrm.h" #include "netlabel.h" @@ -3395,7 +3396,7 @@ out: #endif /* IPV6 */ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, - char **addrp, int *len, int src, u8 *proto) + char **addrp, int src, u8 *proto) { int ret = 0; @@ -3404,7 +3405,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, ret = selinux_parse_skb_ipv4(skb, ad, proto); if (ret || !addrp) break; - *len = 4; *addrp = (char *)(src ? &ad->u.net.v4info.saddr : &ad->u.net.v4info.daddr); break; @@ -3414,7 +3414,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, ret = selinux_parse_skb_ipv6(skb, ad, proto); if (ret || !addrp) break; - *len = 16; *addrp = (char *)(src ? &ad->u.net.v6info.saddr : &ad->u.net.v6info.daddr); break; @@ -3614,7 +3613,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in break; } - err = security_node_sid(family, addrp, addrlen, &sid); + err = sel_netnode_sid(addrp, family, &sid); if (err) goto out; @@ -3826,7 +3825,8 @@ static int selinux_socket_unix_may_send(struct socket *sock, } static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, - struct avc_audit_data *ad, u16 family, char *addrp, int len) + struct avc_audit_data *ad, + u16 family, char *addrp) { int err = 0; u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; @@ -3886,7 +3886,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, if (err) goto out; - err = security_node_sid(family, addrp, len, &node_sid); + err = sel_netnode_sid(addrp, family, &node_sid); if (err) goto out; @@ -3915,7 +3915,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { u16 family; char *addrp; - int len, err = 0; + int err = 0; struct avc_audit_data ad; struct sk_security_struct *sksec = sk->sk_security; @@ -3931,13 +3931,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ad.u.net.netif = skb->iif; ad.u.net.family = family; - err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL); + err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) goto out; if (selinux_compat_net) - err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, - addrp, len); + err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, addrp); else err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); @@ -4158,9 +4157,11 @@ out: #ifdef CONFIG_NETFILTER -static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev, +static int selinux_ip_postroute_last_compat(struct sock *sk, + struct net_device *dev, struct avc_audit_data *ad, - u16 family, char *addrp, int len) + u16 family, + char *addrp) { int err = 0; u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; @@ -4211,7 +4212,7 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device * if (err) goto out; - err = security_node_sid(family, addrp, len, &node_sid); + err = sel_netnode_sid(addrp, family, &node_sid); if (err) goto out; @@ -4245,7 +4246,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, u16 family) { char *addrp; - int len, err = 0; + int err = 0; struct sock *sk; struct avc_audit_data ad; struct net_device *dev = (struct net_device *)out; @@ -4262,13 +4263,13 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, ad.u.net.netif = dev->ifindex; ad.u.net.family = family; - err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto); + err = selinux_parse_skb(skb, &ad, &addrp, 0, &proto); if (err) goto out; if (selinux_compat_net) err = selinux_ip_postroute_last_compat(sk, dev, &ad, - family, addrp, len); + family, addrp); else err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, PACKET__SEND, &ad); diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h new file mode 100644 index 000000000000..1b94450d11d2 --- /dev/null +++ b/security/selinux/include/netnode.h @@ -0,0 +1,32 @@ +/* + * Network node table + * + * SELinux must keep a mapping of network nodes to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead since most of these queries happen on + * a per-packet basis. + * + * Author: Paul Moore + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 + * + * 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_NETNODE_H +#define _SELINUX_NETNODE_H + +int sel_netnode_sid(void *addr, u16 family, u32 *sid); + +#endif diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 2d0a92e97d5a..95fb5ec17354 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -100,6 +100,15 @@ struct netif_security_struct { u32 sid; /* SID for this interface */ }; +struct netnode_security_struct { + union { + __be32 ipv4; /* IPv4 node address */ + struct in6_addr ipv6; /* IPv6 node address */ + } addr; + u32 sid; /* SID for this node */ + u16 family; /* address family */ +}; + struct sk_security_struct { struct sock *sk; /* back pointer to sk object */ u32 sid; /* SID of this object */ diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c new file mode 100644 index 000000000000..49c527799240 --- /dev/null +++ b/security/selinux/netnode.c @@ -0,0 +1,350 @@ +/* + * Network node table + * + * SELinux must keep a mapping of network nodes to labels/SIDs. This + * mapping is maintained as part of the normal policy but a fast cache is + * needed to reduce the lookup overhead since most of these queries happen on + * a per-packet basis. + * + * Author: Paul Moore + * + * This code is heavily based on the "netif" concept originally developed by + * James Morris + * (see security/selinux/netif.c for more information) + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "objsec.h" + +#define SEL_NETNODE_HASH_SIZE 256 +#define SEL_NETNODE_HASH_BKT_LIMIT 16 + +struct sel_netnode { + struct netnode_security_struct nsec; + + struct list_head list; + struct rcu_head rcu; +}; + +/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason + * for this is that I suspect most users will not make heavy use of both + * address families at the same time so one table will usually end up wasted, + * if this becomes a problem we can always add a hash table for each address + * family later */ + +static LIST_HEAD(sel_netnode_list); +static DEFINE_SPINLOCK(sel_netnode_lock); +static struct list_head sel_netnode_hash[SEL_NETNODE_HASH_SIZE]; + +/** + * sel_netnode_free - Frees a node entry + * @p: the entry's RCU field + * + * Description: + * This function is designed to be used as a callback to the call_rcu() + * function so that memory allocated to a hash table node entry can be + * released safely. + * + */ +static void sel_netnode_free(struct rcu_head *p) +{ + struct sel_netnode *node = container_of(p, struct sel_netnode, rcu); + kfree(node); +} + +/** + * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table + * @addr: IPv4 address + * + * Description: + * This is the IPv4 hashing function for the node interface table, it returns + * the bucket number for the given IP address. + * + */ +static u32 sel_netnode_hashfn_ipv4(__be32 addr) +{ + /* at some point we should determine if the mismatch in byte order + * affects the hash function dramatically */ + return (addr & (SEL_NETNODE_HASH_SIZE - 1)); +} + +/** + * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table + * @addr: IPv6 address + * + * Description: + * This is the IPv6 hashing function for the node interface table, it returns + * the bucket number for the given IP address. + * + */ +static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr) +{ + /* just hash the least significant 32 bits to keep things fast (they + * are the most likely to be different anyway), we can revisit this + * later if needed */ + return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1)); +} + +/** + * sel_netnode_find - Search for a node record + * @addr: IP address + * @family: address family + * + * Description: + * Search the network node table and return the record matching @addr. If an + * entry can not be found in the table return NULL. + * + */ +static struct sel_netnode *sel_netnode_find(const void *addr, u16 family) +{ + u32 idx; + struct sel_netnode *node; + + switch (family) { + case PF_INET: + idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr); + break; + case PF_INET6: + idx = sel_netnode_hashfn_ipv6(addr); + break; + default: + BUG(); + } + + list_for_each_entry_rcu(node, &sel_netnode_hash[idx], list) + if (node->nsec.family == family) + switch (family) { + case PF_INET: + if (node->nsec.addr.ipv4 == *(__be32 *)addr) + return node; + break; + case PF_INET6: + if (ipv6_addr_equal(&node->nsec.addr.ipv6, + addr)) + return node; + break; + } + + return NULL; +} + +/** + * sel_netnode_insert - Insert a new node into the table + * @node: the new node record + * + * Description: + * Add a new node record to the network address hash table. Returns zero on + * success, negative values on failure. + * + */ +static int sel_netnode_insert(struct sel_netnode *node) +{ + u32 idx; + u32 count = 0; + struct sel_netnode *iter; + + switch (node->nsec.family) { + case PF_INET: + idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4); + break; + case PF_INET6: + idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6); + break; + default: + BUG(); + } + list_add_rcu(&node->list, &sel_netnode_hash[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 */ + list_for_each_entry(iter, &sel_netnode_hash[idx], list) + if (++count > SEL_NETNODE_HASH_BKT_LIMIT) { + list_del_rcu(&iter->list); + call_rcu(&iter->rcu, sel_netnode_free); + break; + } + + return 0; +} + +/** + * sel_netnode_destroy - Remove a node record from the table + * @node: the existing node record + * + * Description: + * Remove an existing node record from the network address table. + * + */ +static void sel_netnode_destroy(struct sel_netnode *node) +{ + list_del_rcu(&node->list); + call_rcu(&node->rcu, sel_netnode_free); +} + +/** + * sel_netnode_sid_slow - Lookup the SID of a network address using the policy + * @addr: the IP address + * @family: the address family + * @sid: node SID + * + * Description: + * This function determines the SID of a network address by quering the + * security policy. The result is added to the network address table to + * speedup future queries. Returns zero on success, negative values on + * failure. + * + */ +static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) +{ + int ret; + struct sel_netnode *node; + struct sel_netnode *new = NULL; + + spin_lock_bh(&sel_netnode_lock); + node = sel_netnode_find(addr, family); + if (node != NULL) { + *sid = node->nsec.sid; + ret = 0; + goto out; + } + new = kzalloc(sizeof(*new), GFP_ATOMIC); + if (new == NULL) { + ret = -ENOMEM; + goto out; + } + switch (family) { + case PF_INET: + ret = security_node_sid(PF_INET, + addr, sizeof(struct in_addr), + &new->nsec.sid); + new->nsec.addr.ipv4 = *(__be32 *)addr; + break; + case PF_INET6: + ret = security_node_sid(PF_INET6, + addr, sizeof(struct in6_addr), + &new->nsec.sid); + ipv6_addr_copy(&new->nsec.addr.ipv6, addr); + break; + default: + BUG(); + } + if (ret != 0) + goto out; + new->nsec.family = family; + ret = sel_netnode_insert(new); + if (ret != 0) + goto out; + *sid = new->nsec.sid; + +out: + spin_unlock_bh(&sel_netnode_lock); + if (ret != 0) + kfree(new); + return ret; +} + +/** + * sel_netnode_sid - Lookup the SID of a network address + * @addr: the IP address + * @family: the address family + * @sid: node SID + * + * Description: + * This function determines the SID of a network address using the fastest + * method possible. First the address 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_netnode_sid(void *addr, u16 family, u32 *sid) +{ + struct sel_netnode *node; + + rcu_read_lock(); + node = sel_netnode_find(addr, family); + if (node != NULL) { + *sid = node->nsec.sid; + rcu_read_unlock(); + return 0; + } + rcu_read_unlock(); + + return sel_netnode_sid_slow(addr, family, sid); +} + +/** + * sel_netnode_flush - Flush the entire network address table + * + * Description: + * Remove all entries from the network address table. + * + */ +static void sel_netnode_flush(void) +{ + u32 idx; + struct sel_netnode *node; + + spin_lock_bh(&sel_netnode_lock); + for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) + list_for_each_entry(node, &sel_netnode_hash[idx], list) + sel_netnode_destroy(node); + spin_unlock_bh(&sel_netnode_lock); +} + +static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid, + u16 class, u32 perms, u32 *retained) +{ + if (event == AVC_CALLBACK_RESET) { + sel_netnode_flush(); + synchronize_net(); + } + return 0; +} + +static __init int sel_netnode_init(void) +{ + int iter; + int ret; + + if (!selinux_enabled) + return 0; + + for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) + INIT_LIST_HEAD(&sel_netnode_hash[iter]); + + ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET, + SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + if (ret != 0) + panic("avc_add_callback() failed, error %d\n", ret); + + return ret; +} + +__initcall(sel_netnode_init); -- cgit v1.2.1 From 3bb56b25dbe0a4b44bd2ebceab6736d068e85068 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:38:19 -0500 Subject: SELinux: Add a capabilities bitmap to SELinux policy version 22 Add a new policy capabilities bitmap to SELinux policy version 22. This bitmap will enable the security server to query the policy to determine which features it supports. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/Kconfig | 2 +- security/selinux/include/security.h | 15 ++++++- security/selinux/selinuxfs.c | 89 +++++++++++++++++++++++++++++++++++-- security/selinux/ss/policydb.c | 18 +++++++- security/selinux/ss/policydb.h | 2 + security/selinux/ss/services.c | 67 +++++++++++++++++++++++++++- 6 files changed, 185 insertions(+), 8 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index b32a459c0683..2b517d618672 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -145,7 +145,7 @@ config SECURITY_SELINUX_POLICYDB_VERSION_MAX config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE int "NSA SELinux maximum supported policy format version value" depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX - range 15 21 + range 15 22 default 19 help This option sets the value for the maximum policy format version diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index a33437bba932..a22de9771806 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -25,13 +25,14 @@ #define POLICYDB_VERSION_MLS 19 #define POLICYDB_VERSION_AVTAB 20 #define POLICYDB_VERSION_RANGETRANS 21 +#define POLICYDB_VERSION_POLCAP 22 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_RANGETRANS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_POLCAP #endif struct netlbl_lsm_secattr; @@ -39,8 +40,19 @@ struct netlbl_lsm_secattr; extern int selinux_enabled; extern int selinux_mls_enabled; +/* Policy capabilities */ +enum { + POLICYDB_CAPABILITY_NETPEER, + __POLICYDB_CAPABILITY_MAX +}; +#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) + +extern int selinux_policycap_netpeer; + int security_load_policy(void * data, size_t len); +int security_policycap_supported(unsigned int req_cap); + #define SEL_VEC_MAX 32 struct av_decision { u32 allowed; @@ -91,6 +103,7 @@ int security_get_classes(char ***classes, int *nclasses); int security_get_permissions(char *class, char ***perms, int *nperms); int security_get_reject_unknown(void); int security_get_allow_unknown(void); +int security_get_policycaps(int *len, int **values); #define SECURITY_FS_USE_XATTR 1 /* use xattr */ #define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 397fd4955fe1..a85740530afc 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -2,6 +2,11 @@ * * Added conditional policy language extensions * + * Updated: Hewlett-Packard + * + * Added support for the policy capability bitmap + * + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * Copyright (C) 2004 Red Hat, Inc., James Morris * This program is free software; you can redistribute it and/or modify @@ -35,6 +40,11 @@ #include "objsec.h" #include "conditional.h" +/* Policy capability filenames */ +static char *policycap_names[] = { + "network_peer_controls" +}; + unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; #ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT @@ -72,6 +82,9 @@ static int *bool_pending_values = NULL; static struct dentry *class_dir = NULL; static unsigned long last_class_ino; +/* global data for policy capabilities */ +static struct dentry *policycap_dir = NULL; + extern void selnl_notify_setenforce(int val); /* Check whether a task is allowed to use a security operation. */ @@ -111,10 +124,11 @@ enum sel_inos { static unsigned long sel_last_ino = SEL_INO_NEXT - 1; -#define SEL_INITCON_INO_OFFSET 0x01000000 -#define SEL_BOOL_INO_OFFSET 0x02000000 -#define SEL_CLASS_INO_OFFSET 0x04000000 -#define SEL_INO_MASK 0x00ffffff +#define SEL_INITCON_INO_OFFSET 0x01000000 +#define SEL_BOOL_INO_OFFSET 0x02000000 +#define SEL_CLASS_INO_OFFSET 0x04000000 +#define SEL_POLICYCAP_INO_OFFSET 0x08000000 +#define SEL_INO_MASK 0x00ffffff #define TMPBUFLEN 12 static ssize_t sel_read_enforce(struct file *filp, char __user *buf, @@ -263,6 +277,7 @@ static const struct file_operations sel_policyvers_ops = { /* declaration for sel_write_load */ static int sel_make_bools(void); static int sel_make_classes(void); +static int sel_make_policycap(void); /* declaration for sel_make_class_dirs */ static int sel_make_dir(struct inode *dir, struct dentry *dentry, @@ -323,6 +338,12 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, } ret = sel_make_classes(); + if (ret) { + length = ret; + goto out1; + } + + ret = sel_make_policycap(); if (ret) length = ret; else @@ -1399,6 +1420,24 @@ static const struct file_operations sel_perm_ops = { .read = sel_read_perm, }; +static ssize_t sel_read_policycap(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int value; + char tmpbuf[TMPBUFLEN]; + ssize_t length; + unsigned long i_ino = file->f_path.dentry->d_inode->i_ino; + + value = security_policycap_supported(i_ino & SEL_INO_MASK); + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value); + + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static const struct file_operations sel_policycap_ops = { + .read = sel_read_policycap, +}; + static int sel_make_perm_files(char *objclass, int classvalue, struct dentry *dir) { @@ -1545,6 +1584,36 @@ out: return rc; } +static int sel_make_policycap(void) +{ + unsigned int iter; + struct dentry *dentry = NULL; + struct inode *inode = NULL; + + sel_remove_entries(policycap_dir); + + for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { + if (iter < ARRAY_SIZE(policycap_names)) + dentry = d_alloc_name(policycap_dir, + policycap_names[iter]); + else + dentry = d_alloc_name(policycap_dir, "unknown"); + + if (dentry == NULL) + return -ENOMEM; + + inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO); + if (inode == NULL) + return -ENOMEM; + + inode->i_fop = &sel_policycap_ops; + inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET; + d_add(dentry, inode); + } + + return 0; +} + static int sel_make_dir(struct inode *dir, struct dentry *dentry, unsigned long *ino) { @@ -1673,6 +1742,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) class_dir = dentry; + dentry = d_alloc_name(sb->s_root, "policy_capabilities"); + if (!dentry) { + ret = -ENOMEM; + goto err; + } + + ret = sel_make_dir(root_inode, dentry, &sel_last_ino); + if (ret) + goto err; + + policycap_dir = dentry; + out: return ret; err: diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index b582aae3c62c..bd7d6a00342d 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -13,6 +13,11 @@ * * Added conditional policy language extensions * + * Updated: Hewlett-Packard + * + * Added support for the policy capability bitmap + * + * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify @@ -102,6 +107,11 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_POLCAP, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + } }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -183,6 +193,8 @@ static int policydb_init(struct policydb *p) if (rc) goto out_free_symtab; + ebitmap_init(&p->policycaps); + out: return rc; @@ -673,8 +685,8 @@ void policydb_destroy(struct policydb *p) ebitmap_destroy(&p->type_attr_map[i]); } kfree(p->type_attr_map); - kfree(p->undefined_perms); + ebitmap_destroy(&p->policycaps); return; } @@ -1554,6 +1566,10 @@ int policydb_read(struct policydb *p, void *fp) p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN); p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN); + if (p->policyvers >= POLICYDB_VERSION_POLCAP && + ebitmap_read(&p->policycaps, fp) != 0) + goto bad; + info = policydb_lookup_compat(p->policyvers); if (!info) { printk(KERN_ERR "security: unable to find policy compat info " diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index ed6fc687c66f..c4ce996e202c 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -241,6 +241,8 @@ struct policydb { /* type -> attribute reverse mapping */ struct ebitmap *type_attr_map; + struct ebitmap policycaps; + unsigned int policyvers; unsigned int reject_unknown : 1; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8dfaa3e7c26d..8ee04a424df7 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -16,12 +16,13 @@ * Updated: Hewlett-Packard * * Added support for NetLabel + * Added support for the policy capability bitmap * * Updated: Chad Sellers * * Added validation of kernel classes and permissions * - * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris @@ -59,6 +60,8 @@ extern void selnl_notify_policyload(u32 seqno); unsigned int policydb_loaded_version; +int selinux_policycap_netpeer; + /* * This is declared in avc.c */ @@ -1299,6 +1302,12 @@ bad: goto out; } +static void security_load_policycaps(void) +{ + selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_NETPEER); +} + extern void selinux_complete_init(void); static int security_preserve_bools(struct policydb *p); @@ -1346,6 +1355,7 @@ int security_load_policy(void *data, size_t len) avtab_cache_destroy(); return -EINVAL; } + security_load_policycaps(); policydb_loaded_version = policydb.policyvers; ss_initialized = 1; seqno = ++latest_granting; @@ -1404,6 +1414,7 @@ int security_load_policy(void *data, size_t len) POLICY_WRLOCK; memcpy(&policydb, &newpolicydb, sizeof policydb); sidtab_set(&sidtab, &newsidtab); + security_load_policycaps(); seqno = ++latest_granting; policydb_loaded_version = policydb.policyvers; POLICY_WRUNLOCK; @@ -2148,6 +2159,60 @@ int security_get_allow_unknown(void) return policydb.allow_unknown; } +/** + * security_get_policycaps - Query the loaded policy for its capabilities + * @len: the number of capability bits + * @values: the capability bit array + * + * Description: + * Get an array of the policy capabilities in @values where each entry in + * @values is either true (1) or false (0) depending the policy's support of + * that feature. The policy capabilities are defined by the + * POLICYDB_CAPABILITY_* enums. The size of the array is stored in @len and it + * is up to the caller to free the array in @values. Returns zero on success, + * negative values on failure. + * + */ +int security_get_policycaps(int *len, int **values) +{ + int rc = -ENOMEM; + unsigned int iter; + + POLICY_RDLOCK; + + *values = kcalloc(POLICYDB_CAPABILITY_MAX, sizeof(int), GFP_ATOMIC); + if (*values == NULL) + goto out; + for (iter = 0; iter < POLICYDB_CAPABILITY_MAX; iter++) + (*values)[iter] = ebitmap_get_bit(&policydb.policycaps, iter); + *len = POLICYDB_CAPABILITY_MAX; + +out: + POLICY_RDUNLOCK; + return rc; +} + +/** + * security_policycap_supported - Check for a specific policy capability + * @req_cap: capability + * + * Description: + * This function queries the currently loaded policy to see if it supports the + * capability specified by @req_cap. Returns true (1) if the capability is + * supported, false (0) if it isn't supported. + * + */ +int security_policycap_supported(unsigned int req_cap) +{ + int rc; + + POLICY_RDLOCK; + rc = ebitmap_get_bit(&policydb.policycaps, req_cap); + POLICY_RDUNLOCK; + + return rc; +} + struct selinux_audit_rule { u32 au_seqno; struct context au_ctxt; -- cgit v1.2.1 From f67f4f315f31e7907779adb3296fb6682e755342 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:38:21 -0500 Subject: SELinux: Add a new peer class and permissions to the Flask definitions Add additional Flask definitions to support the new "peer" object class and additional permissions to the netif, node, and packet object classes. Also, bring the kernel Flask definitions up to date with the Fedora SELinux policies by adding the "flow_in" and "flow_out" permissions to the "packet" class. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/include/av_perm_to_string.h | 9 +++++++++ security/selinux/include/av_permissions.h | 9 +++++++++ security/selinux/include/class_to_string.h | 7 +++++++ security/selinux/include/flask.h | 1 + 4 files changed, 26 insertions(+) (limited to 'security/selinux') diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index 049bf69429b6..399f868c5c8f 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -37,6 +37,8 @@ S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest") S_(SECCLASS_NODE, NODE__DCCP_RECV, "dccp_recv") S_(SECCLASS_NODE, NODE__DCCP_SEND, "dccp_send") + S_(SECCLASS_NODE, NODE__RECVFROM, "recvfrom") + S_(SECCLASS_NODE, NODE__SENDTO, "sendto") S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv") S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send") S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv") @@ -45,6 +47,8 @@ S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send") S_(SECCLASS_NETIF, NETIF__DCCP_RECV, "dccp_recv") S_(SECCLASS_NETIF, NETIF__DCCP_SEND, "dccp_send") + S_(SECCLASS_NETIF, NETIF__INGRESS, "ingress") + S_(SECCLASS_NETIF, NETIF__EGRESS, "egress") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn") S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom") @@ -149,6 +153,10 @@ S_(SECCLASS_PACKET, PACKET__SEND, "send") S_(SECCLASS_PACKET, PACKET__RECV, "recv") S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto") + S_(SECCLASS_PACKET, PACKET__FLOW_IN, "flow_in") + S_(SECCLASS_PACKET, PACKET__FLOW_OUT, "flow_out") + S_(SECCLASS_PACKET, PACKET__FORWARD_IN, "forward_in") + S_(SECCLASS_PACKET, PACKET__FORWARD_OUT, "forward_out") S_(SECCLASS_KEY, KEY__VIEW, "view") S_(SECCLASS_KEY, KEY__READ, "read") S_(SECCLASS_KEY, KEY__WRITE, "write") @@ -159,3 +167,4 @@ S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind") S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect") S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero") + S_(SECCLASS_PEER, PEER__RECV, "recv") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index eda89a2ec635..84c9abc80978 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -292,6 +292,8 @@ #define NODE__ENFORCE_DEST 0x00000040UL #define NODE__DCCP_RECV 0x00000080UL #define NODE__DCCP_SEND 0x00000100UL +#define NODE__RECVFROM 0x00000200UL +#define NODE__SENDTO 0x00000400UL #define NETIF__TCP_RECV 0x00000001UL #define NETIF__TCP_SEND 0x00000002UL #define NETIF__UDP_RECV 0x00000004UL @@ -300,6 +302,8 @@ #define NETIF__RAWIP_SEND 0x00000020UL #define NETIF__DCCP_RECV 0x00000040UL #define NETIF__DCCP_SEND 0x00000080UL +#define NETIF__INGRESS 0x00000100UL +#define NETIF__EGRESS 0x00000200UL #define NETLINK_SOCKET__IOCTL 0x00000001UL #define NETLINK_SOCKET__READ 0x00000002UL #define NETLINK_SOCKET__WRITE 0x00000004UL @@ -792,6 +796,10 @@ #define PACKET__SEND 0x00000001UL #define PACKET__RECV 0x00000002UL #define PACKET__RELABELTO 0x00000004UL +#define PACKET__FLOW_IN 0x00000008UL +#define PACKET__FLOW_OUT 0x00000010UL +#define PACKET__FORWARD_IN 0x00000020UL +#define PACKET__FORWARD_OUT 0x00000040UL #define KEY__VIEW 0x00000001UL #define KEY__READ 0x00000002UL #define KEY__WRITE 0x00000004UL @@ -824,3 +832,4 @@ #define DCCP_SOCKET__NODE_BIND 0x00400000UL #define DCCP_SOCKET__NAME_CONNECT 0x00800000UL #define MEMPROTECT__MMAP_ZERO 0x00000001UL +#define PEER__RECV 0x00000001UL diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h index e77de0e62ea0..b1b0d1d8f950 100644 --- a/security/selinux/include/class_to_string.h +++ b/security/selinux/include/class_to_string.h @@ -64,3 +64,10 @@ S_(NULL) S_("dccp_socket") S_("memprotect") + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_(NULL) + S_("peer") diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h index a9c2b20f14b5..09e9dd23ee1a 100644 --- a/security/selinux/include/flask.h +++ b/security/selinux/include/flask.h @@ -50,6 +50,7 @@ #define SECCLASS_KEY 58 #define SECCLASS_DCCP_SOCKET 60 #define SECCLASS_MEMPROTECT 61 +#define SECCLASS_PEER 68 /* * Security identifier indices for initial entities -- cgit v1.2.1 From 220deb966ea51e0dedb6a187c0763120809f3e64 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:38:23 -0500 Subject: SELinux: Better integration between peer labeling subsystems Rework the handling of network peer labels so that the different peer labeling subsystems work better together. This includes moving both subsystems to a single "peer" object class which involves not only changes to the permission checks but an improved method of consolidating multiple packet peer labels. As part of this work the inbound packet permission check code has been heavily modified to handle both the old and new behavior in as sane a fashion as possible. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 204 ++++++++++++++++++++---------------- security/selinux/include/netlabel.h | 3 + security/selinux/include/objsec.h | 2 +- security/selinux/include/security.h | 4 + security/selinux/netlabel.c | 10 +- security/selinux/ss/services.c | 85 +++++++++++++++ 6 files changed, 208 insertions(+), 100 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4bca0af4f2af..bfe9a05db3a2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -50,6 +50,7 @@ #include #include /* for local_port_range[] */ #include /* struct or_callable used in sock_rcv_skb */ +#include #include #include #include @@ -3426,36 +3427,39 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, } /** - * selinux_skb_extlbl_sid - Determine the external label of a packet + * selinux_skb_peerlbl_sid - Determine the peer label of a packet * @skb: the packet * @family: protocol family - * @sid: the packet's SID + * @sid: the packet's peer label SID * * Description: - * Check the various different forms of external packet labeling and determine - * the external SID for the packet. If only one form of external labeling is - * present then it is used, if both labeled IPsec and NetLabel labels are - * present then the SELinux type information is taken from the labeled IPsec - * SA and the MLS sensitivity label information is taken from the NetLabel - * security attributes. This bit of "magic" is done in the call to - * selinux_netlbl_skbuff_getsid(). + * Check the various different forms of network peer labeling and determine + * the peer label/SID for the packet; most of the magic actually occurs in + * the security server function security_net_peersid_cmp(). The function + * returns zero if the value in @sid is valid (although it may be SECSID_NULL) + * or -EACCES if @sid is invalid due to inconsistencies with the different + * peer labels. * */ -static void selinux_skb_extlbl_sid(struct sk_buff *skb, - u16 family, - u32 *sid) +static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) { u32 xfrm_sid; u32 nlbl_sid; + u32 nlbl_type; selinux_skb_xfrm_sid(skb, &xfrm_sid); - if (selinux_netlbl_skbuff_getsid(skb, - family, - (xfrm_sid == SECSID_NULL ? - SECINITSID_NETMSG : xfrm_sid), - &nlbl_sid) != 0) - nlbl_sid = SECSID_NULL; - *sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid); + selinux_netlbl_skbuff_getsid(skb, + family, + SECINITSID_NETMSG, + &nlbl_type, + &nlbl_sid); + + if (security_net_peersid_resolve(nlbl_sid, nlbl_type, + xfrm_sid, + sid) != 0) + return -EACCES; + + return 0; } /* socket security operations */ @@ -3521,6 +3525,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, if (sock->sk) { sksec = sock->sk->sk_security; sksec->sid = isec->sid; + sksec->sclass = isec->sclass; err = selinux_netlbl_socket_post_create(sock); } @@ -3824,104 +3829,114 @@ static int selinux_socket_unix_may_send(struct socket *sock, return 0; } -static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, - struct avc_audit_data *ad, - u16 family, char *addrp) +static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, + struct sk_buff *skb, + struct avc_audit_data *ad, + u16 family, + char *addrp) { - int err = 0; - u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; - struct socket *sock; - u16 sock_class = 0; - u32 sock_sid = 0; - - read_lock_bh(&sk->sk_callback_lock); - sock = sk->sk_socket; - if (sock) { - struct inode *inode; - inode = SOCK_INODE(sock); - if (inode) { - struct inode_security_struct *isec; - isec = inode->i_security; - sock_sid = isec->sid; - sock_class = isec->sclass; - } - } - read_unlock_bh(&sk->sk_callback_lock); - if (!sock_sid) - goto out; - - if (!skb->dev) - goto out; + int err; + struct sk_security_struct *sksec = sk->sk_security; + u16 sk_class; + u32 netif_perm, node_perm, recv_perm; + u32 port_sid, node_sid, if_sid, sk_sid; - err = sel_netif_sid(skb->iif, &if_sid); - if (err) - goto out; + sk_sid = sksec->sid; + sk_class = sksec->sclass; - switch (sock_class) { + switch (sk_class) { case SECCLASS_UDP_SOCKET: netif_perm = NETIF__UDP_RECV; node_perm = NODE__UDP_RECV; recv_perm = UDP_SOCKET__RECV_MSG; break; - case SECCLASS_TCP_SOCKET: netif_perm = NETIF__TCP_RECV; node_perm = NODE__TCP_RECV; recv_perm = TCP_SOCKET__RECV_MSG; break; - case SECCLASS_DCCP_SOCKET: netif_perm = NETIF__DCCP_RECV; node_perm = NODE__DCCP_RECV; recv_perm = DCCP_SOCKET__RECV_MSG; break; - default: netif_perm = NETIF__RAWIP_RECV; node_perm = NODE__RAWIP_RECV; + recv_perm = 0; break; } - err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); + err = sel_netif_sid(skb->iif, &if_sid); if (err) - goto out; + return err; + err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); + if (err) + return err; err = sel_netnode_sid(addrp, family, &node_sid); if (err) - goto out; - - err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad); + return err; + err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad); if (err) - goto out; + return err; - if (recv_perm) { - u32 port_sid; + if (!recv_perm) + return 0; + err = security_port_sid(sk->sk_family, sk->sk_type, + sk->sk_protocol, ntohs(ad->u.net.sport), + &port_sid); + if (err) + return err; + return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad); +} - err = security_port_sid(sk->sk_family, sk->sk_type, - sk->sk_protocol, ntohs(ad->u.net.sport), - &port_sid); - if (err) - goto out; +static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, + struct avc_audit_data *ad, + u16 family, char *addrp) +{ + int err; + struct sk_security_struct *sksec = sk->sk_security; + u32 peer_sid; + u32 sk_sid = sksec->sid; + + if (selinux_compat_net) + err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad, + family, addrp); + else + err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, ad); + if (err) + return err; - err = avc_has_perm(sock_sid, port_sid, - sock_class, recv_perm, ad); + if (selinux_policycap_netpeer) { + err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); + if (err) + return err; + err = avc_has_perm(sk_sid, peer_sid, + SECCLASS_PEER, PEER__RECV, ad); + } else { + err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad); + if (err) + return err; + err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad); } -out: return err; } static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { - u16 family; - char *addrp; - int err = 0; - struct avc_audit_data ad; + int err; struct sk_security_struct *sksec = sk->sk_security; + u16 family = sk->sk_family; + u32 sk_sid = sksec->sid; + u32 peer_sid; + struct avc_audit_data ad; + char *addrp; - family = sk->sk_family; if (family != PF_INET && family != PF_INET6) - goto out; + return 0; /* Handle mapped IPv4 packets arriving via IPv6 sockets */ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) @@ -3930,26 +3945,27 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) AVC_AUDIT_DATA_INIT(&ad, NET); ad.u.net.netif = skb->iif; ad.u.net.family = family; - err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) - goto out; + return err; - if (selinux_compat_net) - err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, addrp); - else - err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, - PACKET__RECV, &ad); - if (err) - goto out; + /* If any sort of compatibility mode is enabled then handoff processing + * to the selinux_sock_rcv_skb_compat() function to deal with the + * special handling. We do this in an attempt to keep this function + * as fast and as clean as possible. */ + if (selinux_compat_net || !selinux_policycap_netpeer) + return selinux_sock_rcv_skb_compat(sk, skb, &ad, + family, addrp); - err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad); + err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); if (err) - goto out; + return err; - err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad); -out: - return err; + err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); + if (err) + return err; + return avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); } static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, @@ -4011,7 +4027,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * if (sock && family == PF_UNIX) selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid); else if (skb) - selinux_skb_extlbl_sid(skb, family, &peer_secid); + selinux_skb_peerlbl_sid(skb, family, &peer_secid); out: *secid = peer_secid; @@ -4037,6 +4053,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) newssec->sid = ssec->sid; newssec->peer_sid = ssec->peer_sid; + newssec->sclass = ssec->sclass; selinux_netlbl_sk_security_clone(ssec, newssec); } @@ -4060,6 +4077,7 @@ static void selinux_sock_graft(struct sock* sk, struct socket *parent) if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || sk->sk_family == PF_UNIX) isec->sid = sksec->sid; + sksec->sclass = isec->sclass; selinux_netlbl_sock_graft(sk, parent); } @@ -4072,7 +4090,9 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, u32 newsid; u32 peersid; - selinux_skb_extlbl_sid(skb, sk->sk_family, &peersid); + err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid); + if (err) + return err; if (peersid == SECSID_NULL) { req->secid = sksec->sid; req->peer_secid = SECSID_NULL; @@ -4110,7 +4130,7 @@ static void selinux_inet_conn_established(struct sock *sk, { struct sk_security_struct *sksec = sk->sk_security; - selinux_skb_extlbl_sid(skb, sk->sk_family, &sksec->peer_sid); + selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid); } static void selinux_req_classify_flow(const struct request_sock *req, diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 272769a1cb96..c8c05a6f298c 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -49,6 +49,7 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, u32 base_sid, + u32 *type, u32 *sid); void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); @@ -89,8 +90,10 @@ static inline void selinux_netlbl_sk_security_clone( static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, u32 base_sid, + u32 *type, u32 *sid) { + *type = NETLBL_NLTYPE_NONE; *sid = SECSID_NULL; return 0; } diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 95fb5ec17354..c6c2bb4ebacc 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -113,8 +113,8 @@ struct sk_security_struct { struct sock *sk; /* back pointer to sk object */ u32 sid; /* SID of this object */ u32 peer_sid; /* SID of peer */ -#ifdef CONFIG_NETLABEL u16 sclass; /* sock security class */ +#ifdef CONFIG_NETLABEL enum { /* NetLabel state */ NLBL_UNSET = 0, NLBL_REQUIRE, diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index a22de9771806..9347e2daa8d4 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -99,6 +99,10 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); +int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, + u32 xfrm_sid, + u32 *peer_sid); + int security_get_classes(char ***classes, int *nclasses); int security_get_permissions(char *class, char ***perms, int *nperms); int security_get_reject_unknown(void); diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index f4bcbf12a4c9..b54d28fd3b5d 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -137,7 +137,6 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, * lock as other threads could have access to ssec */ rcu_read_lock(); selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); - newssec->sclass = ssec->sclass; rcu_read_unlock(); } @@ -146,6 +145,7 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, * @skb: the packet * @family: protocol family * @base_sid: the SELinux SID to use as a context for MLS only attributes + * @type: NetLabel labeling protocol type * @sid: the SID * * Description: @@ -157,6 +157,7 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, u32 base_sid, + u32 *type, u32 *sid) { int rc; @@ -177,6 +178,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, netlbl_cache_add(skb, &secattr); } else *sid = SECSID_NULL; + *type = secattr.type; netlbl_secattr_destroy(&secattr); return rc; @@ -194,13 +196,10 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, */ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) { - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec = sk->sk_security; struct netlbl_lsm_secattr secattr; u32 nlbl_peer_sid; - sksec->sclass = isec->sclass; - rcu_read_lock(); if (sksec->nlbl_state != NLBL_REQUIRE) { @@ -238,11 +237,8 @@ int selinux_netlbl_socket_post_create(struct socket *sock) { int rc = 0; struct sock *sk = sock->sk; - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec = sk->sk_security; - sksec->sclass = isec->sclass; - rcu_read_lock(); if (sksec->nlbl_state == NLBL_REQUIRE) rc = selinux_netlbl_sock_setsid(sk, sksec->sid); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8ee04a424df7..7f0ee1b91e1d 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2054,6 +2054,91 @@ out: return rc; } +/** + * security_net_peersid_resolve - Compare and resolve two network peer SIDs + * @nlbl_sid: NetLabel SID + * @nlbl_type: NetLabel labeling protocol type + * @xfrm_sid: XFRM SID + * + * Description: + * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be + * resolved into a single SID it is returned via @peer_sid and the function + * returns zero. Otherwise @peer_sid is set to SECSID_NULL and the function + * returns a negative value. A table summarizing the behavior is below: + * + * | function return | @sid + * ------------------------------+-----------------+----------------- + * no peer labels | 0 | SECSID_NULL + * single peer label | 0 | + * multiple, consistent labels | 0 | + * multiple, inconsistent labels | - | SECSID_NULL + * + */ +int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, + u32 xfrm_sid, + u32 *peer_sid) +{ + int rc; + struct context *nlbl_ctx; + struct context *xfrm_ctx; + + /* handle the common (which also happens to be the set of easy) cases + * right away, these two if statements catch everything involving a + * single or absent peer SID/label */ + if (xfrm_sid == SECSID_NULL) { + *peer_sid = nlbl_sid; + return 0; + } + /* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label + * and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label + * is present */ + if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) { + *peer_sid = xfrm_sid; + return 0; + } + + /* we don't need to check ss_initialized here since the only way both + * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the + * security server was initialized and ss_initialized was true */ + if (!selinux_mls_enabled) { + *peer_sid = SECSID_NULL; + return 0; + } + + POLICY_RDLOCK; + + nlbl_ctx = sidtab_search(&sidtab, nlbl_sid); + if (!nlbl_ctx) { + printk(KERN_ERR + "security_sid_mls_cmp: unrecognized SID %d\n", + nlbl_sid); + rc = -EINVAL; + goto out_slowpath; + } + xfrm_ctx = sidtab_search(&sidtab, xfrm_sid); + if (!xfrm_ctx) { + printk(KERN_ERR + "security_sid_mls_cmp: unrecognized SID %d\n", + xfrm_sid); + rc = -EINVAL; + goto out_slowpath; + } + rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES); + +out_slowpath: + POLICY_RDUNLOCK; + if (rc == 0) + /* at present NetLabel SIDs/labels really only carry MLS + * information so if the MLS portion of the NetLabel SID + * matches the MLS portion of the labeled XFRM SID/label + * then pass along the XFRM SID as it is the most + * expressive */ + *peer_sid = xfrm_sid; + else + *peer_sid = SECSID_NULL; + return rc; +} + static int get_classes_callback(void *k, void *d, void *args) { struct class_datum *datum = d; -- cgit v1.2.1 From d621d35e576aa20a0ddae8022c3810f38357c8ff Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:43:36 -0500 Subject: SELinux: Enable dynamic enable/disable of the network access checks This patch introduces a mechanism for checking when labeled IPsec or SECMARK are in use by keeping introducing a configuration reference counter for each subsystem. In the case of labeled IPsec, whenever a labeled SA or SPD entry is created the labeled IPsec/XFRM reference count is increased and when the entry is removed it is decreased. In the case of SECMARK, when a SECMARK target is created the reference count is increased and later decreased when the target is removed. These reference counters allow SELinux to quickly determine if either of these subsystems are enabled. NetLabel already has a similar mechanism which provides the netlbl_enabled() function. This patch also renames the selinux_relabel_packet_permission() function to selinux_secmark_relabel_packet_permission() as the original name and description were misleading in that they referenced a single packet label which is not the case. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/exports.c | 20 ++++++++++++++++-- security/selinux/hooks.c | 46 +++++++++++++++++++++++++++++++++-------- security/selinux/include/xfrm.h | 12 +++++++++++ security/selinux/xfrm.c | 18 ++++++++++++++-- 4 files changed, 83 insertions(+), 13 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/exports.c b/security/selinux/exports.c index b6f96943be1f..87d2bb3ea355 100644 --- a/security/selinux/exports.c +++ b/security/selinux/exports.c @@ -17,10 +17,14 @@ #include #include #include +#include #include "security.h" #include "objsec.h" +/* SECMARK reference count */ +extern atomic_t selinux_secmark_refcount; + int selinux_sid_to_string(u32 sid, char **ctx, u32 *ctxlen) { if (selinux_enabled) @@ -74,7 +78,7 @@ int selinux_string_to_sid(char *str, u32 *sid) } EXPORT_SYMBOL_GPL(selinux_string_to_sid); -int selinux_relabel_packet_permission(u32 sid) +int selinux_secmark_relabel_packet_permission(u32 sid) { if (selinux_enabled) { struct task_security_struct *tsec = current->security; @@ -84,4 +88,16 @@ int selinux_relabel_packet_permission(u32 sid) } return 0; } -EXPORT_SYMBOL_GPL(selinux_relabel_packet_permission); +EXPORT_SYMBOL_GPL(selinux_secmark_relabel_packet_permission); + +void selinux_secmark_refcount_inc(void) +{ + atomic_inc(&selinux_secmark_refcount); +} +EXPORT_SYMBOL_GPL(selinux_secmark_refcount_inc); + +void selinux_secmark_refcount_dec(void) +{ + atomic_dec(&selinux_secmark_refcount); +} +EXPORT_SYMBOL_GPL(selinux_secmark_refcount_dec); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bfe9a05db3a2..6156241c8770 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -51,8 +51,10 @@ #include /* for local_port_range[] */ #include /* struct or_callable used in sock_rcv_skb */ #include +#include #include #include +#include #include #include #include /* for network interface checks */ @@ -91,6 +93,9 @@ extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); extern int selinux_compat_net; extern struct security_operations *security_ops; +/* SECMARK reference count */ +atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); + #ifdef CONFIG_SECURITY_SELINUX_DEVELOP int selinux_enforcing = 0; @@ -157,6 +162,21 @@ getsecurity_exit: return len; } +/** + * selinux_secmark_enabled - Check to see if SECMARK is currently enabled + * + * Description: + * 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. + * + */ +static int selinux_secmark_enabled(void) +{ + return (atomic_read(&selinux_secmark_refcount) > 0); +} + /* Allocate and free functions for each kind of security blob. */ static int task_alloc_security(struct task_struct *task) @@ -3931,7 +3951,6 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) struct sk_security_struct *sksec = sk->sk_security; u16 family = sk->sk_family; u32 sk_sid = sksec->sid; - u32 peer_sid; struct avc_audit_data ad; char *addrp; @@ -3957,15 +3976,24 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return selinux_sock_rcv_skb_compat(sk, skb, &ad, family, addrp); - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, - PACKET__RECV, &ad); - if (err) - return err; + if (selinux_secmark_enabled()) { + err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); + if (err) + return err; + } - err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); - if (err) - return err; - return avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); + if (netlbl_enabled() || selinux_xfrm_enabled()) { + u32 peer_sid; + + err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); + if (err) + return err; + err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, + PEER__RECV, &ad); + } + + return err; } static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 31929e39f5ca..36b0510efa7b 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -32,6 +32,13 @@ static inline struct inode_security_struct *get_sock_isec(struct sock *sk) } #ifdef CONFIG_SECURITY_NETWORK_XFRM +extern atomic_t selinux_xfrm_refcount; + +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 avc_audit_data *ad); int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, @@ -43,6 +50,11 @@ static inline void selinux_xfrm_notify_policyload(void) atomic_inc(&flow_cache_genid); } #else +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 avc_audit_data *ad) { diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index e07603969033..7e158205d081 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -46,11 +46,14 @@ #include #include #include +#include #include "avc.h" #include "objsec.h" #include "xfrm.h" +/* Labeled XFRM instance counter */ +atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0); /* * Returns true if an LSM/SELinux context @@ -293,6 +296,9 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, BUG_ON(!uctx); err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, 0); + if (err == 0) + atomic_inc(&selinux_xfrm_refcount); + return err; } @@ -340,10 +346,13 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp) struct xfrm_sec_ctx *ctx = xp->security; int rc = 0; - if (ctx) + if (ctx) { rc = avc_has_perm(tsec->sid, ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); + if (rc == 0) + atomic_dec(&selinux_xfrm_refcount); + } return rc; } @@ -360,6 +369,8 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct BUG_ON(!x); err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid); + if (err == 0) + atomic_inc(&selinux_xfrm_refcount); return err; } @@ -382,10 +393,13 @@ int selinux_xfrm_state_delete(struct xfrm_state *x) struct xfrm_sec_ctx *ctx = x->security; int rc = 0; - if (ctx) + if (ctx) { rc = avc_has_perm(tsec->sid, ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); + if (rc == 0) + atomic_dec(&selinux_xfrm_refcount); + } return rc; } -- cgit v1.2.1 From 5dbe1eb0cfc144a2b0cb1466e22bcb6fc34229a8 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:44:18 -0500 Subject: SELinux: Allow NetLabel to directly cache SIDs Now that the SELinux NetLabel "base SID" is always the netmsg initial SID we can do a big optimization - caching the SID and not just the MLS attributes. This not only saves a lot of per-packet memory allocations and copies but it has a nice side effect of removing a chunk of code. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 6 +- security/selinux/include/netlabel.h | 2 - security/selinux/include/security.h | 2 - security/selinux/netlabel.c | 55 ++++++++++------ security/selinux/ss/services.c | 124 ++++++------------------------------ 5 files changed, 55 insertions(+), 134 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6156241c8770..c90e865a8603 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3468,11 +3468,7 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) u32 nlbl_type; selinux_skb_xfrm_sid(skb, &xfrm_sid); - selinux_netlbl_skbuff_getsid(skb, - family, - SECINITSID_NETMSG, - &nlbl_type, - &nlbl_sid); + selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); if (security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index c8c05a6f298c..00a2809c8506 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -48,7 +48,6 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, - u32 base_sid, u32 *type, u32 *sid); @@ -89,7 +88,6 @@ static inline void selinux_netlbl_sk_security_clone( static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, - u32 base_sid, u32 *type, u32 *sid) { diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 9347e2daa8d4..23137c17f917 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -124,7 +124,6 @@ int security_genfs_sid(const char *fstype, char *name, u16 sclass, #ifdef CONFIG_NETLABEL int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, - u32 base_sid, u32 *sid); int security_netlbl_sid_to_secattr(u32 sid, @@ -132,7 +131,6 @@ int security_netlbl_sid_to_secattr(u32 sid, #else static inline int security_netlbl_secattr_to_sid( struct netlbl_lsm_secattr *secattr, - u32 base_sid, u32 *sid) { return -EIDRM; diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index b54d28fd3b5d..0fa2be4149e8 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -35,6 +35,33 @@ #include "objsec.h" #include "security.h" +/** + * selinux_netlbl_sidlookup_cached - Cache a SID lookup + * @skb: the packet + * @secattr: the NetLabel security attributes + * @sid: the SID + * + * Description: + * Query the SELinux security server to lookup the correct SID for the given + * security attributes. If the query is successful, cache the result to speed + * up future lookups. Returns zero on success, negative values on failure. + * + */ +static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb, + struct netlbl_lsm_secattr *secattr, + u32 *sid) +{ + int rc; + + rc = security_netlbl_secattr_to_sid(secattr, sid); + if (rc == 0 && + (secattr->flags & NETLBL_SECATTR_CACHEABLE) && + (secattr->flags & NETLBL_SECATTR_CACHE)) + netlbl_cache_add(skb, secattr); + + return rc; +} + /** * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism * @sk: the socket to label @@ -144,7 +171,6 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel * @skb: the packet * @family: protocol family - * @base_sid: the SELinux SID to use as a context for MLS only attributes * @type: NetLabel labeling protocol type * @sid: the SID * @@ -156,7 +182,6 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, */ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, - u32 base_sid, u32 *type, u32 *sid) { @@ -170,13 +195,9 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); - if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) { - rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid); - if (rc == 0 && - (secattr.flags & NETLBL_SECATTR_CACHEABLE) && - (secattr.flags & NETLBL_SECATTR_CACHE)) - netlbl_cache_add(skb, &secattr); - } else + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) + rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid); + else *sid = SECSID_NULL; *type = secattr.type; netlbl_secattr_destroy(&secattr); @@ -210,9 +231,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) netlbl_secattr_init(&secattr); if (netlbl_sock_getattr(sk, &secattr) == 0 && secattr.flags != NETLBL_SECATTR_NONE && - security_netlbl_secattr_to_sid(&secattr, - SECINITSID_NETMSG, - &nlbl_peer_sid) == 0) + security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0) sksec->peer_sid = nlbl_peer_sid; netlbl_secattr_destroy(&secattr); @@ -316,15 +335,9 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); - if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) { - rc = security_netlbl_secattr_to_sid(&secattr, - SECINITSID_NETMSG, - &nlbl_sid); - if (rc == 0 && - (secattr.flags & NETLBL_SECATTR_CACHEABLE) && - (secattr.flags & NETLBL_SECATTR_CACHE)) - netlbl_cache_add(skb, &secattr); - } else + if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) + rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid); + else nlbl_sid = SECINITSID_UNLABELED; netlbl_secattr_destroy(&secattr); if (rc != 0) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 7f0ee1b91e1d..f96dec1f9258 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2547,50 +2547,10 @@ void selinux_audit_set_callback(int (*callback)(void)) } #ifdef CONFIG_NETLABEL -/* - * NetLabel cache structure - */ -#define NETLBL_CACHE(x) ((struct selinux_netlbl_cache *)(x)) -#define NETLBL_CACHE_T_NONE 0 -#define NETLBL_CACHE_T_SID 1 -#define NETLBL_CACHE_T_MLS 2 -struct selinux_netlbl_cache { - u32 type; - union { - u32 sid; - struct mls_range mls_label; - } data; -}; - -/** - * security_netlbl_cache_free - Free the NetLabel cached data - * @data: the data to free - * - * Description: - * This function is intended to be used as the free() callback inside the - * netlbl_lsm_cache structure. - * - */ -static void security_netlbl_cache_free(const void *data) -{ - struct selinux_netlbl_cache *cache; - - if (data == NULL) - return; - - cache = NETLBL_CACHE(data); - switch (cache->type) { - case NETLBL_CACHE_T_MLS: - ebitmap_destroy(&cache->data.mls_label.level[0].cat); - break; - } - kfree(data); -} - /** * security_netlbl_cache_add - Add an entry to the NetLabel cache * @secattr: the NetLabel packet security attributes - * @ctx: the SELinux context + * @sid: the SELinux SID * * Description: * Attempt to cache the context in @ctx, which was derived from the packet in @@ -2599,60 +2559,46 @@ static void security_netlbl_cache_free(const void *data) * */ static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr, - struct context *ctx) + u32 sid) { - struct selinux_netlbl_cache *cache = NULL; + u32 *sid_cache; - secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); - if (secattr->cache == NULL) - return; - - cache = kzalloc(sizeof(*cache), GFP_ATOMIC); - if (cache == NULL) + sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC); + if (sid_cache == NULL) return; - - cache->type = NETLBL_CACHE_T_MLS; - if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, - &ctx->range.level[0].cat) != 0) { - kfree(cache); + secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); + if (secattr->cache == NULL) { + kfree(sid_cache); return; } - cache->data.mls_label.level[1].cat.highbit = - cache->data.mls_label.level[0].cat.highbit; - cache->data.mls_label.level[1].cat.node = - cache->data.mls_label.level[0].cat.node; - cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; - cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; - secattr->cache->free = security_netlbl_cache_free; - secattr->cache->data = (void *)cache; + *sid_cache = sid; + secattr->cache->free = kfree; + secattr->cache->data = sid_cache; secattr->flags |= NETLBL_SECATTR_CACHE; } /** * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID * @secattr: the NetLabel packet security attributes - * @base_sid: the SELinux SID to use as a context for MLS only attributes * @sid: the SELinux SID * * Description: * Convert the given NetLabel security attributes in @secattr into a * SELinux SID. If the @secattr field does not contain a full SELinux - * SID/context then use the context in @base_sid as the foundation. If - * possibile the 'cache' field of @secattr is set and the CACHE flag is set; - * this is to allow the @secattr to be used by NetLabel to cache the secattr to - * SID conversion for future lookups. Returns zero on success, negative - * values on failure. + * SID/context then use SECINITSID_NETMSG as the foundation. If possibile the + * 'cache' field of @secattr is set and the CACHE flag is set; this is to + * allow the @secattr to be used by NetLabel to cache the secattr to SID + * conversion for future lookups. Returns zero on success, negative values on + * failure. * */ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, - u32 base_sid, u32 *sid) { int rc = -EIDRM; struct context *ctx; struct context ctx_new; - struct selinux_netlbl_cache *cache; if (!ss_initialized) { *sid = SECSID_NULL; @@ -2662,43 +2608,13 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, POLICY_RDLOCK; if (secattr->flags & NETLBL_SECATTR_CACHE) { - cache = NETLBL_CACHE(secattr->cache->data); - switch (cache->type) { - case NETLBL_CACHE_T_SID: - *sid = cache->data.sid; - rc = 0; - break; - case NETLBL_CACHE_T_MLS: - ctx = sidtab_search(&sidtab, base_sid); - if (ctx == NULL) - goto netlbl_secattr_to_sid_return; - - ctx_new.user = ctx->user; - ctx_new.role = ctx->role; - ctx_new.type = ctx->type; - ctx_new.range.level[0].sens = - cache->data.mls_label.level[0].sens; - ctx_new.range.level[0].cat.highbit = - cache->data.mls_label.level[0].cat.highbit; - ctx_new.range.level[0].cat.node = - cache->data.mls_label.level[0].cat.node; - ctx_new.range.level[1].sens = - cache->data.mls_label.level[1].sens; - ctx_new.range.level[1].cat.highbit = - cache->data.mls_label.level[1].cat.highbit; - ctx_new.range.level[1].cat.node = - cache->data.mls_label.level[1].cat.node; - - rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid); - break; - default: - goto netlbl_secattr_to_sid_return; - } + *sid = *(u32 *)secattr->cache->data; + rc = 0; } else if (secattr->flags & NETLBL_SECATTR_SECID) { *sid = secattr->attr.secid; rc = 0; } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { - ctx = sidtab_search(&sidtab, base_sid); + ctx = sidtab_search(&sidtab, SECINITSID_NETMSG); if (ctx == NULL) goto netlbl_secattr_to_sid_return; @@ -2725,7 +2641,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, if (rc != 0) goto netlbl_secattr_to_sid_return_cleanup; - security_netlbl_cache_add(secattr, &ctx_new); + security_netlbl_cache_add(secattr, *sid); ebitmap_destroy(&ctx_new.range.level[0].cat); } else { -- cgit v1.2.1 From effad8df44261031a882e1a895415f7186a5098e Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:49:27 -0500 Subject: SELinux: Add network ingress and egress control permission checks This patch implements packet ingress/egress controls for SELinux which allow SELinux security policy to control the flow of all IPv4 and IPv6 packets into and out of the system. Currently SELinux does not have proper control over forwarded packets and this patch corrects this problem. Special thanks to Venkat Yekkirala whose earlier work on this topic eventually led to this patch. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 402 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 280 insertions(+), 122 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c90e865a8603..b3c064744d32 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -12,8 +12,8 @@ * Copyright (C) 2003 Red Hat, Inc., James Morris * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * - * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. - * Paul Moore, + * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. + * Paul Moore * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. * Yuichi Nakamura * @@ -3845,6 +3845,29 @@ static int selinux_socket_unix_may_send(struct socket *sock, return 0; } +static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, + u32 peer_sid, + struct avc_audit_data *ad) +{ + int err; + u32 if_sid; + u32 node_sid; + + err = sel_netif_sid(ifindex, &if_sid); + if (err) + return err; + err = avc_has_perm(peer_sid, if_sid, + SECCLASS_NETIF, NETIF__INGRESS, ad); + if (err) + return err; + + err = sel_netnode_sid(addrp, family, &node_sid); + if (err) + return err; + return avc_has_perm(peer_sid, node_sid, + SECCLASS_NODE, NODE__RECVFROM, ad); +} + static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, struct sk_buff *skb, struct avc_audit_data *ad, @@ -3972,23 +3995,27 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return selinux_sock_rcv_skb_compat(sk, skb, &ad, family, addrp); - if (selinux_secmark_enabled()) { - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, - PACKET__RECV, &ad); - if (err) - return err; - } - if (netlbl_enabled() || selinux_xfrm_enabled()) { u32 peer_sid; err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); + if (err) + return err; + err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family, + peer_sid, &ad); if (err) return err; err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); } + if (selinux_secmark_enabled()) { + err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); + if (err) + return err; + } + return err; } @@ -4201,151 +4228,255 @@ out: #ifdef CONFIG_NETFILTER -static int selinux_ip_postroute_last_compat(struct sock *sk, - struct net_device *dev, - struct avc_audit_data *ad, - u16 family, - char *addrp) +static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, + u16 family) { - int err = 0; - u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; - struct socket *sock; - struct inode *inode; - struct inode_security_struct *isec; + char *addrp; + u32 peer_sid; + struct avc_audit_data ad; + u8 secmark_active; + u8 peerlbl_active; - sock = sk->sk_socket; - if (!sock) - goto out; + if (!selinux_policycap_netpeer) + return NF_ACCEPT; - inode = SOCK_INODE(sock); - if (!inode) - goto out; + secmark_active = selinux_secmark_enabled(); + peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); + if (!secmark_active && !peerlbl_active) + return NF_ACCEPT; - isec = inode->i_security; - - err = sel_netif_sid(dev->ifindex, &if_sid); - if (err) - goto out; + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = ifindex; + ad.u.net.family = family; + if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) + return NF_DROP; + + if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) + return NF_DROP; + + if (peerlbl_active) + if (selinux_inet_sys_rcv_skb(ifindex, addrp, family, + peer_sid, &ad) != 0) + return NF_DROP; + + if (secmark_active) + if (avc_has_perm(peer_sid, skb->secmark, + SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) + return NF_DROP; + + return NF_ACCEPT; +} + +static unsigned int selinux_ipv4_forward(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return selinux_ip_forward(skb, in->ifindex, PF_INET); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static unsigned int selinux_ipv6_forward(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return selinux_ip_forward(skb, in->ifindex, PF_INET6); +} +#endif /* IPV6 */ + +static int selinux_ip_postroute_iptables_compat(struct sock *sk, + int ifindex, + struct avc_audit_data *ad, + u16 family, char *addrp) +{ + int err; + struct sk_security_struct *sksec = sk->sk_security; + u16 sk_class; + u32 netif_perm, node_perm, send_perm; + u32 port_sid, node_sid, if_sid, sk_sid; + + sk_sid = sksec->sid; + sk_class = sksec->sclass; - switch (isec->sclass) { + switch (sk_class) { case SECCLASS_UDP_SOCKET: netif_perm = NETIF__UDP_SEND; node_perm = NODE__UDP_SEND; send_perm = UDP_SOCKET__SEND_MSG; break; - case SECCLASS_TCP_SOCKET: netif_perm = NETIF__TCP_SEND; node_perm = NODE__TCP_SEND; send_perm = TCP_SOCKET__SEND_MSG; break; - case SECCLASS_DCCP_SOCKET: netif_perm = NETIF__DCCP_SEND; node_perm = NODE__DCCP_SEND; send_perm = DCCP_SOCKET__SEND_MSG; break; - default: netif_perm = NETIF__RAWIP_SEND; node_perm = NODE__RAWIP_SEND; + send_perm = 0; break; } - err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad); + err = sel_netif_sid(ifindex, &if_sid); if (err) - goto out; + return err; + err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); + return err; err = sel_netnode_sid(addrp, family, &node_sid); if (err) - goto out; - - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad); + return err; + err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad); if (err) - goto out; + return err; - if (send_perm) { - u32 port_sid; - - err = security_port_sid(sk->sk_family, - sk->sk_type, - sk->sk_protocol, - ntohs(ad->u.net.dport), - &port_sid); - if (err) - goto out; + if (send_perm != 0) + return 0; + + err = security_port_sid(sk->sk_family, sk->sk_type, + sk->sk_protocol, ntohs(ad->u.net.dport), + &port_sid); + if (err) + return err; + return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad); +} + +static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, + int ifindex, + struct avc_audit_data *ad, + u16 family, + char *addrp, + u8 proto) +{ + struct sock *sk = skb->sk; + struct sk_security_struct *sksec; - err = avc_has_perm(isec->sid, port_sid, isec->sclass, - send_perm, ad); + if (sk == NULL) + return NF_ACCEPT; + sksec = sk->sk_security; + + if (selinux_compat_net) { + if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex, + ad, family, addrp)) + return NF_DROP; + } else { + if (avc_has_perm(sksec->sid, skb->secmark, + SECCLASS_PACKET, PACKET__SEND, ad)) + return NF_DROP; } -out: - return err; + + if (selinux_policycap_netpeer) + if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto)) + return NF_DROP; + + return NF_ACCEPT; } -static unsigned int selinux_ip_postroute_last(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *), - u16 family) +static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, + u16 family) { - char *addrp; - int err = 0; + u32 secmark_perm; + u32 peer_sid; struct sock *sk; struct avc_audit_data ad; - struct net_device *dev = (struct net_device *)out; - struct sk_security_struct *sksec; + char *addrp; u8 proto; - - sk = skb->sk; - if (!sk) - goto out; - - sksec = sk->sk_security; + u8 secmark_active; + u8 peerlbl_active; AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->ifindex; + ad.u.net.netif = ifindex; ad.u.net.family = family; + if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) + return NF_DROP; - err = selinux_parse_skb(skb, &ad, &addrp, 0, &proto); - if (err) - goto out; - - if (selinux_compat_net) - err = selinux_ip_postroute_last_compat(sk, dev, &ad, - family, addrp); - else - err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, - PACKET__SEND, &ad); + /* If any sort of compatibility mode is enabled then handoff processing + * to the selinux_ip_postroute_compat() function to deal with the + * special handling. We do this in an attempt to keep this function + * as fast and as clean as possible. */ + if (selinux_compat_net || !selinux_policycap_netpeer) + return selinux_ip_postroute_compat(skb, ifindex, &ad, + family, addrp, proto); + + /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec + * packet transformation so allow the packet to pass without any checks + * since we'll have another chance to perform access control checks + * when the packet is on it's final way out. + * NOTE: there appear to be some IPv6 multicast cases where skb->dst + * is NULL, in this case go ahead and apply access control. */ + if (skb->dst != NULL && skb->dst->xfrm != NULL) + return NF_ACCEPT; + + secmark_active = selinux_secmark_enabled(); + peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); + if (!secmark_active && !peerlbl_active) + return NF_ACCEPT; + + /* if the packet is locally generated (skb->sk != NULL) then use the + * socket's label as the peer label, otherwise the packet is being + * forwarded through this system and we need to fetch the peer label + * directly from the packet */ + sk = skb->sk; + if (sk) { + struct sk_security_struct *sksec = sk->sk_security; + peer_sid = sksec->sid; + secmark_perm = PACKET__SEND; + } else { + if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) + return NF_DROP; + secmark_perm = PACKET__FORWARD_OUT; + } - if (err) - goto out; + if (secmark_active) + if (avc_has_perm(peer_sid, skb->secmark, + SECCLASS_PACKET, secmark_perm, &ad)) + return NF_DROP; + + if (peerlbl_active) { + u32 if_sid; + u32 node_sid; + + if (sel_netif_sid(ifindex, &if_sid)) + return NF_DROP; + if (avc_has_perm(peer_sid, if_sid, + SECCLASS_NETIF, NETIF__EGRESS, &ad)) + return NF_DROP; + + if (sel_netnode_sid(addrp, family, &node_sid)) + return NF_DROP; + if (avc_has_perm(peer_sid, node_sid, + SECCLASS_NODE, NODE__SENDTO, &ad)) + return NF_DROP; + } - err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto); -out: - return err ? NF_DROP : NF_ACCEPT; + return NF_ACCEPT; } -static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) +static unsigned int selinux_ipv4_postroute(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET); + return selinux_ip_postroute(skb, out->ifindex, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - -static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) +static unsigned int selinux_ipv6_postroute(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET6); + return selinux_ip_postroute(skb, out->ifindex, PF_INET6); } - #endif /* IPV6 */ #endif /* CONFIG_NETFILTER */ @@ -5333,22 +5464,40 @@ security_initcall(selinux_init); #if defined(CONFIG_NETFILTER) -static struct nf_hook_ops selinux_ipv4_op = { - .hook = selinux_ipv4_postroute_last, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_INET_POST_ROUTING, - .priority = NF_IP_PRI_SELINUX_LAST, +static struct nf_hook_ops selinux_ipv4_ops[] = { + { + .hook = selinux_ipv4_postroute, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_SELINUX_LAST, + }, + { + .hook = selinux_ipv4_forward, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_SELINUX_FIRST, + } }; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static struct nf_hook_ops selinux_ipv6_op = { - .hook = selinux_ipv6_postroute_last, - .owner = THIS_MODULE, - .pf = PF_INET6, - .hooknum = NF_INET_POST_ROUTING, - .priority = NF_IP6_PRI_SELINUX_LAST, +static struct nf_hook_ops selinux_ipv6_ops[] = { + { + .hook = selinux_ipv6_postroute, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP6_PRI_SELINUX_LAST, + }, + { + .hook = selinux_ipv6_forward, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP6_PRI_SELINUX_FIRST, + } }; #endif /* IPV6 */ @@ -5356,22 +5505,27 @@ static struct nf_hook_ops selinux_ipv6_op = { static int __init selinux_nf_ip_init(void) { int err = 0; + u32 iter; if (!selinux_enabled) goto out; printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); - err = nf_register_hook(&selinux_ipv4_op); - if (err) - panic("SELinux: nf_register_hook for IPv4: error %d\n", err); + for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) { + err = nf_register_hook(&selinux_ipv4_ops[iter]); + if (err) + panic("SELinux: nf_register_hook for IPv4: error %d\n", + err); + } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - - err = nf_register_hook(&selinux_ipv6_op); - if (err) - panic("SELinux: nf_register_hook for IPv6: error %d\n", err); - + for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) { + err = nf_register_hook(&selinux_ipv6_ops[iter]); + if (err) + panic("SELinux: nf_register_hook for IPv6: error %d\n", + err); + } #endif /* IPV6 */ out: @@ -5383,11 +5537,15 @@ __initcall(selinux_nf_ip_init); #ifdef CONFIG_SECURITY_SELINUX_DISABLE static void selinux_nf_ip_exit(void) { + u32 iter; + printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); - nf_unregister_hook(&selinux_ipv4_op); + for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) + nf_unregister_hook(&selinux_ipv4_ops[iter]); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - nf_unregister_hook(&selinux_ipv6_op); + for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) + nf_unregister_hook(&selinux_ipv6_ops[iter]); #endif /* IPV6 */ } #endif -- cgit v1.2.1 From 71f1cb05f773661b6fa98c7a635d7a395cd9c55d Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 29 Jan 2008 08:51:16 -0500 Subject: SELinux: Add warning messages on network denial due to error Currently network traffic can be sliently dropped due to non-avc errors which can lead to much confusion when trying to debug the problem. This patch adds warning messages so that when these events occur there is a user visible notification. Signed-off-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 29 ++++++++++++++++++++++++----- security/selinux/netif.c | 13 +++++++++++-- security/selinux/netnode.c | 6 +++++- 3 files changed, 40 insertions(+), 8 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b3c064744d32..81bfcf114484 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3443,6 +3443,11 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, break; } + if (unlikely(ret)) + printk(KERN_WARNING + "SELinux: failure in selinux_parse_skb()," + " unable to parse packet\n"); + return ret; } @@ -3463,6 +3468,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, */ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) { + int err; u32 xfrm_sid; u32 nlbl_sid; u32 nlbl_type; @@ -3470,10 +3476,13 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) selinux_skb_xfrm_sid(skb, &xfrm_sid); selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); - if (security_net_peersid_resolve(nlbl_sid, nlbl_type, - xfrm_sid, - sid) != 0) + err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid); + if (unlikely(err)) { + printk(KERN_WARNING + "SELinux: failure in selinux_skb_peerlbl_sid()," + " unable to determine packet's peer label\n"); return -EACCES; + } return 0; } @@ -3925,8 +3934,13 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, err = security_port_sid(sk->sk_family, sk->sk_type, sk->sk_protocol, ntohs(ad->u.net.sport), &port_sid); - if (err) + if (unlikely(err)) { + printk(KERN_WARNING + "SELinux: failure in" + " selinux_sock_rcv_skb_iptables_compat()," + " network port label not found\n"); return err; + } return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad); } @@ -4343,8 +4357,13 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk, err = security_port_sid(sk->sk_family, sk->sk_type, sk->sk_protocol, ntohs(ad->u.net.dport), &port_sid); - if (err) + if (unlikely(err)) { + printk(KERN_WARNING + "SELinux: failure in" + " selinux_ip_postroute_iptables_compat()," + " network port label not found\n"); return err; + } return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad); } diff --git a/security/selinux/netif.c b/security/selinux/netif.c index ee49a7382875..013d3117a86b 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -157,8 +157,12 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) * currently support containers */ dev = dev_get_by_index(&init_net, ifindex); - if (dev == NULL) + if (unlikely(dev == NULL)) { + printk(KERN_WARNING + "SELinux: failure in sel_netif_sid_slow()," + " invalid network interface (%d)\n", ifindex); return -ENOENT; + } spin_lock_bh(&sel_netif_lock); netif = sel_netif_find(ifindex); @@ -184,8 +188,13 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) out: spin_unlock_bh(&sel_netif_lock); dev_put(dev); - if (ret != 0) + if (unlikely(ret)) { + printk(KERN_WARNING + "SELinux: failure in sel_netif_sid_slow()," + " unable to determine network interface label (%d)\n", + ifindex); kfree(new); + } return ret; } diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index 49c527799240..f3c526f2cacb 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -264,8 +264,12 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) out: spin_unlock_bh(&sel_netnode_lock); - if (ret != 0) + if (unlikely(ret)) { + printk(KERN_WARNING + "SELinux: failure in sel_netnode_sid_slow()," + " unable to determine network node label\n"); kfree(new); + } return ret; } -- cgit v1.2.1 From 374ea019cacfa8b69ae49eea993b74cb5968970b Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 29 Jan 2008 00:11:52 +0200 Subject: selinux: make selinux_set_mnt_opts() static selinux_set_mnt_opts() can become static. Signed-off-by: Adrian Bunk Signed-off-by: James Morris --- security/selinux/hooks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security/selinux') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 81bfcf114484..be6de0b8734f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -583,8 +583,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag, * Allow filesystems with binary mount data to explicitly set mount point * labeling information. */ -int selinux_set_mnt_opts(struct super_block *sb, char **mount_options, - int *flags, int num_opts) +static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options, + int *flags, int num_opts) { int rc = 0, i; struct task_security_struct *tsec = current->security; -- cgit v1.2.1