*/
#include <linux/init.h>
+#include <linux/kd.h>
#include <linux/kernel.h>
#include <linux/tracehook.h>
#include <linux/errno.h>
+#include <linux/ext2_fs.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/xattr.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
+#include <linux/proc_fs.h>
#include <linux/swap.h>
#include <linux/spinlock.h>
#include <linux/syscalls.h>
#include <linux/mutex.h>
#include <linux/posix-timers.h>
#include <linux/syslog.h>
+#include <linux/user_namespace.h>
#include "avc.h"
#include "objsec.h"
continue;
default:
BUG();
+ return;
};
/* we need a comma before each option */
seq_putc(m, ',');
printk(KERN_ERR
"SELinux: out of range capability %d\n", cap);
BUG();
+ return -EINVAL;
}
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);
+ if (audit == SECURITY_CAP_AUDIT) {
+ int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
+ if (rc2)
+ return rc2;
+ }
return rc;
}
static int inode_has_perm(const struct cred *cred,
struct inode *inode,
u32 perms,
- struct common_audit_data *adp)
+ struct common_audit_data *adp,
+ unsigned flags)
{
struct inode_security_struct *isec;
struct common_audit_data ad;
if (!adp) {
adp = &ad;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.inode = inode;
+ COMMON_AUDIT_DATA_INIT(&ad, INODE);
+ ad.u.inode = inode;
}
- return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
+ return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
}
/* 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(const struct cred *cred,
- struct vfsmount *mnt,
struct dentry *dentry,
u32 av)
{
struct inode *inode = dentry->d_inode;
struct common_audit_data ad;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path.mnt = mnt;
- ad.u.fs.path.dentry = dentry;
- return inode_has_perm(cred, inode, av, &ad);
+ COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.u.dentry = dentry;
+ return inode_has_perm(cred, inode, av, &ad, 0);
+}
+
+/* Same as inode_has_perm, but pass explicit audit data containing
+ the path to help the auditing code to more easily generate the
+ pathname if needed. */
+static inline int path_has_perm(const struct cred *cred,
+ struct path *path,
+ u32 av)
+{
+ struct inode *inode = path->dentry->d_inode;
+ struct common_audit_data ad;
+
+ COMMON_AUDIT_DATA_INIT(&ad, PATH);
+ ad.u.path = *path;
+ return inode_has_perm(cred, inode, av, &ad, 0);
}
/* Check whether a task can use an open file descriptor to
u32 sid = cred_sid(cred);
int rc;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path = file->f_path;
+ COMMON_AUDIT_DATA_INIT(&ad, PATH);
+ ad.u.path = file->f_path;
if (sid != fsec->sid) {
rc = avc_has_perm(sid, fsec->sid,
/* av is zero if only checking access to the descriptor. */
rc = 0;
if (av)
- rc = inode_has_perm(cred, inode, av, &ad);
+ rc = inode_has_perm(cred, inode, av, &ad, 0);
out:
return rc;
sid = tsec->sid;
newsid = tsec->create_sid;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path.dentry = dentry;
+ COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.u.dentry = dentry;
rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
return rc;
if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
- rc = security_transition_sid(sid, dsec->sid, tclass, NULL, &newsid);
+ rc = security_transition_sid(sid, dsec->sid, tclass,
+ &dentry->d_name, &newsid);
if (rc)
return rc;
}
dsec = dir->i_security;
isec = dentry->d_inode->i_security;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path.dentry = dentry;
+ COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.u.dentry = dentry;
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
new_dsec = new_dir->i_security;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
- ad.u.fs.path.dentry = old_dentry;
+ ad.u.dentry = old_dentry;
rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
DIR__REMOVE_NAME | DIR__SEARCH, &ad);
if (rc)
return rc;
}
- ad.u.fs.path.dentry = new_dentry;
+ ad.u.dentry = new_dentry;
av = DIR__ADD_NAME | DIR__SEARCH;
if (new_dentry->d_inode)
av |= DIR__REMOVE_NAME;
*/
static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
- int cap, int audit)
+ struct user_namespace *ns, int cap, int audit)
{
int rc;
- rc = cap_capable(tsk, cred, cap, audit);
+ rc = cap_capable(tsk, cred, ns, cap, audit);
if (rc)
return rc;
{
const struct cred *cred = current_cred();
- return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
+ return dentry_has_perm(cred, dentry, FILE__QUOTAON);
}
static int selinux_syslog(int type)
{
int rc, cap_sys_admin = 0;
- rc = selinux_capable(current, current_cred(), CAP_SYS_ADMIN,
+ rc = selinux_capable(current, current_cred(),
+ &init_user_ns, CAP_SYS_ADMIN,
SECURITY_CAP_NOAUDIT);
if (rc == 0)
cap_sys_admin = 1;
return rc;
}
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path = bprm->file->f_path;
+ COMMON_AUDIT_DATA_INIT(&ad, PATH);
+ ad.u.path = bprm->file->f_path;
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
new_tsec->sid = old_tsec->sid;
file = file_priv->file;
inode = file->f_path.dentry->d_inode;
if (inode_has_perm(cred, inode,
- FILE__READ | FILE__WRITE, NULL)) {
+ FILE__READ | FILE__WRITE, NULL, 0)) {
drop_tty = 1;
}
}
/* Revalidate access to inherited open files. */
- COMMON_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, INODE);
spin_lock(&files->file_lock);
for (;;) {
return rc;
}
+static int selinux_sb_remount(struct super_block *sb, void *data)
+{
+ int rc, i, *flags;
+ struct security_mnt_opts opts;
+ char *secdata, **mount_options;
+ struct superblock_security_struct *sbsec = sb->s_security;
+
+ if (!(sbsec->flags & SE_SBINITIALIZED))
+ return 0;
+
+ if (!data)
+ return 0;
+
+ if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
+ return 0;
+
+ security_init_mnt_opts(&opts);
+ secdata = alloc_secdata();
+ if (!secdata)
+ return -ENOMEM;
+ rc = selinux_sb_copy_data(data, secdata);
+ if (rc)
+ goto out_free_secdata;
+
+ rc = selinux_parse_opts_str(secdata, &opts);
+ if (rc)
+ goto out_free_secdata;
+
+ mount_options = opts.mnt_opts;
+ flags = opts.mnt_opts_flags;
+
+ for (i = 0; i < opts.num_mnt_opts; i++) {
+ u32 sid;
+ size_t len;
+
+ if (flags[i] == SE_SBLABELSUPP)
+ continue;
+ len = strlen(mount_options[i]);
+ rc = security_context_to_sid(mount_options[i], len, &sid);
+ if (rc) {
+ printk(KERN_WARNING "SELinux: security_context_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ mount_options[i], sb->s_id, sb->s_type->name, rc);
+ goto out_free_opts;
+ }
+ rc = -EINVAL;
+ switch (flags[i]) {
+ case FSCONTEXT_MNT:
+ if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+ goto out_bad_option;
+ break;
+ case CONTEXT_MNT:
+ if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+ goto out_bad_option;
+ break;
+ case ROOTCONTEXT_MNT: {
+ struct inode_security_struct *root_isec;
+ root_isec = sb->s_root->d_inode->i_security;
+
+ if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+ goto out_bad_option;
+ break;
+ }
+ case DEFCONTEXT_MNT:
+ if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+ goto out_bad_option;
+ break;
+ default:
+ goto out_free_opts;
+ }
+ }
+
+ rc = 0;
+out_free_opts:
+ security_free_mnt_opts(&opts);
+out_free_secdata:
+ free_secdata(secdata);
+ return rc;
+out_bad_option:
+ printk(KERN_WARNING "SELinux: unable to change security options "
+ "during remount (dev %s, type=%s)\n", sb->s_id,
+ sb->s_type->name);
+ goto out_free_opts;
+}
+
static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
const struct cred *cred = current_cred();
if (flags & MS_KERNMOUNT)
return 0;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path.dentry = sb->s_root;
+ COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.u.dentry = sb->s_root;
return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
}
const struct cred *cred = current_cred();
struct common_audit_data ad;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path.dentry = dentry->d_sb->s_root;
+ COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.u.dentry = dentry->d_sb->s_root;
return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
}
return superblock_has_perm(cred, path->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
else
- return dentry_has_perm(cred, path->mnt, path->dentry,
- FILE__MOUNTON);
+ return path_has_perm(cred, path, FILE__MOUNTON);
}
static int selinux_umount(struct vfsmount *mnt, int flags)
{
const struct cred *cred = current_cred();
- return dentry_has_perm(cred, NULL, dentry, FILE__READ);
+ return dentry_has_perm(cred, dentry, FILE__READ);
}
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
{
const struct cred *cred = current_cred();
- return dentry_has_perm(cred, NULL, dentry, FILE__READ);
+ return dentry_has_perm(cred, dentry, FILE__READ);
}
-static int selinux_inode_permission(struct inode *inode, int mask)
+static int selinux_inode_permission(struct inode *inode, int mask, unsigned flags)
{
const struct cred *cred = current_cred();
struct common_audit_data ad;
if (!mask)
return 0;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.inode = inode;
+ COMMON_AUDIT_DATA_INIT(&ad, INODE);
+ ad.u.inode = inode;
if (from_access)
ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS;
perms = file_mask_to_av(inode->i_mode, mask);
- return inode_has_perm(cred, inode, perms, &ad);
+ return inode_has_perm(cred, inode, perms, &ad, flags);
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
- return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
+ return dentry_has_perm(cred, dentry, FILE__SETATTR);
- return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);
+ return dentry_has_perm(cred, dentry, FILE__WRITE);
}
static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
{
const struct cred *cred = current_cred();
+ struct path path;
+
+ path.dentry = dentry;
+ path.mnt = mnt;
- return dentry_has_perm(cred, mnt, dentry, FILE__GETATTR);
+ return path_has_perm(cred, &path, FILE__GETATTR);
}
static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
/* Not an attribute we recognize, so just check the
ordinary setattr permission. */
- return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
+ return dentry_has_perm(cred, dentry, FILE__SETATTR);
}
static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
if (!(sbsec->flags & SE_SBLABELSUPP))
return -EOPNOTSUPP;
- if (!is_owner_or_cap(inode))
+ if (!inode_owner_or_capable(inode))
return -EPERM;
- COMMON_AUDIT_DATA_INIT(&ad, FS);
- ad.u.fs.path.dentry = dentry;
+ COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.u.dentry = dentry;
rc = avc_has_perm(sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
{
const struct cred *cred = current_cred();
- return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
+ return dentry_has_perm(cred, dentry, FILE__GETATTR);
}
static int selinux_inode_listxattr(struct dentry *dentry)
{
const struct cred *cred = current_cred();
- return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
+ return dentry_has_perm(cred, dentry, FILE__GETATTR);
}
static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
* and lack of permission just means that we fall back to the
* in-core context value, not a denial.
*/
- error = selinux_capable(current, current_cred(), CAP_MAC_ADMIN,
+ error = selinux_capable(current, current_cred(),
+ &init_user_ns, CAP_MAC_ADMIN,
SECURITY_CAP_NOAUDIT);
if (!error)
error = security_sid_to_context_force(isec->sid, &context,
unsigned long arg)
{
const struct cred *cred = current_cred();
- u32 av = 0;
+ int error = 0;
- if (_IOC_DIR(cmd) & _IOC_WRITE)
- av |= FILE__WRITE;
- if (_IOC_DIR(cmd) & _IOC_READ)
- av |= FILE__READ;
- if (!av)
- av = FILE__IOCTL;
+ switch (cmd) {
+ case FIONREAD:
+ /* fall through */
+ case FIBMAP:
+ /* fall through */
+ case FIGETBSZ:
+ /* fall through */
+ case EXT2_IOC_GETFLAGS:
+ /* fall through */
+ case EXT2_IOC_GETVERSION:
+ error = file_has_perm(cred, file, FILE__GETATTR);
+ break;
- return file_has_perm(cred, file, av);
+ case EXT2_IOC_SETFLAGS:
+ /* fall through */
+ case EXT2_IOC_SETVERSION:
+ error = file_has_perm(cred, file, FILE__SETATTR);
+ break;
+
+ /* sys_ioctl() checks */
+ case FIONBIO:
+ /* fall through */
+ case FIOASYNC:
+ error = file_has_perm(cred, file, 0);
+ break;
+
+ case KDSKBENT:
+ case KDSKBSENT:
+ error = task_has_capability(current, cred, CAP_SYS_TTY_CONFIG,
+ SECURITY_CAP_AUDIT);
+ break;
+
+ /* default case assumes that the command will go
+ * to the file's ioctl() function.
+ */
+ default:
+ error = file_has_perm(cred, file, FILE__IOCTL);
+ }
+ return error;
}
static int default_noexec;
* new inode label or new policy.
* This check is not redundant - do not remove.
*/
- return inode_has_perm(cred, inode, open_file_to_av(file), NULL);
+ return inode_has_perm(cred, inode, open_file_to_av(file), NULL, 0);
}
/* task security operations */
{
struct task_security_struct *tsec = cred->security;
- BUG_ON((unsigned long) cred->security < PAGE_SIZE);
+ /*
+ * cred->security == NULL if security_cred_alloc_blank() or
+ * security_prepare_creds() returned an error.
+ */
+ BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE);
cred->security = (void *) 0x7UL;
kfree(tsec);
}
/* socket security operations */
-static u32 socket_sockcreate_sid(const struct task_security_struct *tsec)
+static int socket_sockcreate_sid(const struct task_security_struct *tsec,
+ u16 secclass, u32 *socksid)
{
- return tsec->sockcreate_sid ? : tsec->sid;
+ if (tsec->sockcreate_sid > SECSID_NULL) {
+ *socksid = tsec->sockcreate_sid;
+ return 0;
+ }
+
+ return security_transition_sid(tsec->sid, tsec->sid, secclass, NULL,
+ socksid);
}
static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
const struct task_security_struct *tsec = current_security();
u32 newsid;
u16 secclass;
+ int rc;
if (kern)
return 0;
- newsid = socket_sockcreate_sid(tsec);
secclass = socket_type_to_security_class(family, type, protocol);
+ rc = socket_sockcreate_sid(tsec, secclass, &newsid);
+ if (rc)
+ return rc;
+
return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL);
}
struct sk_security_struct *sksec;
int err = 0;
+ isec->sclass = socket_type_to_security_class(family, type, protocol);
+
if (kern)
isec->sid = SECINITSID_KERNEL;
- else
- isec->sid = socket_sockcreate_sid(tsec);
+ else {
+ err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid));
+ if (err)
+ return err;
+ }
- isec->sclass = socket_type_to_security_class(family, type, protocol);
isec->initialized = 1;
if (sock->sk) {
static void selinux_req_classify_flow(const struct request_sock *req,
struct flowi *fl)
{
- fl->secid = req->secid;
+ fl->flowi_secid = req->secid;
}
static int selinux_tun_dev_create(void)
* from the sending socket, otherwise use the kernel's sid */
sk = skb->sk;
if (sk == NULL) {
- switch (family) {
- case PF_INET:
- if (IPCB(skb)->flags & IPSKB_FORWARDED)
- secmark_perm = PACKET__FORWARD_OUT;
- else
- secmark_perm = PACKET__SEND;
- break;
- case PF_INET6:
- if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
- secmark_perm = PACKET__FORWARD_OUT;
- else
- secmark_perm = PACKET__SEND;
- break;
- default:
- return NF_DROP_ERR(-ECONNREFUSED);
- }
- if (secmark_perm == PACKET__FORWARD_OUT) {
+ if (skb->skb_iif) {
+ secmark_perm = PACKET__FORWARD_OUT;
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
return NF_DROP;
- } else
+ } else {
+ secmark_perm = PACKET__SEND;
peer_sid = SECINITSID_KERNEL;
+ }
} else {
struct sk_security_struct *sksec = sk->sk_security;
peer_sid = sksec->sid;
{
int err;
struct common_audit_data ad;
+ u32 sid;
err = cap_netlink_recv(skb, capability);
if (err)
COMMON_AUDIT_DATA_INIT(&ad, CAP);
ad.u.cap = capability;
- return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid,
- SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad);
+ security_task_getsecid(current, &sid);
+ return avc_has_perm(sid, sid, SECCLASS_CAPABILITY,
+ CAP_TO_MASK(capability), &ad);
}
static int ipc_alloc_security(struct task_struct *task,
.sb_alloc_security = selinux_sb_alloc_security,
.sb_free_security = selinux_sb_free_security,
.sb_copy_data = selinux_sb_copy_data,
+ .sb_remount = selinux_sb_remount,
.sb_kern_mount = selinux_sb_kern_mount,
.sb_show_options = selinux_sb_show_options,
.sb_statfs = selinux_sb_statfs,