diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 938 |
1 files changed, 704 insertions, 234 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 925e546b5a87..4cafe6a19167 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -67,6 +67,8 @@ #include <linux/tcp.h> #include <linux/udp.h> #include <linux/dccp.h> +#include <linux/sctp.h> +#include <net/sctp/structs.h> #include <linux/quota.h> #include <linux/un.h> /* for Unix socket types */ #include <net/af_unix.h> /* for Unix socket types */ @@ -98,20 +100,24 @@ #include "audit.h" #include "avc_ss.h" +struct selinux_state selinux_state; + /* SECMARK reference count */ static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); #ifdef CONFIG_SECURITY_SELINUX_DEVELOP -int selinux_enforcing; +static int selinux_enforcing_boot; static int __init enforcing_setup(char *str) { unsigned long enforcing; if (!kstrtoul(str, 0, &enforcing)) - selinux_enforcing = enforcing ? 1 : 0; + selinux_enforcing_boot = enforcing ? 1 : 0; return 1; } __setup("enforcing=", enforcing_setup); +#else +#define selinux_enforcing_boot 1 #endif #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM @@ -129,6 +135,19 @@ __setup("selinux=", selinux_enabled_setup); int selinux_enabled = 1; #endif +static unsigned int selinux_checkreqprot_boot = + CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; + +static int __init checkreqprot_setup(char *str) +{ + unsigned long checkreqprot; + + if (!kstrtoul(str, 0, &checkreqprot)) + selinux_checkreqprot_boot = checkreqprot ? 1 : 0; + return 1; +} +__setup("checkreqprot=", checkreqprot_setup); + static struct kmem_cache *sel_inode_cache; static struct kmem_cache *file_security_cache; @@ -145,7 +164,8 @@ static struct kmem_cache *file_security_cache; */ static int selinux_secmark_enabled(void) { - return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount)); + return (selinux_policycap_alwaysnetwork() || + atomic_read(&selinux_secmark_refcount)); } /** @@ -160,7 +180,8 @@ static int selinux_secmark_enabled(void) */ static int selinux_peerlbl_enabled(void) { - return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled()); + return (selinux_policycap_alwaysnetwork() || + netlbl_enabled() || selinux_xfrm_enabled()); } static int selinux_netcache_avc_callback(u32 event) @@ -264,7 +285,8 @@ static int __inode_security_revalidate(struct inode *inode, might_sleep_if(may_sleep); - if (ss_initialized && isec->initialized != LABEL_INITIALIZED) { + if (selinux_state.initialized && + isec->initialized != LABEL_INITIALIZED) { if (!may_sleep) return -ECHILD; @@ -446,12 +468,14 @@ static int may_context_mount_sb_relabel(u32 sid, const struct task_security_struct *tsec = cred->security; int rc; - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, NULL); if (rc) return rc; - rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + tsec->sid, sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, NULL); return rc; } @@ -462,12 +486,14 @@ static int may_context_mount_inode_relabel(u32 sid, { const struct task_security_struct *tsec = cred->security; int rc; - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, NULL); if (rc) return rc; - rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, + rc = avc_has_perm(&selinux_state, + sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, NULL); return rc; } @@ -486,7 +512,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) !strcmp(sb->s_type->name, "debugfs") || !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "rootfs") || - (selinux_policycap_cgroupseclabel && + (selinux_policycap_cgroupseclabel() && (!strcmp(sb->s_type->name, "cgroup") || !strcmp(sb->s_type->name, "cgroup2"))); } @@ -586,7 +612,7 @@ static int selinux_get_mnt_opts(const struct super_block *sb, if (!(sbsec->flags & SE_SBINITIALIZED)) return -EINVAL; - if (!ss_initialized) + if (!selinux_state.initialized) return -EINVAL; /* make sure we always check enough bits to cover the mask */ @@ -617,21 +643,25 @@ static int selinux_get_mnt_opts(const struct super_block *sb, i = 0; if (sbsec->flags & FSCONTEXT_MNT) { - rc = security_sid_to_context(sbsec->sid, &context, &len); + rc = security_sid_to_context(&selinux_state, sbsec->sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; opts->mnt_opts_flags[i++] = FSCONTEXT_MNT; } if (sbsec->flags & CONTEXT_MNT) { - rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len); + rc = security_sid_to_context(&selinux_state, + sbsec->mntpoint_sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; opts->mnt_opts_flags[i++] = CONTEXT_MNT; } if (sbsec->flags & DEFCONTEXT_MNT) { - rc = security_sid_to_context(sbsec->def_sid, &context, &len); + rc = security_sid_to_context(&selinux_state, sbsec->def_sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; @@ -641,7 +671,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb, struct dentry *root = sbsec->sb->s_root; struct inode_security_struct *isec = backing_inode_security(root); - rc = security_sid_to_context(isec->sid, &context, &len); + rc = security_sid_to_context(&selinux_state, isec->sid, + &context, &len); if (rc) goto out_free; opts->mnt_opts[i] = context; @@ -704,7 +735,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, mutex_lock(&sbsec->lock); - if (!ss_initialized) { + if (!selinux_state.initialized) { if (!num_opts) { /* Defer initialization until selinux_complete_init, after the initial policy is loaded and the security @@ -750,7 +781,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (flags[i] == SBLABEL_MNT) continue; - rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL); + rc = security_context_str_to_sid(&selinux_state, + mount_options[i], &sid, + GFP_KERNEL); if (rc) { printk(KERN_WARNING "SELinux: security_context_str_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -826,7 +859,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, * Determine the labeling behavior to use for this * filesystem type. */ - rc = security_fs_use(sb); + rc = security_fs_use(&selinux_state, sb); if (rc) { printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", @@ -851,7 +884,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, } if (sbsec->behavior == SECURITY_FS_USE_XATTR) { sbsec->behavior = SECURITY_FS_USE_MNTPOINT; - rc = security_transition_sid(current_sid(), current_sid(), + rc = security_transition_sid(&selinux_state, + current_sid(), + current_sid(), SECCLASS_FILE, NULL, &sbsec->mntpoint_sid); if (rc) @@ -987,7 +1022,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, * if the parent was able to be mounted it clearly had no special lsm * mount options. thus we can safely deal with this superblock later */ - if (!ss_initialized) + if (!selinux_state.initialized) return 0; /* @@ -1014,7 +1049,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, if (newsbsec->behavior == SECURITY_FS_USE_NATIVE && !(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) { - rc = security_fs_use(newsb); + rc = security_fs_use(&selinux_state, newsb); if (rc) goto out; } @@ -1297,7 +1332,7 @@ static inline int default_protocol_dgram(int protocol) static inline u16 socket_type_to_security_class(int family, int type, int protocol) { - int extsockclass = selinux_policycap_extsockclass; + int extsockclass = selinux_policycap_extsockclass(); switch (family) { case PF_UNIX: @@ -1471,7 +1506,8 @@ static int selinux_genfs_get_sid(struct dentry *dentry, path++; } } - rc = security_genfs_sid(sb->s_type->name, path, tclass, sid); + rc = security_genfs_sid(&selinux_state, sb->s_type->name, + path, tclass, sid); } free_page((unsigned long)buffer); return rc; @@ -1589,7 +1625,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent sid = sbsec->def_sid; rc = 0; } else { - rc = security_context_to_sid_default(context, rc, &sid, + rc = security_context_to_sid_default(&selinux_state, + context, rc, &sid, sbsec->def_sid, GFP_NOFS); if (rc) { @@ -1622,7 +1659,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent sid = sbsec->sid; /* Try to obtain a transition SID. */ - rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid); + rc = security_transition_sid(&selinux_state, task_sid, sid, + sclass, NULL, &sid); if (rc) goto out; break; @@ -1740,9 +1778,11 @@ static int cred_has_capability(const struct cred *cred, return -EINVAL; } - rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); + rc = avc_has_perm_noaudit(&selinux_state, + sid, sid, sclass, av, 0, &avd); if (audit == SECURITY_CAP_AUDIT) { - int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0); + int rc2 = avc_audit(&selinux_state, + sid, sid, sclass, av, &avd, rc, &ad, 0); if (rc2) return rc2; } @@ -1768,7 +1808,8 @@ static int inode_has_perm(const struct cred *cred, sid = cred_sid(cred); isec = inode->i_security; - return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp); + return avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, perms, adp); } /* Same as inode_has_perm, but pass explicit audit data containing @@ -1841,7 +1882,8 @@ static int file_has_perm(const struct cred *cred, ad.u.file = file; if (sid != fsec->sid) { - rc = avc_has_perm(sid, fsec->sid, + rc = avc_has_perm(&selinux_state, + sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); @@ -1883,7 +1925,8 @@ selinux_determine_inode_label(const struct task_security_struct *tsec, *_new_isid = tsec->create_sid; } else { const struct inode_security_struct *dsec = inode_security(dir); - return security_transition_sid(tsec->sid, dsec->sid, tclass, + return security_transition_sid(&selinux_state, tsec->sid, + dsec->sid, tclass, name, _new_isid); } @@ -1910,7 +1953,8 @@ static int may_create(struct inode *dir, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; - rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, + rc = avc_has_perm(&selinux_state, + sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, &ad); if (rc) @@ -1921,11 +1965,13 @@ static int may_create(struct inode *dir, if (rc) return rc; - rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad); + rc = avc_has_perm(&selinux_state, + sid, newsid, tclass, FILE__CREATE, &ad); if (rc) return rc; - return avc_has_perm(newsid, sbsec->sid, + return avc_has_perm(&selinux_state, + newsid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, &ad); } @@ -1954,7 +2000,8 @@ static int may_link(struct inode *dir, av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); - rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad); + rc = avc_has_perm(&selinux_state, + sid, dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; @@ -1974,7 +2021,8 @@ static int may_link(struct inode *dir, return 0; } - rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad); + rc = avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, av, &ad); return rc; } @@ -1998,16 +2046,19 @@ static inline int may_rename(struct inode *old_dir, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = old_dentry; - rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, + rc = avc_has_perm(&selinux_state, + sid, old_dsec->sid, SECCLASS_DIR, DIR__REMOVE_NAME | DIR__SEARCH, &ad); if (rc) return rc; - rc = avc_has_perm(sid, old_isec->sid, + rc = avc_has_perm(&selinux_state, + sid, old_isec->sid, old_isec->sclass, FILE__RENAME, &ad); if (rc) return rc; if (old_is_dir && new_dir != old_dir) { - rc = avc_has_perm(sid, old_isec->sid, + rc = avc_has_perm(&selinux_state, + sid, old_isec->sid, old_isec->sclass, DIR__REPARENT, &ad); if (rc) return rc; @@ -2017,13 +2068,15 @@ static inline int may_rename(struct inode *old_dir, av = DIR__ADD_NAME | DIR__SEARCH; if (d_is_positive(new_dentry)) av |= DIR__REMOVE_NAME; - rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad); + rc = avc_has_perm(&selinux_state, + sid, new_dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; if (d_is_positive(new_dentry)) { new_isec = backing_inode_security(new_dentry); new_is_dir = d_is_dir(new_dentry); - rc = avc_has_perm(sid, new_isec->sid, + rc = avc_has_perm(&selinux_state, + sid, new_isec->sid, new_isec->sclass, (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad); if (rc) @@ -2043,7 +2096,8 @@ static int superblock_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); sbsec = sb->s_security; - return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); + return avc_has_perm(&selinux_state, + sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); } /* Convert a Linux mode and permission mask to an access vector. */ @@ -2106,7 +2160,8 @@ static inline u32 open_file_to_av(struct file *file) u32 av = file_to_av(file); struct inode *inode = file_inode(file); - if (selinux_policycap_openperm && inode->i_sb->s_magic != SOCKFS_MAGIC) + if (selinux_policycap_openperm() && + inode->i_sb->s_magic != SOCKFS_MAGIC) av |= FILE__OPEN; return av; @@ -2119,7 +2174,8 @@ static int selinux_binder_set_context_mgr(struct task_struct *mgr) u32 mysid = current_sid(); u32 mgrsid = task_sid(mgr); - return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER, + return avc_has_perm(&selinux_state, + mysid, mgrsid, SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); } @@ -2132,13 +2188,15 @@ static int selinux_binder_transaction(struct task_struct *from, int rc; if (mysid != fromsid) { - rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, + rc = avc_has_perm(&selinux_state, + mysid, fromsid, SECCLASS_BINDER, BINDER__IMPERSONATE, NULL); if (rc) return rc; } - return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, + return avc_has_perm(&selinux_state, + fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, NULL); } @@ -2148,7 +2206,8 @@ static int selinux_binder_transfer_binder(struct task_struct *from, u32 fromsid = task_sid(from); u32 tosid = task_sid(to); - return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, + return avc_has_perm(&selinux_state, + fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, NULL); } @@ -2167,7 +2226,8 @@ static int selinux_binder_transfer_file(struct task_struct *from, ad.u.path = file->f_path; if (sid != fsec->sid) { - rc = avc_has_perm(sid, fsec->sid, + rc = avc_has_perm(&selinux_state, + sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); @@ -2185,7 +2245,8 @@ static int selinux_binder_transfer_file(struct task_struct *from, return 0; isec = backing_inode_security(dentry); - return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), + return avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, file_to_av(file), &ad); } @@ -2196,21 +2257,25 @@ static int selinux_ptrace_access_check(struct task_struct *child, u32 csid = task_sid(child); if (mode & PTRACE_MODE_READ) - return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); + return avc_has_perm(&selinux_state, + sid, csid, SECCLASS_FILE, FILE__READ, NULL); - return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); + return avc_has_perm(&selinux_state, + sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); } static int selinux_ptrace_traceme(struct task_struct *parent) { - return avc_has_perm(task_sid(parent), current_sid(), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + task_sid(parent), current_sid(), SECCLASS_PROCESS, PROCESS__PTRACE, NULL); } static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { - return avc_has_perm(current_sid(), task_sid(target), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(target), SECCLASS_PROCESS, PROCESS__GETCAP, NULL); } @@ -2219,7 +2284,8 @@ static int selinux_capset(struct cred *new, const struct cred *old, const kernel_cap_t *inheritable, const kernel_cap_t *permitted) { - return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + cred_sid(old), cred_sid(new), SECCLASS_PROCESS, PROCESS__SETCAP, NULL); } @@ -2279,18 +2345,21 @@ static int selinux_syslog(int type) switch (type) { case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL); case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ /* Set level of messages printed to console */ case SYSLOG_ACTION_CONSOLE_LEVEL: - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, NULL); } /* All other syslog types */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL); } @@ -2351,13 +2420,14 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, * policy allows the corresponding permission between * the old and new contexts. */ - if (selinux_policycap_nnp_nosuid_transition) { + if (selinux_policycap_nnp_nosuid_transition()) { av = 0; if (nnp) av |= PROCESS2__NNP_TRANSITION; if (nosuid) av |= PROCESS2__NOSUID_TRANSITION; - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS2, av, NULL); if (!rc) return 0; @@ -2368,7 +2438,8 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, * i.e. SIDs that are guaranteed to only be allowed a subset * of the permissions of the current SID. */ - rc = security_bounded_transition(old_tsec->sid, new_tsec->sid); + rc = security_bounded_transition(&selinux_state, old_tsec->sid, + new_tsec->sid); if (!rc) return 0; @@ -2420,8 +2491,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) return rc; } else { /* Check for a default transition on this program. */ - rc = security_transition_sid(old_tsec->sid, isec->sid, - SECCLASS_PROCESS, NULL, + rc = security_transition_sid(&selinux_state, old_tsec->sid, + isec->sid, SECCLASS_PROCESS, NULL, &new_tsec->sid); if (rc) return rc; @@ -2439,25 +2510,29 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) ad.u.file = bprm->file; if (new_tsec->sid == old_tsec->sid) { - rc = avc_has_perm(old_tsec->sid, isec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, isec->sid, SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); if (rc) return rc; } else { /* Check permissions for the transition. */ - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__TRANSITION, &ad); if (rc) return rc; - rc = avc_has_perm(new_tsec->sid, isec->sid, + rc = avc_has_perm(&selinux_state, + new_tsec->sid, isec->sid, SECCLASS_FILE, FILE__ENTRYPOINT, &ad); if (rc) return rc; /* Check for shared state */ if (bprm->unsafe & LSM_UNSAFE_SHARE) { - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__SHARE, NULL); if (rc) @@ -2469,7 +2544,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) if (bprm->unsafe & LSM_UNSAFE_PTRACE) { u32 ptsid = ptrace_parent_sid(); if (ptsid != 0) { - rc = avc_has_perm(ptsid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + ptsid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); if (rc) @@ -2483,7 +2559,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) /* Enable secure mode for SIDs transitions unless the noatsecure permission is granted between the two SIDs, i.e. ahp returns 0. */ - rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + rc = avc_has_perm(&selinux_state, + old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__NOATSECURE, NULL); bprm->secureexec |= !!rc; @@ -2575,7 +2652,8 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm) * higher than the default soft limit for cases where the default is * lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK. */ - rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__RLIMITINH, NULL); if (rc) { /* protect against do_prlimit() */ @@ -2615,7 +2693,8 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) * This must occur _after_ the task SID has been updated so that any * kill done after the flush will be checked against the new SID. */ - rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); + rc = avc_has_perm(&selinux_state, + osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); if (rc) { if (IS_ENABLED(CONFIG_POSIX_TIMERS)) { memset(&itimer, 0, sizeof itimer); @@ -2779,7 +2858,9 @@ static int selinux_sb_remount(struct super_block *sb, void *data) if (flags[i] == SBLABEL_MNT) continue; - rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL); + rc = security_context_str_to_sid(&selinux_state, + mount_options[i], &sid, + GFP_KERNEL); if (rc) { printk(KERN_WARNING "SELinux: security_context_str_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -2904,7 +2985,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, if (rc) return rc; - return security_sid_to_context(newsid, (char **)ctx, ctxlen); + return security_sid_to_context(&selinux_state, newsid, (char **)ctx, + ctxlen); } static int selinux_dentry_create_files_as(struct dentry *dentry, int mode, @@ -2958,14 +3040,15 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, isec->initialized = LABEL_INITIALIZED; } - if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT)) + if (!selinux_state.initialized || !(sbsec->flags & SBLABEL_MNT)) return -EOPNOTSUPP; if (name) *name = XATTR_SELINUX_SUFFIX; if (value && len) { - rc = security_sid_to_context_force(newsid, &context, &clen); + rc = security_sid_to_context_force(&selinux_state, newsid, + &context, &clen); if (rc) return rc; *value = context; @@ -3040,7 +3123,8 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, if (IS_ERR(isec)) return PTR_ERR(isec); - return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, + return avc_has_perm_flags(&selinux_state, + sid, isec->sid, isec->sclass, FILE__READ, &ad, rcu ? MAY_NOT_BLOCK : 0); } @@ -3056,7 +3140,8 @@ static noinline int audit_inode_permission(struct inode *inode, ad.type = LSM_AUDIT_DATA_INODE; ad.u.inode = inode; - rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, + rc = slow_avc_audit(&selinux_state, + current_sid(), isec->sid, isec->sclass, perms, audited, denied, result, &ad, flags); if (rc) return rc; @@ -3094,7 +3179,8 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (IS_ERR(isec)) return PTR_ERR(isec); - rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); + rc = avc_has_perm_noaudit(&selinux_state, + sid, isec->sid, isec->sclass, perms, 0, &avd); audited = avc_audit_required(perms, &avd, rc, from_access ? FILE__AUDIT_ACCESS : 0, &denied); @@ -3126,7 +3212,7 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) return dentry_has_perm(cred, dentry, FILE__SETATTR); - if (selinux_policycap_openperm && + if (selinux_policycap_openperm() && inode->i_sb->s_magic != SOCKFS_MAGIC && (ia_valid & ATTR_SIZE) && !(ia_valid & ATTR_FILE)) @@ -3183,12 +3269,14 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, ad.u.dentry = dentry; isec = backing_inode_security(dentry); - rc = avc_has_perm(sid, isec->sid, isec->sclass, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, FILE__RELABELFROM, &ad); if (rc) return rc; - rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL); + rc = security_context_to_sid(&selinux_state, value, size, &newsid, + GFP_KERNEL); if (rc == -EINVAL) { if (!has_cap_mac_admin(true)) { struct audit_buffer *ab; @@ -3213,22 +3301,25 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, return rc; } - rc = security_context_to_sid_force(value, size, &newsid); + rc = security_context_to_sid_force(&selinux_state, value, + size, &newsid); } if (rc) return rc; - rc = avc_has_perm(sid, newsid, isec->sclass, + rc = avc_has_perm(&selinux_state, + sid, newsid, isec->sclass, FILE__RELABELTO, &ad); if (rc) return rc; - rc = security_validate_transition(isec->sid, newsid, sid, - isec->sclass); + rc = security_validate_transition(&selinux_state, isec->sid, newsid, + sid, isec->sclass); if (rc) return rc; - return avc_has_perm(newsid, + return avc_has_perm(&selinux_state, + newsid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, @@ -3249,7 +3340,8 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, return; } - rc = security_context_to_sid_force(value, size, &newsid); + rc = security_context_to_sid_force(&selinux_state, value, size, + &newsid); if (rc) { printk(KERN_ERR "SELinux: unable to map context to SID" "for (%s, %lu), rc=%d\n", @@ -3324,10 +3416,12 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void */ isec = inode_security(inode); if (has_cap_mac_admin(false)) - error = security_sid_to_context_force(isec->sid, &context, + error = security_sid_to_context_force(&selinux_state, + isec->sid, &context, &size); else - error = security_sid_to_context(isec->sid, &context, &size); + error = security_sid_to_context(&selinux_state, isec->sid, + &context, &size); if (error) return error; error = size; @@ -3353,7 +3447,8 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, if (!value || !size) return -EACCES; - rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL); + rc = security_context_to_sid(&selinux_state, value, size, &newsid, + GFP_KERNEL); if (rc) return rc; @@ -3442,7 +3537,7 @@ static int selinux_file_permission(struct file *file, int mask) isec = inode_security(inode); if (sid == fsec->sid && fsec->isid == isec->sid && - fsec->pseqno == avc_policy_seqno()) + fsec->pseqno == avc_policy_seqno(&selinux_state)) /* No change since file_open check. */ return 0; @@ -3482,7 +3577,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, ad.u.op->path = file->f_path; if (ssid != fsec->sid) { - rc = avc_has_perm(ssid, fsec->sid, + rc = avc_has_perm(&selinux_state, + ssid, fsec->sid, SECCLASS_FD, FD__USE, &ad); @@ -3494,8 +3590,9 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, return 0; isec = inode_security(inode); - rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, - requested, driver, xperm, &ad); + rc = avc_has_extended_perms(&selinux_state, + ssid, isec->sid, isec->sclass, + requested, driver, xperm, &ad); out: return rc; } @@ -3563,7 +3660,8 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared * private file mapping that will also be writable. * This has an additional check. */ - rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM, NULL); if (rc) goto error; @@ -3593,7 +3691,8 @@ static int selinux_mmap_addr(unsigned long addr) if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { u32 sid = current_sid(); - rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, NULL); } @@ -3615,7 +3714,7 @@ static int selinux_mmap_file(struct file *file, unsigned long reqprot, return rc; } - if (selinux_checkreqprot) + if (selinux_state.checkreqprot) prot = reqprot; return file_map_prot_check(file, prot, @@ -3629,7 +3728,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, const struct cred *cred = current_cred(); u32 sid = cred_sid(cred); - if (selinux_checkreqprot) + if (selinux_state.checkreqprot) prot = reqprot; if (default_noexec && @@ -3637,13 +3736,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, int rc = 0; if (vma->vm_start >= vma->vm_mm->start_brk && vma->vm_end <= vma->vm_mm->brk) { - rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__EXECHEAP, NULL); } else if (!vma->vm_file && ((vma->vm_start <= vma->vm_mm->start_stack && vma->vm_end >= vma->vm_mm->start_stack) || vma_is_stack_for_current(vma))) { - rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + rc = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__EXECSTACK, NULL); } else if (vma->vm_file && vma->anon_vma) { /* @@ -3735,7 +3836,8 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, else perm = signal_to_av(signum); - return avc_has_perm(fsec->fown_sid, sid, + return avc_has_perm(&selinux_state, + fsec->fown_sid, sid, SECCLASS_PROCESS, perm, NULL); } @@ -3761,7 +3863,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * struct as its SID. */ fsec->isid = isec->sid; - fsec->pseqno = avc_policy_seqno(); + fsec->pseqno = avc_policy_seqno(&selinux_state); /* * Since the inode label or policy seqno may have changed * between the selinux_inode_permission check and the saving @@ -3780,7 +3882,8 @@ static int selinux_task_alloc(struct task_struct *task, { u32 sid = current_sid(); - return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); + return avc_has_perm(&selinux_state, + sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); } /* @@ -3844,6 +3947,11 @@ static void selinux_cred_transfer(struct cred *new, const struct cred *old) *tsec = *old_tsec; } +static void selinux_cred_getsecid(const struct cred *c, u32 *secid) +{ + *secid = cred_sid(c); +} + /* * set the security data for a kernel service * - all the creation contexts are set to unlabelled @@ -3854,7 +3962,8 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid) u32 sid = current_sid(); int ret; - ret = avc_has_perm(sid, secid, + ret = avc_has_perm(&selinux_state, + sid, secid, SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__USE_AS_OVERRIDE, NULL); @@ -3878,7 +3987,8 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) u32 sid = current_sid(); int ret; - ret = avc_has_perm(sid, isec->sid, + ret = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__CREATE_FILES_AS, NULL); @@ -3895,7 +4005,8 @@ static int selinux_kernel_module_request(char *kmod_name) ad.type = LSM_AUDIT_DATA_KMOD; ad.u.kmod_name = kmod_name; - return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__MODULE_REQUEST, &ad); } @@ -3909,7 +4020,8 @@ static int selinux_kernel_module_from_file(struct file *file) /* init_module */ if (file == NULL) - return avc_has_perm(sid, sid, SECCLASS_SYSTEM, + return avc_has_perm(&selinux_state, + sid, sid, SECCLASS_SYSTEM, SYSTEM__MODULE_LOAD, NULL); /* finit_module */ @@ -3919,13 +4031,15 @@ static int selinux_kernel_module_from_file(struct file *file) fsec = file->f_security; if (sid != fsec->sid) { - rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); + rc = avc_has_perm(&selinux_state, + sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); if (rc) return rc; } isec = inode_security(file_inode(file)); - return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SYSTEM, SYSTEM__MODULE_LOAD, &ad); } @@ -3947,19 +4061,22 @@ static int selinux_kernel_read_file(struct file *file, static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETPGID, NULL); } static int selinux_task_getpgid(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETPGID, NULL); } static int selinux_task_getsid(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETSESSION, NULL); } @@ -3970,19 +4087,22 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid) static int selinux_task_setnice(struct task_struct *p, int nice) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_setioprio(struct task_struct *p, int ioprio) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getioprio(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } @@ -3997,7 +4117,8 @@ static int selinux_task_prlimit(const struct cred *cred, const struct cred *tcre av |= PROCESS__SETRLIMIT; if (flags & LSM_PRLIMIT_READ) av |= PROCESS__GETRLIMIT; - return avc_has_perm(cred_sid(cred), cred_sid(tcred), + return avc_has_perm(&selinux_state, + cred_sid(cred), cred_sid(tcred), SECCLASS_PROCESS, av, NULL); } @@ -4011,7 +4132,8 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, later be used as a safe reset point for the soft limit upon context transitions. See selinux_bprm_committing_creds. */ if (old_rlim->rlim_max != new_rlim->rlim_max) - return avc_has_perm(current_sid(), task_sid(p), + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL); return 0; @@ -4019,34 +4141,41 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, static int selinux_task_setscheduler(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getscheduler(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } static int selinux_task_movememory(struct task_struct *p) { - return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, + return avc_has_perm(&selinux_state, + current_sid(), task_sid(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_kill(struct task_struct *p, struct siginfo *info, - int sig, u32 secid) + int sig, const struct cred *cred) { + u32 secid; u32 perm; if (!sig) perm = PROCESS__SIGNULL; /* null signal; existence test */ else perm = signal_to_av(sig); - if (!secid) + if (!cred) secid = current_sid(); - return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); + else + secid = cred_sid(cred); + return avc_has_perm(&selinux_state, + secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); } static void selinux_task_to_inode(struct task_struct *p, @@ -4134,6 +4263,23 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, break; } +#if IS_ENABLED(CONFIG_IP_SCTP) + case IPPROTO_SCTP: { + struct sctphdr _sctph, *sh; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + offset += ihlen; + sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph); + if (sh == NULL) + break; + + ad->u.net->sport = sh->source; + ad->u.net->dport = sh->dest; + break; + } +#endif default: break; } @@ -4207,6 +4353,19 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, break; } +#if IS_ENABLED(CONFIG_IP_SCTP) + case IPPROTO_SCTP: { + struct sctphdr _sctph, *sh; + + sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph); + if (sh == NULL) + break; + + ad->u.net->sport = sh->source; + ad->u.net->dport = sh->dest; + break; + } +#endif /* includes fragments */ default: break; @@ -4287,7 +4446,8 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) if (unlikely(err)) return -EACCES; - err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid); + err = security_net_peersid_resolve(&selinux_state, nlbl_sid, + nlbl_type, xfrm_sid, sid); if (unlikely(err)) { printk(KERN_WARNING "SELinux: failure in selinux_skb_peerlbl_sid()," @@ -4315,7 +4475,8 @@ static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) int err = 0; if (skb_sid != SECSID_NULL) - err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); + err = security_sid_mls_copy(&selinux_state, sk_sid, skb_sid, + conn_sid); else *conn_sid = sk_sid; @@ -4332,8 +4493,8 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec, return 0; } - return security_transition_sid(tsec->sid, tsec->sid, secclass, NULL, - socksid); + return security_transition_sid(&selinux_state, tsec->sid, tsec->sid, + secclass, NULL, socksid); } static int sock_has_perm(struct sock *sk, u32 perms) @@ -4349,7 +4510,8 @@ static int sock_has_perm(struct sock *sk, u32 perms) ad.u.net = &net; ad.u.net->sk = sk; - return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms, + return avc_has_perm(&selinux_state, + current_sid(), sksec->sid, sksec->sclass, perms, &ad); } @@ -4369,7 +4531,8 @@ static int selinux_socket_create(int family, int type, if (rc) return rc; - return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); + return avc_has_perm(&selinux_state, + tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); } static int selinux_socket_post_create(struct socket *sock, int family, @@ -4396,6 +4559,10 @@ static int selinux_socket_post_create(struct socket *sock, int family, sksec = sock->sk->sk_security; sksec->sclass = sclass; sksec->sid = sid; + /* Allows detection of the first association on this socket */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + sksec->sctp_assoc_state = SCTP_ASSOC_UNSET; + err = selinux_netlbl_socket_post_create(sock->sk, family); } @@ -4416,11 +4583,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; - /* - * If PF_INET or PF_INET6, check name_bind permission for the port. - * Multiple address binding for SCTP is not supported yet: we just - * check the first address now. - */ + /* If PF_INET or PF_INET6, check name_bind permission for the port. */ family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { char *addrp; @@ -4432,22 +4595,35 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in unsigned short snum; u32 sid, node_perm; - if (family == PF_INET) { - if (addrlen < sizeof(struct sockaddr_in)) { - err = -EINVAL; - goto out; - } + /* + * sctp_bindx(3) calls via selinux_sctp_bind_connect() + * that validates multiple binding addresses. Because of this + * need to check address->sa_family as it is possible to have + * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. + */ + switch (address->sa_family) { + case AF_INET: + if (addrlen < sizeof(struct sockaddr_in)) + return -EINVAL; addr4 = (struct sockaddr_in *)address; snum = ntohs(addr4->sin_port); addrp = (char *)&addr4->sin_addr.s_addr; - } else { - if (addrlen < SIN6_LEN_RFC2133) { - err = -EINVAL; - goto out; - } + break; + case AF_INET6: + if (addrlen < SIN6_LEN_RFC2133) + return -EINVAL; addr6 = (struct sockaddr_in6 *)address; snum = ntohs(addr6->sin6_port); addrp = (char *)&addr6->sin6_addr.s6_addr; + break; + default: + /* Note that SCTP services expect -EINVAL, whereas + * others expect -EAFNOSUPPORT. + */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + return -EINVAL; + else + return -EAFNOSUPPORT; } if (snum) { @@ -4465,7 +4641,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ad.u.net = &net; ad.u.net->sport = htons(snum); ad.u.net->family = family; - err = avc_has_perm(sksec->sid, sid, + err = avc_has_perm(&selinux_state, + sksec->sid, sid, sksec->sclass, SOCKET__NAME_BIND, &ad); if (err) @@ -4486,6 +4663,10 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in node_perm = DCCP_SOCKET__NODE_BIND; break; + case SECCLASS_SCTP_SOCKET: + node_perm = SCTP_SOCKET__NODE_BIND; + break; + default: node_perm = RAWIP_SOCKET__NODE_BIND; break; @@ -4500,12 +4681,13 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ad.u.net->sport = htons(snum); ad.u.net->family = family; - if (family == PF_INET) + if (address->sa_family == AF_INET) ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; else ad.u.net->v6info.saddr = addr6->sin6_addr; - err = avc_has_perm(sksec->sid, sid, + err = avc_has_perm(&selinux_state, + sksec->sid, sid, sksec->sclass, node_perm, &ad); if (err) goto out; @@ -4514,7 +4696,11 @@ out: return err; } -static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) +/* This supports connect(2) and SCTP connect services such as sctp_connectx(3) + * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.txt + */ +static int selinux_socket_connect_helper(struct socket *sock, + struct sockaddr *address, int addrlen) { struct sock *sk = sock->sk; struct sk_security_struct *sksec = sk->sk_security; @@ -4525,10 +4711,12 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, return err; /* - * If a TCP or DCCP socket, check name_connect permission for the port. + * If a TCP, DCCP or SCTP socket, check name_connect permission + * for the port. */ if (sksec->sclass == SECCLASS_TCP_SOCKET || - sksec->sclass == SECCLASS_DCCP_SOCKET) { + sksec->sclass == SECCLASS_DCCP_SOCKET || + sksec->sclass == SECCLASS_SCTP_SOCKET) { struct common_audit_data ad; struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; @@ -4536,38 +4724,75 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, unsigned short snum; u32 sid, perm; - if (sk->sk_family == PF_INET) { + /* sctp_connectx(3) calls via selinux_sctp_bind_connect() + * that validates multiple connect addresses. Because of this + * need to check address->sa_family as it is possible to have + * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. + */ + switch (address->sa_family) { + case AF_INET: addr4 = (struct sockaddr_in *)address; if (addrlen < sizeof(struct sockaddr_in)) return -EINVAL; snum = ntohs(addr4->sin_port); - } else { + break; + case AF_INET6: addr6 = (struct sockaddr_in6 *)address; if (addrlen < SIN6_LEN_RFC2133) return -EINVAL; snum = ntohs(addr6->sin6_port); + break; + default: + /* Note that SCTP services expect -EINVAL, whereas + * others expect -EAFNOSUPPORT. + */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + return -EINVAL; + else + return -EAFNOSUPPORT; } err = sel_netport_sid(sk->sk_protocol, snum, &sid); if (err) - goto out; + return err; - perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? - TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; + switch (sksec->sclass) { + case SECCLASS_TCP_SOCKET: + perm = TCP_SOCKET__NAME_CONNECT; + break; + case SECCLASS_DCCP_SOCKET: + perm = DCCP_SOCKET__NAME_CONNECT; + break; + case SECCLASS_SCTP_SOCKET: + perm = SCTP_SOCKET__NAME_CONNECT; + break; + } ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; ad.u.net->dport = htons(snum); ad.u.net->family = sk->sk_family; - err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); + err = avc_has_perm(&selinux_state, + sksec->sid, sid, sksec->sclass, perm, &ad); if (err) - goto out; + return err; } - err = selinux_netlbl_socket_connect(sk, address); + return 0; +} -out: - return err; +/* Supports connect(2), see comments in selinux_socket_connect_helper() */ +static int selinux_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) +{ + int err; + struct sock *sk = sock->sk; + + err = selinux_socket_connect_helper(sock, address, addrlen); + if (err) + return err; + + return selinux_netlbl_socket_connect(sk, address); } static int selinux_socket_listen(struct socket *sock, int backlog) @@ -4660,7 +4885,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, ad.u.net = &net; ad.u.net->sk = other; - err = avc_has_perm(sksec_sock->sid, sksec_other->sid, + err = avc_has_perm(&selinux_state, + sksec_sock->sid, sksec_other->sid, sksec_other->sclass, UNIX_STREAM_SOCKET__CONNECTTO, &ad); if (err) @@ -4668,8 +4894,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, /* server child socket */ sksec_new->peer_sid = sksec_sock->sid; - err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid, - &sksec_new->sid); + err = security_sid_mls_copy(&selinux_state, sksec_other->sid, + sksec_sock->sid, &sksec_new->sid); if (err) return err; @@ -4691,7 +4917,8 @@ static int selinux_socket_unix_may_send(struct socket *sock, ad.u.net = &net; ad.u.net->sk = other->sk; - return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, + return avc_has_perm(&selinux_state, + ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, &ad); } @@ -4706,7 +4933,8 @@ static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, err = sel_netif_sid(ns, ifindex, &if_sid); if (err) return err; - err = avc_has_perm(peer_sid, if_sid, + err = avc_has_perm(&selinux_state, + peer_sid, if_sid, SECCLASS_NETIF, NETIF__INGRESS, ad); if (err) return err; @@ -4714,7 +4942,8 @@ static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, err = sel_netnode_sid(addrp, family, &node_sid); if (err) return err; - return avc_has_perm(peer_sid, node_sid, + return avc_has_perm(&selinux_state, + peer_sid, node_sid, SECCLASS_NODE, NODE__RECVFROM, ad); } @@ -4737,7 +4966,8 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, return err; if (selinux_secmark_enabled()) { - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + err = avc_has_perm(&selinux_state, + sk_sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); if (err) return err; @@ -4774,7 +5004,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * 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_policycap_netpeer) + if (!selinux_policycap_netpeer()) return selinux_sock_rcv_skb_compat(sk, skb, family); secmark_active = selinux_secmark_enabled(); @@ -4802,7 +5032,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) selinux_netlbl_err(skb, family, err, 0); return err; } - err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, + err = avc_has_perm(&selinux_state, + sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); if (err) { selinux_netlbl_err(skb, family, err, 0); @@ -4811,7 +5042,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } if (secmark_active) { - err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, + err = avc_has_perm(&selinux_state, + sk_sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); if (err) return err; @@ -4830,12 +5062,14 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op u32 peer_sid = SECSID_NULL; if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || - sksec->sclass == SECCLASS_TCP_SOCKET) + sksec->sclass == SECCLASS_TCP_SOCKET || + sksec->sclass == SECCLASS_SCTP_SOCKET) peer_sid = sksec->peer_sid; if (peer_sid == SECSID_NULL) return -ENOPROTOOPT; - err = security_sid_to_context(peer_sid, &scontext, &scontext_len); + err = security_sid_to_context(&selinux_state, peer_sid, &scontext, + &scontext_len); if (err) return err; @@ -4943,6 +5177,172 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) sksec->sclass = isec->sclass; } +/* Called whenever SCTP receives an INIT chunk. This happens when an incoming + * connect(2), sctp_connectx(3) or sctp_sendmsg(3) (with no association + * already present). + */ +static int selinux_sctp_assoc_request(struct sctp_endpoint *ep, + struct sk_buff *skb) +{ + struct sk_security_struct *sksec = ep->base.sk->sk_security; + struct common_audit_data ad; + struct lsm_network_audit net = {0,}; + u8 peerlbl_active; + u32 peer_sid = SECINITSID_UNLABELED; + u32 conn_sid; + int err = 0; + + if (!selinux_policycap_extsockclass()) + return 0; + + peerlbl_active = selinux_peerlbl_enabled(); + + if (peerlbl_active) { + /* This will return peer_sid = SECSID_NULL if there are + * no peer labels, see security_net_peersid_resolve(). + */ + err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family, + &peer_sid); + if (err) + return err; + + if (peer_sid == SECSID_NULL) + peer_sid = SECINITSID_UNLABELED; + } + + if (sksec->sctp_assoc_state == SCTP_ASSOC_UNSET) { + sksec->sctp_assoc_state = SCTP_ASSOC_SET; + + /* Here as first association on socket. As the peer SID + * was allowed by peer recv (and the netif/node checks), + * then it is approved by policy and used as the primary + * peer SID for getpeercon(3). + */ + sksec->peer_sid = peer_sid; + } else if (sksec->peer_sid != peer_sid) { + /* Other association peer SIDs are checked to enforce + * consistency among the peer SIDs. + */ + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sk = ep->base.sk; + err = avc_has_perm(&selinux_state, + sksec->peer_sid, peer_sid, sksec->sclass, + SCTP_SOCKET__ASSOCIATION, &ad); + if (err) + return err; + } + + /* Compute the MLS component for the connection and store + * the information in ep. This will be used by SCTP TCP type + * sockets and peeled off connections as they cause a new + * socket to be generated. selinux_sctp_sk_clone() will then + * plug this into the new socket. + */ + err = selinux_conn_sid(sksec->sid, peer_sid, &conn_sid); + if (err) + return err; + + ep->secid = conn_sid; + ep->peer_secid = peer_sid; + + /* Set any NetLabel labels including CIPSO/CALIPSO options. */ + return selinux_netlbl_sctp_assoc_request(ep, skb); +} + +/* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting + * based on their @optname. + */ +static int selinux_sctp_bind_connect(struct sock *sk, int optname, + struct sockaddr *address, + int addrlen) +{ + int len, err = 0, walk_size = 0; + void *addr_buf; + struct sockaddr *addr; + struct socket *sock; + + if (!selinux_policycap_extsockclass()) + return 0; + + /* Process one or more addresses that may be IPv4 or IPv6 */ + sock = sk->sk_socket; + addr_buf = address; + + while (walk_size < addrlen) { + addr = addr_buf; + switch (addr->sa_family) { + case AF_INET: + len = sizeof(struct sockaddr_in); + break; + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; + default: + return -EAFNOSUPPORT; + } + + err = -EINVAL; + switch (optname) { + /* Bind checks */ + case SCTP_PRIMARY_ADDR: + case SCTP_SET_PEER_PRIMARY_ADDR: + case SCTP_SOCKOPT_BINDX_ADD: + err = selinux_socket_bind(sock, addr, len); + break; + /* Connect checks */ + case SCTP_SOCKOPT_CONNECTX: + case SCTP_PARAM_SET_PRIMARY: + case SCTP_PARAM_ADD_IP: + case SCTP_SENDMSG_CONNECT: + err = selinux_socket_connect_helper(sock, addr, len); + if (err) + return err; + + /* As selinux_sctp_bind_connect() is called by the + * SCTP protocol layer, the socket is already locked, + * therefore selinux_netlbl_socket_connect_locked() is + * is called here. The situations handled are: + * sctp_connectx(3), sctp_sendmsg(3), sendmsg(2), + * whenever a new IP address is added or when a new + * primary address is selected. + * Note that an SCTP connect(2) call happens before + * the SCTP protocol layer and is handled via + * selinux_socket_connect(). + */ + err = selinux_netlbl_socket_connect_locked(sk, addr); + break; + } + + if (err) + return err; + + addr_buf += len; + walk_size += len; + } + + return 0; +} + +/* Called whenever a new socket is created by accept(2) or sctp_peeloff(3). */ +static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk, + struct sock *newsk) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct sk_security_struct *newsksec = newsk->sk_security; + + /* If policy does not support SECCLASS_SCTP_SOCKET then call + * the non-sctp clone version. + */ + if (!selinux_policycap_extsockclass()) + return selinux_sk_clone_security(sk, newsk); + + newsksec->sid = ep->secid; + newsksec->peer_sid = ep->peer_secid; + newsksec->sclass = sksec->sclass; + selinux_netlbl_sctp_sk_clone(sk, newsk); +} + static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { @@ -5001,7 +5401,9 @@ static int selinux_secmark_relabel_packet(u32 sid) __tsec = current_security(); tsid = __tsec->sid; - return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, NULL); + return avc_has_perm(&selinux_state, + tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, + NULL); } static void selinux_secmark_refcount_inc(void) @@ -5049,7 +5451,8 @@ static int selinux_tun_dev_create(void) * connections unlike traditional sockets - check the TUN driver to * get a better understanding of why this socket is special */ - return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE, + return avc_has_perm(&selinux_state, + sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE, NULL); } @@ -5057,7 +5460,8 @@ static int selinux_tun_dev_attach_queue(void *security) { struct tun_security_struct *tunsec = security; - return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET, + return avc_has_perm(&selinux_state, + current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__ATTACH_QUEUE, NULL); } @@ -5085,11 +5489,13 @@ static int selinux_tun_dev_open(void *security) u32 sid = current_sid(); int err; - err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET, + err = avc_has_perm(&selinux_state, + sid, tunsec->sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__RELABELFROM, NULL); if (err) return err; - err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, + err = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__RELABELTO, NULL); if (err) return err; @@ -5120,7 +5526,8 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) sk->sk_protocol, nlh->nlmsg_type, secclass_map[sksec->sclass - 1].name, task_pid_nr(current), current->comm); - if (!selinux_enforcing || security_get_allow_unknown()) + if (!enforcing_enabled(&selinux_state) || + security_get_allow_unknown(&selinux_state)) err = 0; } @@ -5150,7 +5557,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, u8 netlbl_active; u8 peerlbl_active; - if (!selinux_policycap_netpeer) + if (!selinux_policycap_netpeer()) return NF_ACCEPT; secmark_active = selinux_secmark_enabled(); @@ -5179,7 +5586,8 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, } if (secmark_active) - if (avc_has_perm(peer_sid, skb->secmark, + if (avc_has_perm(&selinux_state, + peer_sid, skb->secmark, SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) return NF_DROP; @@ -5291,7 +5699,8 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_DROP; if (selinux_secmark_enabled()) - if (avc_has_perm(sksec->sid, skb->secmark, + if (avc_has_perm(&selinux_state, + sksec->sid, skb->secmark, SECCLASS_PACKET, PACKET__SEND, &ad)) return NF_DROP_ERR(-ECONNREFUSED); @@ -5319,7 +5728,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, * 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_policycap_netpeer) + if (!selinux_policycap_netpeer()) return selinux_ip_postroute_compat(skb, ifindex, family); secmark_active = selinux_secmark_enabled(); @@ -5414,7 +5823,8 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, return NF_DROP; if (secmark_active) - if (avc_has_perm(peer_sid, skb->secmark, + if (avc_has_perm(&selinux_state, + peer_sid, skb->secmark, SECCLASS_PACKET, secmark_perm, &ad)) return NF_DROP_ERR(-ECONNREFUSED); @@ -5424,13 +5834,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid)) return NF_DROP; - if (avc_has_perm(peer_sid, if_sid, + if (avc_has_perm(&selinux_state, + peer_sid, if_sid, SECCLASS_NETIF, NETIF__EGRESS, &ad)) return NF_DROP_ERR(-ECONNREFUSED); if (sel_netnode_sid(addrp, family, &node_sid)) return NF_DROP; - if (avc_has_perm(peer_sid, node_sid, + if (avc_has_perm(&selinux_state, + peer_sid, node_sid, SECCLASS_NODE, NODE__SENDTO, &ad)) return NF_DROP_ERR(-ECONNREFUSED); } @@ -5518,7 +5930,8 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = ipc_perms->key; - return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); + return avc_has_perm(&selinux_state, + sid, isec->sid, isec->sclass, perms, &ad); } static int selinux_msg_msg_alloc_security(struct msg_msg *msg) @@ -5548,7 +5961,8 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; - rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__CREATE, &ad); if (rc) { ipc_free_security(msq); @@ -5573,7 +5987,8 @@ static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; - return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__ASSOCIATE, &ad); } @@ -5586,10 +6001,12 @@ static int selinux_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd) case IPC_INFO: case MSG_INFO: /* No specific object, just general system-wide information. */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case MSG_STAT: + case MSG_STAT_ANY: perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; break; case IPC_SET: @@ -5625,8 +6042,8 @@ static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *m * Compute new sid based on current process and * message queue this message will be stored in */ - rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG, - NULL, &msec->sid); + rc = security_transition_sid(&selinux_state, sid, isec->sid, + SECCLASS_MSG, NULL, &msec->sid); if (rc) return rc; } @@ -5635,15 +6052,18 @@ static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *m ad.u.ipc_id = msq->key; /* Can this process write to the queue? */ - rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__WRITE, &ad); if (!rc) /* Can this process send the message */ - rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG, + rc = avc_has_perm(&selinux_state, + sid, msec->sid, SECCLASS_MSG, MSG__SEND, &ad); if (!rc) /* Can the message be put in the queue? */ - rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ, + rc = avc_has_perm(&selinux_state, + msec->sid, isec->sid, SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad); return rc; @@ -5665,10 +6085,12 @@ static int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *m ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; - rc = avc_has_perm(sid, isec->sid, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_MSGQ, MSGQ__READ, &ad); if (!rc) - rc = avc_has_perm(sid, msec->sid, + rc = avc_has_perm(&selinux_state, + sid, msec->sid, SECCLASS_MSG, MSG__RECEIVE, &ad); return rc; } @@ -5690,7 +6112,8 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->key; - rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SHM, SHM__CREATE, &ad); if (rc) { ipc_free_security(shp); @@ -5715,7 +6138,8 @@ static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->key; - return avc_has_perm(sid, isec->sid, SECCLASS_SHM, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SHM, SHM__ASSOCIATE, &ad); } @@ -5729,10 +6153,12 @@ static int selinux_shm_shmctl(struct kern_ipc_perm *shp, int cmd) case IPC_INFO: case SHM_INFO: /* No specific object, just general system-wide information. */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case IPC_STAT: case SHM_STAT: + case SHM_STAT_ANY: perms = SHM__GETATTR | SHM__ASSOCIATE; break; case IPC_SET: @@ -5783,7 +6209,8 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->key; - rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM, + rc = avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SEM, SEM__CREATE, &ad); if (rc) { ipc_free_security(sma); @@ -5808,7 +6235,8 @@ static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg) ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->key; - return avc_has_perm(sid, isec->sid, SECCLASS_SEM, + return avc_has_perm(&selinux_state, + sid, isec->sid, SECCLASS_SEM, SEM__ASSOCIATE, &ad); } @@ -5822,7 +6250,8 @@ static int selinux_sem_semctl(struct kern_ipc_perm *sma, int cmd) case IPC_INFO: case SEM_INFO: /* No specific object, just general system-wide information. */ - return avc_has_perm(current_sid(), SECINITSID_KERNEL, + return avc_has_perm(&selinux_state, + current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); case GETPID: case GETNCNT: @@ -5845,6 +6274,7 @@ static int selinux_sem_semctl(struct kern_ipc_perm *sma, int cmd) break; case IPC_STAT: case SEM_STAT: + case SEM_STAT_ANY: perms = SEM__GETATTR | SEM__ASSOCIATE; break; default: @@ -5908,7 +6338,8 @@ static int selinux_getprocattr(struct task_struct *p, __tsec = __task_cred(p)->security; if (current != p) { - error = avc_has_perm(current_sid(), __tsec->sid, + error = avc_has_perm(&selinux_state, + current_sid(), __tsec->sid, SECCLASS_PROCESS, PROCESS__GETATTR, NULL); if (error) goto bad; @@ -5935,7 +6366,7 @@ static int selinux_getprocattr(struct task_struct *p, if (!sid) return 0; - error = security_sid_to_context(sid, value, &len); + error = security_sid_to_context(&selinux_state, sid, value, &len); if (error) return error; return len; @@ -5957,19 +6388,24 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) * Basic control over ability to set these attributes at all. */ if (!strcmp(name, "exec")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETEXEC, NULL); else if (!strcmp(name, "fscreate")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETFSCREATE, NULL); else if (!strcmp(name, "keycreate")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETKEYCREATE, NULL); else if (!strcmp(name, "sockcreate")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETSOCKCREATE, NULL); else if (!strcmp(name, "current")) - error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + mysid, mysid, SECCLASS_PROCESS, PROCESS__SETCURRENT, NULL); else error = -EINVAL; @@ -5982,7 +6418,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) str[size-1] = 0; size--; } - error = security_context_to_sid(value, size, &sid, GFP_KERNEL); + error = security_context_to_sid(&selinux_state, value, size, + &sid, GFP_KERNEL); if (error == -EINVAL && !strcmp(name, "fscreate")) { if (!has_cap_mac_admin(true)) { struct audit_buffer *ab; @@ -6001,8 +6438,9 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) return error; } - error = security_context_to_sid_force(value, size, - &sid); + error = security_context_to_sid_force( + &selinux_state, + value, size, &sid); } if (error) return error; @@ -6024,7 +6462,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) } else if (!strcmp(name, "fscreate")) { tsec->create_sid = sid; } else if (!strcmp(name, "keycreate")) { - error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE, + error = avc_has_perm(&selinux_state, + mysid, sid, SECCLASS_KEY, KEY__CREATE, NULL); if (error) goto abort_change; @@ -6039,13 +6478,15 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) /* Only allow single threaded processes to change context */ error = -EPERM; if (!current_is_single_threaded()) { - error = security_bounded_transition(tsec->sid, sid); + error = security_bounded_transition(&selinux_state, + tsec->sid, sid); if (error) goto abort_change; } /* Check permissions for the transition. */ - error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + tsec->sid, sid, SECCLASS_PROCESS, PROCESS__DYNTRANSITION, NULL); if (error) goto abort_change; @@ -6054,7 +6495,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) Otherwise, leave SID unchanged and fail. */ ptsid = ptrace_parent_sid(); if (ptsid != 0) { - error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, + error = avc_has_perm(&selinux_state, + ptsid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); if (error) goto abort_change; @@ -6081,12 +6523,14 @@ static int selinux_ismaclabel(const char *name) static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { - return security_sid_to_context(secid, secdata, seclen); + return security_sid_to_context(&selinux_state, secid, + secdata, seclen); } static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) { - return security_context_to_sid(secdata, seclen, secid, GFP_KERNEL); + return security_context_to_sid(&selinux_state, secdata, seclen, + secid, GFP_KERNEL); } static void selinux_release_secctx(char *secdata, u32 seclen) @@ -6178,7 +6622,8 @@ static int selinux_key_permission(key_ref_t key_ref, key = key_ref_to_ptr(key_ref); ksec = key->security; - return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL); + return avc_has_perm(&selinux_state, + sid, ksec->sid, SECCLASS_KEY, perm, NULL); } static int selinux_key_getsecurity(struct key *key, char **_buffer) @@ -6188,7 +6633,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) unsigned len; int rc; - rc = security_sid_to_context(ksec->sid, &context, &len); + rc = security_sid_to_context(&selinux_state, ksec->sid, + &context, &len); if (!rc) rc = len; *_buffer = context; @@ -6213,7 +6659,8 @@ static int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val) ibpkey.subnet_prefix = subnet_prefix; ibpkey.pkey = pkey_val; ad.u.ibpkey = &ibpkey; - return avc_has_perm(sec->sid, sid, + return avc_has_perm(&selinux_state, + sec->sid, sid, SECCLASS_INFINIBAND_PKEY, INFINIBAND_PKEY__ACCESS, &ad); } @@ -6227,7 +6674,8 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, struct ib_security_struct *sec = ib_sec; struct lsm_ibendport_audit ibendport; - err = security_ib_endport_sid(dev_name, port_num, &sid); + err = security_ib_endport_sid(&selinux_state, dev_name, port_num, + &sid); if (err) return err; @@ -6236,7 +6684,8 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, strncpy(ibendport.dev_name, dev_name, sizeof(ibendport.dev_name)); ibendport.port = port_num; ad.u.ibendport = &ibendport; - return avc_has_perm(sec->sid, sid, + return avc_has_perm(&selinux_state, + sec->sid, sid, SECCLASS_INFINIBAND_ENDPORT, INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad); } @@ -6269,11 +6718,13 @@ static int selinux_bpf(int cmd, union bpf_attr *attr, switch (cmd) { case BPF_MAP_CREATE: - ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE, + ret = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL); break; case BPF_PROG_LOAD: - ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__PROG_LOAD, + ret = avc_has_perm(&selinux_state, + sid, sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL); break; default: @@ -6313,14 +6764,16 @@ static int bpf_fd_pass(struct file *file, u32 sid) if (file->f_op == &bpf_map_fops) { map = file->private_data; bpfsec = map->security; - ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + ret = avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, bpf_map_fmode_to_av(file->f_mode), NULL); if (ret) return ret; } else if (file->f_op == &bpf_prog_fops) { prog = file->private_data; bpfsec = prog->aux->security; - ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + ret = avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_RUN, NULL); if (ret) return ret; @@ -6334,7 +6787,8 @@ static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode) struct bpf_security_struct *bpfsec; bpfsec = map->security; - return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + return avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, bpf_map_fmode_to_av(fmode), NULL); } @@ -6344,7 +6798,8 @@ static int selinux_bpf_prog(struct bpf_prog *prog) struct bpf_security_struct *bpfsec; bpfsec = prog->aux->security; - return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + return avc_has_perm(&selinux_state, + sid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_RUN, NULL); } @@ -6479,6 +6934,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(cred_free, selinux_cred_free), LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare), LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer), + LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid), LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as), LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as), LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request), @@ -6563,6 +7019,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sk_clone_security, selinux_sk_clone_security), LSM_HOOK_INIT(sk_getsecid, selinux_sk_getsecid), LSM_HOOK_INIT(sock_graft, selinux_sock_graft), + LSM_HOOK_INIT(sctp_assoc_request, selinux_sctp_assoc_request), + LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone), + LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect), LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request), LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone), LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established), @@ -6638,6 +7097,12 @@ static __init int selinux_init(void) printk(KERN_INFO "SELinux: Initializing.\n"); + memset(&selinux_state, 0, sizeof(selinux_state)); + enforcing_set(&selinux_state, selinux_enforcing_boot); + selinux_state.checkreqprot = selinux_checkreqprot_boot; + selinux_ss_init(&selinux_state.ss); + selinux_avc_init(&selinux_state.avc); + /* Set the security state for the initial task. */ cred_init_security(); @@ -6651,6 +7116,12 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); + avtab_cache_init(); + + ebitmap_cache_init(); + + hashtab_cache_init(); + security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) @@ -6659,7 +7130,7 @@ static __init int selinux_init(void) if (avc_add_callback(selinux_lsm_notifier_avc_callback, AVC_CALLBACK_RESET)) panic("SELinux: Unable to register AVC LSM notifier callback\n"); - if (selinux_enforcing) + if (selinux_enforcing_boot) printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n"); else printk(KERN_DEBUG "SELinux: Starting in permissive mode\n"); @@ -6780,23 +7251,22 @@ static void selinux_nf_ip_exit(void) #endif /* CONFIG_NETFILTER */ #ifdef CONFIG_SECURITY_SELINUX_DISABLE -static int selinux_disabled; - -int selinux_disable(void) +int selinux_disable(struct selinux_state *state) { - if (ss_initialized) { + if (state->initialized) { /* Not permitted after initial policy load. */ return -EINVAL; } - if (selinux_disabled) { + if (state->disabled) { /* Only do this once. */ return -EINVAL; } + state->disabled = 1; + printk(KERN_INFO "SELinux: Disabled at runtime.\n"); - selinux_disabled = 1; selinux_enabled = 0; security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); |