Message ID | 20190222145718.5740-4-omosnace@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Allow initializing the kernfs node's secctx based on its parent | expand |
On 2/22/19 9:57 AM, Ondrej Mosnacek wrote: > Since kernfs supports the security xattr handlers, we can simply use > these to determine the inode's context, dropping the need to update it > from kernfs explicitly using a security_inode_notifysecctx() call. > > We achieve this by setting a new sbsec flag SE_SBGENFS_XATTR to all > mounts that are known to use kernfs under the hood and then fetching the > xattrs after determining the fallback genfs sid in > inode_doinit_with_dentry() when this flag is set. > > This will allow implementing full security xattr support in kernfs and > removing the ...notifysecctx() call in a subsequent patch. > > Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com> Acked-by: Stephen Smalley <sds@tycho.nsa.gov> > --- > security/selinux/hooks.c | 157 +++++++++++++++------------- > security/selinux/include/security.h | 1 + > 2 files changed, 85 insertions(+), 73 deletions(-) > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 81e012c66d95..0f62757b6634 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -793,11 +793,13 @@ static int selinux_set_mnt_opts(struct super_block *sb, > > if (!strcmp(sb->s_type->name, "debugfs") || > !strcmp(sb->s_type->name, "tracefs") || > - !strcmp(sb->s_type->name, "sysfs") || > - !strcmp(sb->s_type->name, "pstore") || > + !strcmp(sb->s_type->name, "pstore")) > + sbsec->flags |= SE_SBGENFS; > + > + if (!strcmp(sb->s_type->name, "sysfs") || > !strcmp(sb->s_type->name, "cgroup") || > !strcmp(sb->s_type->name, "cgroup2")) > - sbsec->flags |= SE_SBGENFS; > + sbsec->flags |= SE_SBGENFS | SE_SBGENFS_XATTR; > > if (!sbsec->behavior) { > /* > @@ -1392,6 +1394,67 @@ static int selinux_genfs_get_sid(struct dentry *dentry, > return rc; > } > > +static int inode_doinit_use_xattr(struct inode *inode, struct dentry *dentry, > + u32 def_sid, u32 *sid) > +{ > +#define INITCONTEXTLEN 255 > + char *context; > + unsigned int len; > + int rc; > + > + len = INITCONTEXTLEN; > + context = kmalloc(len + 1, GFP_NOFS); > + if (!context) > + return -ENOMEM; > + > + context[len] = '\0'; > + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); > + if (rc == -ERANGE) { > + kfree(context); > + > + /* Need a larger buffer. Query for the right size. */ > + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); > + if (rc < 0) > + return rc; > + > + len = rc; > + context = kmalloc(len + 1, GFP_NOFS); > + if (!context) > + return -ENOMEM; > + > + context[len] = '\0'; > + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, > + context, len); > + } > + if (rc < 0) { > + kfree(context); > + if (rc != -ENODATA) { > + pr_warn("SELinux: %s: getxattr returned %d for dev=%s ino=%ld\n", > + __func__, -rc, inode->i_sb->s_id, inode->i_ino); > + return rc; > + } > + *sid = def_sid; > + return 0; > + } > + > + rc = security_context_to_sid_default(&selinux_state, context, rc, sid, > + def_sid, GFP_NOFS); > + if (rc) { > + char *dev = inode->i_sb->s_id; > + unsigned long ino = inode->i_ino; > + > + if (rc == -EINVAL) { > + pr_notice_ratelimited("SELinux: inode=%lu on dev=%s was found to have an invalid context=%s. This indicates you may need to relabel the inode or the filesystem in question.\n", > + ino, dev, context); > + } else { > + pr_warn("SELinux: %s: context_to_sid(%s) returned %d for dev=%s ino=%ld\n", > + __func__, context, -rc, dev, ino); > + } > + } > + kfree(context); > + return 0; > +} > + > /* The inode's security attributes must be initialized before first use. */ > static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) > { > @@ -1400,9 +1463,6 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent > u32 task_sid, sid = 0; > u16 sclass; > struct dentry *dentry; > -#define INITCONTEXTLEN 255 > - char *context = NULL; > - unsigned len = 0; > int rc = 0; > > if (isec->initialized == LABEL_INITIALIZED) > @@ -1470,72 +1530,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent > goto out; > } > > - len = INITCONTEXTLEN; > - context = kmalloc(len+1, GFP_NOFS); > - if (!context) { > - rc = -ENOMEM; > - dput(dentry); > - goto out; > - } > - context[len] = '\0'; > - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); > - if (rc == -ERANGE) { > - kfree(context); > - > - /* Need a larger buffer. Query for the right size. */ > - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); > - if (rc < 0) { > - dput(dentry); > - goto out; > - } > - len = rc; > - context = kmalloc(len+1, GFP_NOFS); > - if (!context) { > - rc = -ENOMEM; > - dput(dentry); > - goto out; > - } > - context[len] = '\0'; > - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); > - } > + rc = inode_doinit_use_xattr(inode, dentry, sbsec->def_sid, > + &sid); > dput(dentry); > - if (rc < 0) { > - if (rc != -ENODATA) { > - pr_warn("SELinux: %s: getxattr returned " > - "%d for dev=%s ino=%ld\n", __func__, > - -rc, inode->i_sb->s_id, inode->i_ino); > - kfree(context); > - goto out; > - } > - /* Map ENODATA to the default file SID */ > - sid = sbsec->def_sid; > - rc = 0; > - } else { > - rc = security_context_to_sid_default(&selinux_state, > - context, rc, &sid, > - sbsec->def_sid, > - GFP_NOFS); > - if (rc) { > - char *dev = inode->i_sb->s_id; > - unsigned long ino = inode->i_ino; > - > - if (rc == -EINVAL) { > - if (printk_ratelimit()) > - pr_notice("SELinux: inode=%lu on dev=%s was found to have an invalid " > - "context=%s. This indicates you may need to relabel the inode or the " > - "filesystem in question.\n", ino, dev, context); > - } else { > - pr_warn("SELinux: %s: context_to_sid(%s) " > - "returned %d for dev=%s ino=%ld\n", > - __func__, context, -rc, dev, ino); > - } > - kfree(context); > - /* Leave with the unlabeled SID */ > - rc = 0; > - break; > - } > - } > - kfree(context); > + if (rc) > + goto out; > break; > case SECURITY_FS_USE_TASK: > sid = task_sid; > @@ -1586,9 +1585,21 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent > goto out; > rc = selinux_genfs_get_sid(dentry, sclass, > sbsec->flags, &sid); > - dput(dentry); > - if (rc) > + if (rc) { > + dput(dentry); > goto out; > + } > + > + if ((sbsec->flags & SE_SBGENFS_XATTR) && > + (inode->i_opflags & IOP_XATTR)) { > + rc = inode_doinit_use_xattr(inode, dentry, > + sid, &sid); > + if (rc) { > + dput(dentry); > + goto out; > + } > + } > + dput(dentry); > } > break; > } > diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h > index f68fb25b5702..6e5928f951da 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -58,6 +58,7 @@ > #define SE_SBINITIALIZED 0x0100 > #define SE_SBPROC 0x0200 > #define SE_SBGENFS 0x0400 > +#define SE_SBGENFS_XATTR 0x0800 > > #define CONTEXT_STR "context=" > #define FSCONTEXT_STR "fscontext=" >
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 81e012c66d95..0f62757b6634 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -793,11 +793,13 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (!strcmp(sb->s_type->name, "debugfs") || !strcmp(sb->s_type->name, "tracefs") || - !strcmp(sb->s_type->name, "sysfs") || - !strcmp(sb->s_type->name, "pstore") || + !strcmp(sb->s_type->name, "pstore")) + sbsec->flags |= SE_SBGENFS; + + if (!strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "cgroup") || !strcmp(sb->s_type->name, "cgroup2")) - sbsec->flags |= SE_SBGENFS; + sbsec->flags |= SE_SBGENFS | SE_SBGENFS_XATTR; if (!sbsec->behavior) { /* @@ -1392,6 +1394,67 @@ static int selinux_genfs_get_sid(struct dentry *dentry, return rc; } +static int inode_doinit_use_xattr(struct inode *inode, struct dentry *dentry, + u32 def_sid, u32 *sid) +{ +#define INITCONTEXTLEN 255 + char *context; + unsigned int len; + int rc; + + len = INITCONTEXTLEN; + context = kmalloc(len + 1, GFP_NOFS); + if (!context) + return -ENOMEM; + + context[len] = '\0'; + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); + if (rc == -ERANGE) { + kfree(context); + + /* Need a larger buffer. Query for the right size. */ + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); + if (rc < 0) + return rc; + + len = rc; + context = kmalloc(len + 1, GFP_NOFS); + if (!context) + return -ENOMEM; + + context[len] = '\0'; + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, + context, len); + } + if (rc < 0) { + kfree(context); + if (rc != -ENODATA) { + pr_warn("SELinux: %s: getxattr returned %d for dev=%s ino=%ld\n", + __func__, -rc, inode->i_sb->s_id, inode->i_ino); + return rc; + } + *sid = def_sid; + return 0; + } + + rc = security_context_to_sid_default(&selinux_state, context, rc, sid, + def_sid, GFP_NOFS); + if (rc) { + char *dev = inode->i_sb->s_id; + unsigned long ino = inode->i_ino; + + if (rc == -EINVAL) { + pr_notice_ratelimited("SELinux: inode=%lu on dev=%s was found to have an invalid context=%s. This indicates you may need to relabel the inode or the filesystem in question.\n", + ino, dev, context); + } else { + pr_warn("SELinux: %s: context_to_sid(%s) returned %d for dev=%s ino=%ld\n", + __func__, context, -rc, dev, ino); + } + } + kfree(context); + return 0; +} + /* The inode's security attributes must be initialized before first use. */ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) { @@ -1400,9 +1463,6 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent u32 task_sid, sid = 0; u16 sclass; struct dentry *dentry; -#define INITCONTEXTLEN 255 - char *context = NULL; - unsigned len = 0; int rc = 0; if (isec->initialized == LABEL_INITIALIZED) @@ -1470,72 +1530,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent goto out; } - len = INITCONTEXTLEN; - context = kmalloc(len+1, GFP_NOFS); - if (!context) { - rc = -ENOMEM; - dput(dentry); - goto out; - } - context[len] = '\0'; - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); - if (rc == -ERANGE) { - kfree(context); - - /* Need a larger buffer. Query for the right size. */ - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); - if (rc < 0) { - dput(dentry); - goto out; - } - len = rc; - context = kmalloc(len+1, GFP_NOFS); - if (!context) { - rc = -ENOMEM; - dput(dentry); - goto out; - } - context[len] = '\0'; - rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); - } + rc = inode_doinit_use_xattr(inode, dentry, sbsec->def_sid, + &sid); dput(dentry); - if (rc < 0) { - if (rc != -ENODATA) { - pr_warn("SELinux: %s: getxattr returned " - "%d for dev=%s ino=%ld\n", __func__, - -rc, inode->i_sb->s_id, inode->i_ino); - kfree(context); - goto out; - } - /* Map ENODATA to the default file SID */ - sid = sbsec->def_sid; - rc = 0; - } else { - rc = security_context_to_sid_default(&selinux_state, - context, rc, &sid, - sbsec->def_sid, - GFP_NOFS); - if (rc) { - char *dev = inode->i_sb->s_id; - unsigned long ino = inode->i_ino; - - if (rc == -EINVAL) { - if (printk_ratelimit()) - pr_notice("SELinux: inode=%lu on dev=%s was found to have an invalid " - "context=%s. This indicates you may need to relabel the inode or the " - "filesystem in question.\n", ino, dev, context); - } else { - pr_warn("SELinux: %s: context_to_sid(%s) " - "returned %d for dev=%s ino=%ld\n", - __func__, context, -rc, dev, ino); - } - kfree(context); - /* Leave with the unlabeled SID */ - rc = 0; - break; - } - } - kfree(context); + if (rc) + goto out; break; case SECURITY_FS_USE_TASK: sid = task_sid; @@ -1586,9 +1585,21 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent goto out; rc = selinux_genfs_get_sid(dentry, sclass, sbsec->flags, &sid); - dput(dentry); - if (rc) + if (rc) { + dput(dentry); goto out; + } + + if ((sbsec->flags & SE_SBGENFS_XATTR) && + (inode->i_opflags & IOP_XATTR)) { + rc = inode_doinit_use_xattr(inode, dentry, + sid, &sid); + if (rc) { + dput(dentry); + goto out; + } + } + dput(dentry); } break; } diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index f68fb25b5702..6e5928f951da 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -58,6 +58,7 @@ #define SE_SBINITIALIZED 0x0100 #define SE_SBPROC 0x0200 #define SE_SBGENFS 0x0400 +#define SE_SBGENFS_XATTR 0x0800 #define CONTEXT_STR "context=" #define FSCONTEXT_STR "fscontext="
Since kernfs supports the security xattr handlers, we can simply use these to determine the inode's context, dropping the need to update it from kernfs explicitly using a security_inode_notifysecctx() call. We achieve this by setting a new sbsec flag SE_SBGENFS_XATTR to all mounts that are known to use kernfs under the hood and then fetching the xattrs after determining the fallback genfs sid in inode_doinit_with_dentry() when this flag is set. This will allow implementing full security xattr support in kernfs and removing the ...notifysecctx() call in a subsequent patch. Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com> --- security/selinux/hooks.c | 157 +++++++++++++++------------- security/selinux/include/security.h | 1 + 2 files changed, 85 insertions(+), 73 deletions(-)