diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 1254 |
1 files changed, 670 insertions, 584 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f85597a4d73..853b58c8b2c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -156,33 +156,62 @@ static int selinux_secmark_enabled(void) return (atomic_read(&selinux_secmark_refcount) > 0); } -/* Allocate and free functions for each kind of security blob. */ - -static int task_alloc_security(struct task_struct *task) +/* + * initialise the security for the init task + */ +static void cred_init_security(void) { + struct cred *cred = (struct cred *) current->real_cred; struct task_security_struct *tsec; tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL); if (!tsec) - return -ENOMEM; + panic("SELinux: Failed to initialize initial task.\n"); - tsec->osid = tsec->sid = SECINITSID_UNLABELED; - task->security = tsec; + tsec->osid = tsec->sid = SECINITSID_KERNEL; + cred->security = tsec; +} - return 0; +/* + * get the security ID of a set of credentials + */ +static inline u32 cred_sid(const struct cred *cred) +{ + const struct task_security_struct *tsec; + + tsec = cred->security; + return tsec->sid; } -static void task_free_security(struct task_struct *task) +/* + * get the objective security ID of a task + */ +static inline u32 task_sid(const struct task_struct *task) { - struct task_security_struct *tsec = task->security; - task->security = NULL; - kfree(tsec); + u32 sid; + + rcu_read_lock(); + sid = cred_sid(__task_cred(task)); + rcu_read_unlock(); + return sid; } +/* + * get the subjective security ID of the current task + */ +static inline u32 current_sid(void) +{ + const struct task_security_struct *tsec = current_cred()->security; + + return tsec->sid; +} + +/* Allocate and free functions for each kind of security blob. */ + static int inode_alloc_security(struct inode *inode) { - struct task_security_struct *tsec = current->security; struct inode_security_struct *isec; + u32 sid = current_sid(); isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS); if (!isec) @@ -193,7 +222,7 @@ static int inode_alloc_security(struct inode *inode) isec->inode = inode; isec->sid = SECINITSID_UNLABELED; isec->sclass = SECCLASS_FILE; - isec->task_sid = tsec->sid; + isec->task_sid = sid; inode->i_security = isec; return 0; @@ -215,15 +244,15 @@ static void inode_free_security(struct inode *inode) static int file_alloc_security(struct file *file) { - struct task_security_struct *tsec = current->security; struct file_security_struct *fsec; + u32 sid = current_sid(); fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL); if (!fsec) return -ENOMEM; - fsec->sid = tsec->sid; - fsec->fown_sid = tsec->sid; + fsec->sid = sid; + fsec->fown_sid = sid; file->f_security = fsec; return 0; @@ -338,8 +367,9 @@ static const match_table_t tokens = { static int may_context_mount_sb_relabel(u32 sid, struct superblock_security_struct *sbsec, - struct task_security_struct *tsec) + const struct cred *cred) { + const struct task_security_struct *tsec = cred->security; int rc; rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, @@ -354,8 +384,9 @@ static int may_context_mount_sb_relabel(u32 sid, static int may_context_mount_inode_relabel(u32 sid, struct superblock_security_struct *sbsec, - struct task_security_struct *tsec) + const struct cred *cred) { + const struct task_security_struct *tsec = cred->security; int rc; rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, NULL); @@ -553,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag, static int selinux_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts) { + const struct cred *cred = current_cred(); int rc = 0, i; - struct task_security_struct *tsec = current->security; struct superblock_security_struct *sbsec = sb->s_security; const char *name = sb->s_type->name; struct inode *inode = sbsec->sb->s_root->d_inode; @@ -671,7 +702,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, sbsec->proc = 1; /* Determine the labeling behavior to use for this filesystem type. */ - rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid); + rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid); if (rc) { printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", __func__, sb->s_type->name, rc); @@ -680,8 +711,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, /* sets the context of the superblock for the fs being mounted. */ if (fscontext_sid) { - - rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec); + rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred); if (rc) goto out; @@ -695,12 +725,14 @@ static int selinux_set_mnt_opts(struct super_block *sb, */ if (context_sid) { if (!fscontext_sid) { - rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec); + rc = may_context_mount_sb_relabel(context_sid, sbsec, + cred); if (rc) goto out; sbsec->sid = context_sid; } else { - rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec); + rc = may_context_mount_inode_relabel(context_sid, sbsec, + cred); if (rc) goto out; } @@ -712,7 +744,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, } if (rootcontext_sid) { - rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec); + rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, + cred); if (rc) goto out; @@ -730,7 +763,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (defcontext_sid != sbsec->def_sid) { rc = may_context_mount_inode_relabel(defcontext_sid, - sbsec, tsec); + sbsec, cred); if (rc) goto out; } @@ -1345,18 +1378,53 @@ static inline u32 signal_to_av(int sig) return perm; } -/* Check permission betweeen a pair of tasks, e.g. signal checks, - fork check, ptrace check, etc. */ -static int task_has_perm(struct task_struct *tsk1, - struct task_struct *tsk2, +/* + * Check permission between a pair of credentials + * fork check, ptrace check, etc. + */ +static int cred_has_perm(const struct cred *actor, + const struct cred *target, + u32 perms) +{ + u32 asid = cred_sid(actor), tsid = cred_sid(target); + + return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL); +} + +/* + * Check permission between a pair of tasks, e.g. signal checks, + * fork check, ptrace check, etc. + * tsk1 is the actor and tsk2 is the target + * - this uses the default subjective creds of tsk1 + */ +static int task_has_perm(const struct task_struct *tsk1, + const struct task_struct *tsk2, u32 perms) { - struct task_security_struct *tsec1, *tsec2; + const struct task_security_struct *__tsec1, *__tsec2; + u32 sid1, sid2; - tsec1 = tsk1->security; - tsec2 = tsk2->security; - return avc_has_perm(tsec1->sid, tsec2->sid, - SECCLASS_PROCESS, perms, NULL); + rcu_read_lock(); + __tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid; + __tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid; + rcu_read_unlock(); + return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL); +} + +/* + * Check permission between current and another task, e.g. signal checks, + * fork check, ptrace check, etc. + * current is the actor and tsk2 is the target + * - this uses current's subjective creds + */ +static int current_has_perm(const struct task_struct *tsk, + u32 perms) +{ + u32 sid, tsid; + + sid = current_sid(); + tsid = task_sid(tsk); + return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL); } #if CAP_LAST_CAP > 63 @@ -1365,14 +1433,14 @@ static int task_has_perm(struct task_struct *tsk1, /* Check whether a task is allowed to use a capability. */ static int task_has_capability(struct task_struct *tsk, - int cap) + int cap, int audit) { - struct task_security_struct *tsec; struct avc_audit_data ad; + struct av_decision avd; u16 sclass; + u32 sid = task_sid(tsk); u32 av = CAP_TO_MASK(cap); - - tsec = tsk->security; + int rc; AVC_AUDIT_DATA_INIT(&ad, CAP); ad.tsk = tsk; @@ -1390,37 +1458,39 @@ static int task_has_capability(struct task_struct *tsk, "SELinux: out of range capability %d\n", cap); BUG(); } - return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad); + + rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); + if (audit == SECURITY_CAP_AUDIT) + avc_audit(sid, sid, sclass, av, &avd, rc, &ad); + return rc; } /* Check whether a task is allowed to use a system operation. */ static int task_has_system(struct task_struct *tsk, u32 perms) { - struct task_security_struct *tsec; + u32 sid = task_sid(tsk); - tsec = tsk->security; - - return avc_has_perm(tsec->sid, SECINITSID_KERNEL, + return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, perms, NULL); } /* Check whether a task has a particular permission to an inode. The 'adp' parameter is optional and allows other audit data to be passed (e.g. the dentry). */ -static int inode_has_perm(struct task_struct *tsk, +static int inode_has_perm(const struct cred *cred, struct inode *inode, u32 perms, struct avc_audit_data *adp) { - struct task_security_struct *tsec; struct inode_security_struct *isec; struct avc_audit_data ad; + u32 sid; if (unlikely(IS_PRIVATE(inode))) return 0; - tsec = tsk->security; + sid = cred_sid(cred); isec = inode->i_security; if (!adp) { @@ -1429,23 +1499,24 @@ static int inode_has_perm(struct task_struct *tsk, ad.u.fs.inode = inode; } - return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp); + return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp); } /* Same as inode_has_perm, but pass explicit audit data containing the dentry to help the auditing code to more easily generate the pathname if needed. */ -static inline int dentry_has_perm(struct task_struct *tsk, +static inline int dentry_has_perm(const struct cred *cred, struct vfsmount *mnt, struct dentry *dentry, u32 av) { struct inode *inode = dentry->d_inode; struct avc_audit_data ad; + AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.path.mnt = mnt; ad.u.fs.path.dentry = dentry; - return inode_has_perm(tsk, inode, av, &ad); + return inode_has_perm(cred, inode, av, &ad); } /* Check whether a task can use an open file descriptor to @@ -1456,33 +1527,35 @@ static inline int dentry_has_perm(struct task_struct *tsk, has the same SID as the process. If av is zero, then access to the file is not checked, e.g. for cases where only the descriptor is affected like seek. */ -static int file_has_perm(struct task_struct *tsk, - struct file *file, - u32 av) +static int file_has_perm(const struct cred *cred, + struct file *file, + u32 av) { - struct task_security_struct *tsec = tsk->security; struct file_security_struct *fsec = file->f_security; struct inode *inode = file->f_path.dentry->d_inode; struct avc_audit_data ad; + u32 sid = cred_sid(cred); int rc; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.path = file->f_path; - if (tsec->sid != fsec->sid) { - rc = avc_has_perm(tsec->sid, fsec->sid, + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); if (rc) - return rc; + goto out; } /* av is zero if only checking access to the descriptor. */ + rc = 0; if (av) - return inode_has_perm(tsk, inode, av, &ad); + rc = inode_has_perm(cred, inode, av, &ad); - return 0; +out: + return rc; } /* Check whether a task can create a file. */ @@ -1490,36 +1563,36 @@ static int may_create(struct inode *dir, struct dentry *dentry, u16 tclass) { - struct task_security_struct *tsec; + const struct cred *cred = current_cred(); + const struct task_security_struct *tsec = cred->security; struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; - u32 newsid; + u32 sid, newsid; struct avc_audit_data ad; int rc; - tsec = current->security; dsec = dir->i_security; sbsec = dir->i_sb->s_security; + sid = tsec->sid; + newsid = tsec->create_sid; + AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.path.dentry = dentry; - rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, + rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, &ad); if (rc) return rc; - if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { - newsid = tsec->create_sid; - } else { - rc = security_transition_sid(tsec->sid, dsec->sid, tclass, - &newsid); + if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) { + rc = security_transition_sid(sid, dsec->sid, tclass, &newsid); if (rc) return rc; } - rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad); + rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad); if (rc) return rc; @@ -1532,11 +1605,9 @@ static int may_create(struct inode *dir, static int may_create_key(u32 ksid, struct task_struct *ctx) { - struct task_security_struct *tsec; + u32 sid = task_sid(ctx); - tsec = ctx->security; - - return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL); + return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL); } #define MAY_LINK 0 @@ -1549,13 +1620,12 @@ static int may_link(struct inode *dir, int kind) { - struct task_security_struct *tsec; struct inode_security_struct *dsec, *isec; struct avc_audit_data ad; + u32 sid = current_sid(); u32 av; int rc; - tsec = current->security; dsec = dir->i_security; isec = dentry->d_inode->i_security; @@ -1564,7 +1634,7 @@ static int may_link(struct inode *dir, av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); - rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad); + rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; @@ -1584,7 +1654,7 @@ static int may_link(struct inode *dir, return 0; } - rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad); + rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad); return rc; } @@ -1593,14 +1663,13 @@ static inline int may_rename(struct inode *old_dir, struct inode *new_dir, struct dentry *new_dentry) { - struct task_security_struct *tsec; struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec; struct avc_audit_data ad; + u32 sid = current_sid(); u32 av; int old_is_dir, new_is_dir; int rc; - tsec = current->security; old_dsec = old_dir->i_security; old_isec = old_dentry->d_inode->i_security; old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); @@ -1609,16 +1678,16 @@ static inline int may_rename(struct inode *old_dir, AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.path.dentry = old_dentry; - rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR, + rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, DIR__REMOVE_NAME | DIR__SEARCH, &ad); if (rc) return rc; - rc = avc_has_perm(tsec->sid, old_isec->sid, + rc = avc_has_perm(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(tsec->sid, old_isec->sid, + rc = avc_has_perm(sid, old_isec->sid, old_isec->sclass, DIR__REPARENT, &ad); if (rc) return rc; @@ -1628,13 +1697,13 @@ static inline int may_rename(struct inode *old_dir, av = DIR__ADD_NAME | DIR__SEARCH; if (new_dentry->d_inode) av |= DIR__REMOVE_NAME; - rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad); + rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; if (new_dentry->d_inode) { new_isec = new_dentry->d_inode->i_security; new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode); - rc = avc_has_perm(tsec->sid, new_isec->sid, + rc = avc_has_perm(sid, new_isec->sid, new_isec->sclass, (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad); if (rc) @@ -1645,18 +1714,16 @@ static inline int may_rename(struct inode *old_dir, } /* Check whether a task can perform a filesystem operation. */ -static int superblock_has_perm(struct task_struct *tsk, +static int superblock_has_perm(const struct cred *cred, struct super_block *sb, u32 perms, struct avc_audit_data *ad) { - struct task_security_struct *tsec; struct superblock_security_struct *sbsec; + u32 sid = cred_sid(cred); - tsec = tsk->security; sbsec = sb->s_security; - return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - perms, ad); + return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); } /* Convert a Linux mode and permission mask to an access vector. */ @@ -1687,15 +1754,39 @@ static inline u32 file_mask_to_av(int mode, int mask) return av; } +/* Convert a Linux file to an access vector. */ +static inline u32 file_to_av(struct file *file) +{ + u32 av = 0; + + if (file->f_mode & FMODE_READ) + av |= FILE__READ; + if (file->f_mode & FMODE_WRITE) { + if (file->f_flags & O_APPEND) + av |= FILE__APPEND; + else + av |= FILE__WRITE; + } + if (!av) { + /* + * Special file opened with flags 3 for ioctl-only use. + */ + av = FILE__IOCTL; + } + + return av; +} + /* - * Convert a file mask to an access vector and include the correct open + * Convert a file to an access vector and include the correct open * open permission. */ -static inline u32 open_file_mask_to_av(int mode, int mask) +static inline u32 open_file_to_av(struct file *file) { - u32 av = file_mask_to_av(mode, mask); + u32 av = file_to_av(file); if (selinux_policycap_openperm) { + mode_t mode = file->f_path.dentry->d_inode->i_mode; /* * lnk files and socks do not really have an 'open' */ @@ -1711,34 +1802,11 @@ static inline u32 open_file_mask_to_av(int mode, int mask) av |= DIR__OPEN; else printk(KERN_ERR "SELinux: WARNING: inside %s with " - "unknown mode:%x\n", __func__, mode); + "unknown mode:%o\n", __func__, mode); } return av; } -/* Convert a Linux file to an access vector. */ -static inline u32 file_to_av(struct file *file) -{ - u32 av = 0; - - if (file->f_mode & FMODE_READ) - av |= FILE__READ; - if (file->f_mode & FMODE_WRITE) { - if (file->f_flags & O_APPEND) - av |= FILE__APPEND; - else - av |= FILE__WRITE; - } - if (!av) { - /* - * Special file opened with flags 3 for ioctl-only use. - */ - av = FILE__IOCTL; - } - - return av; -} - /* Hook functions begin here. */ static int selinux_ptrace_may_access(struct task_struct *child, @@ -1751,13 +1819,12 @@ static int selinux_ptrace_may_access(struct task_struct *child, return rc; if (mode == PTRACE_MODE_READ) { - struct task_security_struct *tsec = current->security; - struct task_security_struct *csec = child->security; - return avc_has_perm(tsec->sid, csec->sid, - SECCLASS_FILE, FILE__READ, NULL); + u32 sid = current_sid(); + u32 csid = task_sid(child); + return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); } - return task_has_perm(current, child, PROCESS__PTRACE); + return current_has_perm(child, PROCESS__PTRACE); } static int selinux_ptrace_traceme(struct task_struct *parent) @@ -1776,40 +1843,37 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, { int error; - error = task_has_perm(current, target, PROCESS__GETCAP); + error = current_has_perm(target, PROCESS__GETCAP); if (error) return error; return secondary_ops->capget(target, effective, inheritable, permitted); } -static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) +static int selinux_capset(struct cred *new, const struct cred *old, + const kernel_cap_t *effective, + const kernel_cap_t *inheritable, + const kernel_cap_t *permitted) { int error; - error = secondary_ops->capset_check(target, effective, inheritable, permitted); + error = secondary_ops->capset(new, old, + effective, inheritable, permitted); if (error) return error; - return task_has_perm(current, target, PROCESS__SETCAP); + return cred_has_perm(old, new, PROCESS__SETCAP); } -static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) -{ - secondary_ops->capset_set(target, effective, inheritable, permitted); -} - -static int selinux_capable(struct task_struct *tsk, int cap) +static int selinux_capable(struct task_struct *tsk, int cap, int audit) { int rc; - rc = secondary_ops->capable(tsk, cap); + rc = secondary_ops->capable(tsk, cap, audit); if (rc) return rc; - return task_has_capability(tsk, cap); + return task_has_capability(tsk, cap, audit); } static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) @@ -1857,15 +1921,14 @@ static int selinux_sysctl(ctl_table *table, int op) { int error = 0; u32 av; - struct task_security_struct *tsec; - u32 tsid; + u32 tsid, sid; int rc; rc = secondary_ops->sysctl(table, op); if (rc) return rc; - tsec = current->security; + sid = current_sid(); rc = selinux_sysctl_get_sid(table, (op == 0001) ? SECCLASS_DIR : SECCLASS_FILE, &tsid); @@ -1877,7 +1940,7 @@ static int selinux_sysctl(ctl_table *table, int op) /* The op values are "defined" in sysctl.c, thereby creating * a bad coupling between this module and sysctl.c */ if (op == 001) { - error = avc_has_perm(tsec->sid, tsid, + error = avc_has_perm(sid, tsid, SECCLASS_DIR, DIR__SEARCH, NULL); } else { av = 0; @@ -1886,7 +1949,7 @@ static int selinux_sysctl(ctl_table *table, int op) if (op & 002) av |= FILE__WRITE; if (av) - error = avc_has_perm(tsec->sid, tsid, + error = avc_has_perm(sid, tsid, SECCLASS_FILE, av, NULL); } @@ -1895,6 +1958,7 @@ static int selinux_sysctl(ctl_table *table, int op) static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) { + const struct cred *cred = current_cred(); int rc = 0; if (!sb) @@ -1906,14 +1970,12 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) case Q_QUOTAOFF: case Q_SETINFO: case Q_SETQUOTA: - rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAMOD, - NULL); + rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL); break; case Q_GETFMT: case Q_GETINFO: case Q_GETQUOTA: - rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAGET, - NULL); + rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL); break; default: rc = 0; /* let the kernel handle invalid cmds */ @@ -1924,7 +1986,9 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) static int selinux_quota_on(struct dentry *dentry) { - return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON); + const struct cred *cred = current_cred(); + + return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON); } static int selinux_syslog(int type) @@ -1972,16 +2036,8 @@ static int selinux_syslog(int type) static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) { int rc, cap_sys_admin = 0; - struct task_security_struct *tsec = current->security; - - rc = secondary_ops->capable(current, CAP_SYS_ADMIN); - if (rc == 0) - rc = avc_has_perm_noaudit(tsec->sid, tsec->sid, - SECCLASS_CAPABILITY, - CAP_TO_MASK(CAP_SYS_ADMIN), - 0, - NULL); + rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT); if (rc == 0) cap_sys_admin = 1; @@ -1990,59 +2046,45 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) /* binprm security operations */ -static int selinux_bprm_alloc_security(struct linux_binprm *bprm) -{ - struct bprm_security_struct *bsec; - - bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL); - if (!bsec) - return -ENOMEM; - - bsec->sid = SECINITSID_UNLABELED; - bsec->set = 0; - - bprm->security = bsec; - return 0; -} - -static int selinux_bprm_set_security(struct linux_binprm *bprm) +static int selinux_bprm_set_creds(struct linux_binprm *bprm) { - struct task_security_struct *tsec; - struct inode *inode = bprm->file->f_path.dentry->d_inode; + const struct task_security_struct *old_tsec; + struct task_security_struct *new_tsec; struct inode_security_struct *isec; - struct bprm_security_struct *bsec; - u32 newsid; struct avc_audit_data ad; + struct inode *inode = bprm->file->f_path.dentry->d_inode; int rc; - rc = secondary_ops->bprm_set_security(bprm); + rc = secondary_ops->bprm_set_creds(bprm); if (rc) return rc; - bsec = bprm->security; - - if (bsec->set) + /* SELinux context only depends on initial program or script and not + * the script interpreter */ + if (bprm->cred_prepared) return 0; - tsec = current->security; + old_tsec = current_security(); + new_tsec = bprm->cred->security; isec = inode->i_security; /* Default to the current task SID. */ - bsec->sid = tsec->sid; + new_tsec->sid = old_tsec->sid; + new_tsec->osid = old_tsec->sid; /* Reset fs, key, and sock SIDs on execve. */ - tsec->create_sid = 0; - tsec->keycreate_sid = 0; - tsec->sockcreate_sid = 0; + new_tsec->create_sid = 0; + new_tsec->keycreate_sid = 0; + new_tsec->sockcreate_sid = 0; - if (tsec->exec_sid) { - newsid = tsec->exec_sid; + if (old_tsec->exec_sid) { + new_tsec->sid = old_tsec->exec_sid; /* Reset exec SID on execve. */ - tsec->exec_sid = 0; + new_tsec->exec_sid = 0; } else { /* Check for a default transition on this program. */ - rc = security_transition_sid(tsec->sid, isec->sid, - SECCLASS_PROCESS, &newsid); + rc = security_transition_sid(old_tsec->sid, isec->sid, + SECCLASS_PROCESS, &new_tsec->sid); if (rc) return rc; } @@ -2051,33 +2093,63 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm) ad.u.fs.path = bprm->file->f_path; if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) - newsid = tsec->sid; + new_tsec->sid = old_tsec->sid; - if (tsec->sid == newsid) { - rc = avc_has_perm(tsec->sid, isec->sid, + if (new_tsec->sid == old_tsec->sid) { + rc = avc_has_perm(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(tsec->sid, newsid, + rc = avc_has_perm(old_tsec->sid, new_tsec->sid, SECCLASS_PROCESS, PROCESS__TRANSITION, &ad); if (rc) return rc; - rc = avc_has_perm(newsid, isec->sid, + rc = avc_has_perm(new_tsec->sid, isec->sid, SECCLASS_FILE, FILE__ENTRYPOINT, &ad); if (rc) return rc; - /* Clear any possibly unsafe personality bits on exec: */ - current->personality &= ~PER_CLEAR_ON_SETID; + /* Check for shared state */ + if (bprm->unsafe & LSM_UNSAFE_SHARE) { + rc = avc_has_perm(old_tsec->sid, new_tsec->sid, + SECCLASS_PROCESS, PROCESS__SHARE, + NULL); + if (rc) + return -EPERM; + } + + /* Make sure that anyone attempting to ptrace over a task that + * changes its SID has the appropriate permit */ + if (bprm->unsafe & + (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { + struct task_struct *tracer; + struct task_security_struct *sec; + u32 ptsid = 0; - /* Set the security field to the new SID. */ - bsec->sid = newsid; + rcu_read_lock(); + tracer = tracehook_tracer_task(current); + if (likely(tracer != NULL)) { + sec = __task_cred(tracer)->security; + ptsid = sec->sid; + } + rcu_read_unlock(); + + if (ptsid != 0) { + rc = avc_has_perm(ptsid, new_tsec->sid, + SECCLASS_PROCESS, + PROCESS__PTRACE, NULL); + if (rc) + return -EPERM; + } + } + + /* Clear any possibly unsafe personality bits on exec: */ + bprm->per_clear |= PER_CLEAR_ON_SETID; } - bsec->set = 1; return 0; } @@ -2086,35 +2158,34 @@ static int selinux_bprm_check_security(struct linux_binprm *bprm) return secondary_ops->bprm_check_security(bprm); } - static int selinux_bprm_secureexec(struct linux_binprm *bprm) { - struct task_security_struct *tsec = current->security; + const struct cred *cred = current_cred(); + const struct task_security_struct *tsec = cred->security; + u32 sid, osid; int atsecure = 0; - if (tsec->osid != tsec->sid) { + sid = tsec->sid; + osid = tsec->osid; + + if (osid != sid) { /* Enable secure mode for SIDs transitions unless the noatsecure permission is granted between the two SIDs, i.e. ahp returns 0. */ - atsecure = avc_has_perm(tsec->osid, tsec->sid, - SECCLASS_PROCESS, - PROCESS__NOATSECURE, NULL); + atsecure = avc_has_perm(osid, sid, + SECCLASS_PROCESS, + PROCESS__NOATSECURE, NULL); } return (atsecure || secondary_ops->bprm_secureexec(bprm)); } -static void selinux_bprm_free_security(struct linux_binprm *bprm) -{ - kfree(bprm->security); - bprm->security = NULL; -} - extern struct vfsmount *selinuxfs_mount; extern struct dentry *selinux_null; /* Derived from fs/exec.c:flush_old_files. */ -static inline void flush_unauthorized_files(struct files_struct *files) +static inline void flush_unauthorized_files(const struct cred *cred, + struct files_struct *files) { struct avc_audit_data ad; struct file *file, *devnull = NULL; @@ -2136,7 +2207,7 @@ static inline void flush_unauthorized_files(struct files_struct *files) interested in the inode-based check here. */ file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list); inode = file->f_path.dentry->d_inode; - if (inode_has_perm(current, inode, + if (inode_has_perm(cred, inode, FILE__READ | FILE__WRITE, NULL)) { drop_tty = 1; } @@ -2171,7 +2242,7 @@ static inline void flush_unauthorized_files(struct files_struct *files) file = fget(i); if (!file) continue; - if (file_has_perm(current, + if (file_has_perm(cred, file, file_to_av(file))) { sys_close(i); @@ -2185,7 +2256,10 @@ static inline void flush_unauthorized_files(struct files_struct *files) if (devnull) { get_file(devnull); } else { - devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); + devnull = dentry_open( + dget(selinux_null), + mntget(selinuxfs_mount), + O_RDWR, cred); if (IS_ERR(devnull)) { devnull = NULL; put_unused_fd(fd); @@ -2204,94 +2278,78 @@ static inline void flush_unauthorized_files(struct files_struct *files) spin_unlock(&files->file_lock); } -static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) +/* + * Prepare a process for imminent new credential changes due to exec + */ +static void selinux_bprm_committing_creds(struct linux_binprm *bprm) { - struct task_security_struct *tsec; - struct bprm_security_struct *bsec; - u32 sid; - int rc; - - secondary_ops->bprm_apply_creds(bprm, unsafe); - - tsec = current->security; + struct task_security_struct *new_tsec; + struct rlimit *rlim, *initrlim; + int rc, i; - bsec = bprm->security; - sid = bsec->sid; + secondary_ops->bprm_committing_creds(bprm); - tsec->osid = tsec->sid; - bsec->unsafe = 0; - if (tsec->sid != sid) { - /* Check for shared state. If not ok, leave SID - unchanged and kill. */ - if (unsafe & LSM_UNSAFE_SHARE) { - rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, - PROCESS__SHARE, NULL); - if (rc) { - bsec->unsafe = 1; - return; - } - } + new_tsec = bprm->cred->security; + if (new_tsec->sid == new_tsec->osid) + return; - /* Check for ptracing, and update the task SID if ok. - Otherwise, leave SID unchanged and kill. */ - if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { - struct task_struct *tracer; - struct task_security_struct *sec; - u32 ptsid = 0; + /* Close files for which the new task SID is not authorized. */ + flush_unauthorized_files(bprm->cred, current->files); - rcu_read_lock(); - tracer = tracehook_tracer_task(current); - if (likely(tracer != NULL)) { - sec = tracer->security; - ptsid = sec->sid; - } - rcu_read_unlock(); + /* Always clear parent death signal on SID transitions. */ + current->pdeath_signal = 0; - if (ptsid != 0) { - rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, - PROCESS__PTRACE, NULL); - if (rc) { - bsec->unsafe = 1; - return; - } - } + /* Check whether the new SID can inherit resource limits from the old + * SID. If not, reset all soft limits to the lower of the current + * task's hard limit and the init task's soft limit. + * + * Note that the setting of hard limits (even to lower them) can be + * controlled by the setrlimit check. The inclusion of the init task's + * soft limit into the computation is to avoid resetting soft limits + * 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, + PROCESS__RLIMITINH, NULL); + if (rc) { + for (i = 0; i < RLIM_NLIMITS; i++) { + rlim = current->signal->rlim + i; + initrlim = init_task.signal->rlim + i; + rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur); } - tsec->sid = sid; + update_rlimit_cpu(rlim->rlim_cur); } } /* - * called after apply_creds without the task lock held + * Clean up the process immediately after the installation of new credentials + * due to exec */ -static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm) +static void selinux_bprm_committed_creds(struct linux_binprm *bprm) { - struct task_security_struct *tsec; - struct rlimit *rlim, *initrlim; |