From 0d422afb892e3f993cf934b76a2c2ef839c446e0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 8 May 2013 18:05:41 -0400 Subject: security: cap_inode_getsecctx returning garbage We shouldn't be returning success from this function without also filling in the return values ctx and ctxlen. Note currently this doesn't appear to cause bugs since the only inode_getsecctx caller I can find is fs/sysfs/inode.c, which only calls this if security_inode_setsecurity succeeds. Assuming security_inode_setsecurity is set to cap_inode_setsecurity whenever inode_getsecctx is set to cap_inode_getsecctx, this function can never actually called. So I noticed this only because the server labeled NFS patches add a real caller. Acked-by: Serge E. Hallyn Signed-off-by: J. Bruce Fields --- security/capability.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/capability.c') diff --git a/security/capability.c b/security/capability.c index 1728d4e375d..83efc90b344 100644 --- a/security/capability.c +++ b/security/capability.c @@ -843,7 +843,7 @@ static int cap_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) { - return 0; + return -EOPNOTSUPP; } #ifdef CONFIG_KEYS static int cap_key_alloc(struct key *key, const struct cred *cred, -- cgit v1.2.3-70-g09d2 From d47be3dfecaf20255af89a57460285c82d5271ad Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:34 -0400 Subject: Security: Add hook to calculate context based on a negative dentry. There is a time where we need to calculate a context without the inode having been created yet. To do this we take the negative dentry and calculate a context based on the process and the parent directory contexts. Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- include/linux/security.h | 27 +++++++++++++++++++++++++++ security/capability.c | 8 ++++++++ security/security.c | 10 ++++++++++ security/selinux/hooks.c | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) (limited to 'security/capability.c') diff --git a/include/linux/security.h b/include/linux/security.h index 4686491852a..c2af46264ae 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -26,6 +26,7 @@ #include #include #include +#include struct linux_binprm; struct cred; @@ -306,6 +307,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Parse a string of security data filling in the opts structure * @options string containing all mount options known by the LSM * @opts binary data structure usable by the LSM + * @dentry_init_security: + * Compute a context for a dentry as the inode is not yet available + * since NFSv4 has no label backed by an EA anyway. + * @dentry dentry to use in calculating the context. + * @mode mode used to determine resource type. + * @name name of the last path component used to create file + * @ctx pointer to place the pointer to the resulting context in. + * @ctxlen point to place the length of the resulting context. + * * * Security hooks for inode operations. * @@ -1443,6 +1453,10 @@ struct security_operations { int (*sb_clone_mnt_opts) (const struct super_block *oldsb, struct super_block *newsb); int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); + int (*dentry_init_security) (struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen); + #ifdef CONFIG_SECURITY_PATH int (*path_unlink) (struct path *dir, struct dentry *dentry); @@ -1729,6 +1743,9 @@ int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *o int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb); int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); +int security_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen); int security_inode_alloc(struct inode *inode); void security_inode_free(struct inode *inode); @@ -2035,6 +2052,16 @@ static inline int security_inode_alloc(struct inode *inode) static inline void security_inode_free(struct inode *inode) { } +static inline int security_dentry_init_security(struct dentry *dentry, + int mode, + struct qstr *name, + void **ctx, + u32 *ctxlen) +{ + return -EOPNOTSUPP; +} + + static inline int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, diff --git a/security/capability.c b/security/capability.c index 1728d4e375d..58578b4bdad 100644 --- a/security/capability.c +++ b/security/capability.c @@ -109,6 +109,13 @@ static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts) return 0; } +static int cap_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen) +{ + return 0; +} + static int cap_inode_alloc_security(struct inode *inode) { return 0; @@ -931,6 +938,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, sb_set_mnt_opts); set_to_cap_if_null(ops, sb_clone_mnt_opts); set_to_cap_if_null(ops, sb_parse_opts_str); + set_to_cap_if_null(ops, dentry_init_security); set_to_cap_if_null(ops, inode_alloc_security); set_to_cap_if_null(ops, inode_free_security); set_to_cap_if_null(ops, inode_init_security); diff --git a/security/security.c b/security/security.c index a3dce87d1ae..0fe2b2ee954 100644 --- a/security/security.c +++ b/security/security.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -324,6 +325,15 @@ void security_inode_free(struct inode *inode) security_ops->inode_free_security(inode); } +int security_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen) +{ + return security_ops->dentry_init_security(dentry, mode, name, + ctx, ctxlen); +} +EXPORT_SYMBOL(security_dentry_init_security); + int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const initxattrs initxattrs, void *fs_data) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5c6f2cd2d09..b1f7bd727bd 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2515,6 +2515,40 @@ static void selinux_inode_free_security(struct inode *inode) inode_free_security(inode); } +static int selinux_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen) +{ + const struct cred *cred = current_cred(); + struct task_security_struct *tsec; + struct inode_security_struct *dsec; + struct superblock_security_struct *sbsec; + struct inode *dir = dentry->d_parent->d_inode; + u32 newsid; + int rc; + + tsec = cred->security; + dsec = dir->i_security; + sbsec = dir->i_sb->s_security; + + if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { + newsid = tsec->create_sid; + } else { + rc = security_transition_sid(tsec->sid, dsec->sid, + inode_mode_to_security_class(mode), + name, + &newsid); + if (rc) { + printk(KERN_WARNING + "%s: security_transition_sid failed, rc=%d\n", + __func__, -rc); + return rc; + } + } + + return security_sid_to_context(newsid, (char **)ctx, ctxlen); +} + static int selinux_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, char **name, void **value, size_t *len) @@ -5562,6 +5596,7 @@ static struct security_operations selinux_ops = { .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, .sb_parse_opts_str = selinux_parse_opts_str, + .dentry_init_security = selinux_dentry_init_security, .inode_alloc_security = selinux_inode_alloc_security, .inode_free_security = selinux_inode_free_security, -- cgit v1.2.3-70-g09d2 From 746df9b59c8a5f162c907796c7295d3c4c0d8995 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:35 -0400 Subject: Security: Add Hook to test if the particular xattr is part of a MAC model. The interface to request security labels from user space is the xattr interface. When requesting the security label from an NFS server it is important to make sure the requested xattr actually is a MAC label. This allows us to make sure that we get the desired semantics from the attribute instead of something else such as capabilities or a time based LSM. Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- include/linux/security.h | 14 ++++++++++++++ security/capability.c | 6 ++++++ security/security.c | 6 ++++++ security/selinux/hooks.c | 6 ++++++ security/smack/smack_lsm.c | 11 +++++++++++ 5 files changed, 43 insertions(+) (limited to 'security/capability.c') diff --git a/include/linux/security.h b/include/linux/security.h index c2af46264ae..cff3e4fc428 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1323,6 +1323,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @pages contains the number of pages. * Return 0 if permission is granted. * + * @ismaclabel: + * Check if the extended attribute specified by @name + * represents a MAC label. Returns 1 if name is a MAC + * attribute otherwise returns 0. + * @name full extended attribute name to check against + * LSM as a MAC label. + * * @secid_to_secctx: * Convert secid to security context. If secdata is NULL the length of * the result will be returned in seclen, but no secdata will be returned. @@ -1604,6 +1611,7 @@ struct security_operations { int (*getprocattr) (struct task_struct *p, char *name, char **value); int (*setprocattr) (struct task_struct *p, char *name, void *value, size_t size); + int (*ismaclabel) (const char *name); int (*secid_to_secctx) (u32 secid, char **secdata, u32 *seclen); int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid); void (*release_secctx) (char *secdata, u32 seclen); @@ -1857,6 +1865,7 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode); int security_getprocattr(struct task_struct *p, char *name, char **value); int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); +int security_ismaclabel(const char *name); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); void security_release_secctx(char *secdata, u32 seclen); @@ -2547,6 +2556,11 @@ static inline int security_netlink_send(struct sock *sk, struct sk_buff *skb) return cap_netlink_send(sk, skb); } +static inline int security_ismaclabel(const char *name) +{ + return 0; +} + static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return -EOPNOTSUPP; diff --git a/security/capability.c b/security/capability.c index 58578b4bdad..71f9682bfb5 100644 --- a/security/capability.c +++ b/security/capability.c @@ -823,6 +823,11 @@ static int cap_setprocattr(struct task_struct *p, char *name, void *value, return -EINVAL; } +static int cap_ismaclabel(const char *name) +{ + return 0; +} + static int cap_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return -EOPNOTSUPP; @@ -1042,6 +1047,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, d_instantiate); set_to_cap_if_null(ops, getprocattr); set_to_cap_if_null(ops, setprocattr); + set_to_cap_if_null(ops, ismaclabel); set_to_cap_if_null(ops, secid_to_secctx); set_to_cap_if_null(ops, secctx_to_secid); set_to_cap_if_null(ops, release_secctx); diff --git a/security/security.c b/security/security.c index 0fe2b2ee954..c3ceb754e70 100644 --- a/security/security.c +++ b/security/security.c @@ -1057,6 +1057,12 @@ int security_netlink_send(struct sock *sk, struct sk_buff *skb) return security_ops->netlink_send(sk, skb); } +int security_ismaclabel(const char *name) +{ + return security_ops->ismaclabel(name); +} +EXPORT_SYMBOL(security_ismaclabel); + int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return security_ops->secid_to_secctx(secid, secdata, seclen); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b1f7bd727bd..bbf219a494d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5454,6 +5454,11 @@ abort_change: return error; } +static int selinux_ismaclabel(const char *name) +{ + return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0); +} + static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return security_sid_to_context(secid, secdata, seclen); @@ -5692,6 +5697,7 @@ static struct security_operations selinux_ops = { .getprocattr = selinux_getprocattr, .setprocattr = selinux_setprocattr, + .ismaclabel = selinux_ismaclabel, .secid_to_secctx = selinux_secid_to_secctx, .secctx_to_secid = selinux_secctx_to_secid, .release_secctx = selinux_release_secctx, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index d52c780bdb7..a7f485bb4e2 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3328,6 +3328,16 @@ static void smack_audit_rule_free(void *vrule) #endif /* CONFIG_AUDIT */ +/** + * smack_ismaclabel - check if xattr @name references a smack MAC label + * @name: Full xattr name to check. + */ +static int smack_ismaclabel(const char *name) +{ + return (strcmp(name, XATTR_SMACK_SUFFIX) == 0); +} + + /** * smack_secid_to_secctx - return the smack label for a secid * @secid: incoming integer @@ -3524,6 +3534,7 @@ struct security_operations smack_ops = { .audit_rule_free = smack_audit_rule_free, #endif /* CONFIG_AUDIT */ + .ismaclabel = smack_ismaclabel, .secid_to_secctx = smack_secid_to_secctx, .secctx_to_secid = smack_secctx_to_secid, .release_secctx = smack_release_secctx, -- cgit v1.2.3-70-g09d2 From 649f6e7718891fe7691e5084ce3fa623acba3129 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:36 -0400 Subject: LSM: Add flags field to security_sb_set_mnt_opts for in kernel mount data. There is no way to differentiate if a text mount option is passed from user space or the kernel. A flags field is being added to the security_sb_set_mnt_opts hook to allow for in kernel security flags to be sent to the LSM for processing in addition to the text options received from mount. This patch also updated existing code to fix compilation errors. Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: David P. Quigley Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 3 ++- include/linux/security.h | 13 ++++++++++--- security/capability.c | 5 ++++- security/security.c | 7 +++++-- security/selinux/hooks.c | 12 ++++++++++-- 5 files changed, 31 insertions(+), 9 deletions(-) (limited to 'security/capability.c') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a366107a733..c1bbb53d444 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2411,7 +2411,8 @@ static int nfs_bdi_register(struct nfs_server *server) int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot, struct nfs_mount_info *mount_info) { - return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts); + return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts, + 0, NULL); } EXPORT_SYMBOL_GPL(nfs_set_sb_security); diff --git a/include/linux/security.h b/include/linux/security.h index cff3e4fc428..aa656fbc430 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1456,7 +1456,9 @@ struct security_operations { int (*sb_pivotroot) (struct path *old_path, struct path *new_path); int (*sb_set_mnt_opts) (struct super_block *sb, - struct security_mnt_opts *opts); + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags); int (*sb_clone_mnt_opts) (const struct super_block *oldsb, struct super_block *newsb); int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); @@ -1747,7 +1749,10 @@ int security_sb_mount(const char *dev_name, struct path *path, const char *type, unsigned long flags, void *data); int security_sb_umount(struct vfsmount *mnt, int flags); int security_sb_pivotroot(struct path *old_path, struct path *new_path); -int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts); +int security_sb_set_mnt_opts(struct super_block *sb, + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags); int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb); int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); @@ -2037,7 +2042,9 @@ static inline int security_sb_pivotroot(struct path *old_path, } static inline int security_sb_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) { return 0; } diff --git a/security/capability.c b/security/capability.c index 71f9682bfb5..d32e16e3c6a 100644 --- a/security/capability.c +++ b/security/capability.c @@ -91,7 +91,10 @@ static int cap_sb_pivotroot(struct path *old_path, struct path *new_path) } static int cap_sb_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) + { if (unlikely(opts->num_mnt_opts)) return -EOPNOTSUPP; diff --git a/security/security.c b/security/security.c index c3ceb754e70..8d0b9a79611 100644 --- a/security/security.c +++ b/security/security.c @@ -294,9 +294,12 @@ int security_sb_pivotroot(struct path *old_path, struct path *new_path) } int security_sb_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) { - return security_ops->sb_set_mnt_opts(sb, opts); + return security_ops->sb_set_mnt_opts(sb, opts, kern_flags, + set_kern_flags); } EXPORT_SYMBOL(security_sb_set_mnt_opts); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bbf219a494d..f3b54466a03 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -552,7 +552,9 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag, * labeling information. */ static int selinux_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) { const struct cred *cred = current_cred(); int rc = 0, i; @@ -580,6 +582,12 @@ static int selinux_set_mnt_opts(struct super_block *sb, "before the security server is initialized\n"); goto out; } + if (kern_flags && !set_kern_flags) { + /* Specifying internal flags without providing a place to + * place the results is not allowed */ + rc = -EINVAL; + goto out; + } /* * Binary mount data FS will come through this function twice. Once @@ -980,7 +988,7 @@ static int superblock_doinit(struct super_block *sb, void *data) goto out_err; out: - rc = selinux_set_mnt_opts(sb, &opts); + rc = selinux_set_mnt_opts(sb, &opts, 0, NULL); out_err: security_free_mnt_opts(&opts); -- cgit v1.2.3-70-g09d2