#include <linux/kd.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/tty.h>
#include <net/icmp.h>
#include <net/ip.h> /* for sysctl_local_port_range[] */
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
#include <linux/nfs_mount.h>
#include <net/ipv6.h>
#include <linux/hugetlb.h>
-#include <linux/major.h>
+#include <linux/personality.h>
+#include <linux/sysctl.h>
#include "avc.h"
#include "objsec.h"
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+extern unsigned int policydb_loaded_version;
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;
#endif
#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
-int selinux_enabled = 1;
+int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
static int __init selinux_enabled_setup(char *str)
{
/* Lists of inode and superblock security structures initialized
before the policy was loaded. */
static LIST_HEAD(superblock_security_head);
-static spinlock_t sb_security_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(sb_security_lock);
/* Allocate and free functions for each kind of security blob. */
break;
case Opt_fscontext:
- if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
- rc = -EINVAL;
- printk(KERN_WARNING "SELinux: "
- "fscontext option is invalid for"
- " this filesystem type\n");
- goto out_free;
- }
if (seen & (Opt_context|Opt_fscontext)) {
rc = -EINVAL;
printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
}
rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELFROM, NULL, NULL);
+ FILESYSTEM__RELABELFROM, NULL);
if (rc)
goto out_free;
rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELTO, NULL, NULL);
+ FILESYSTEM__RELABELTO, NULL);
if (rc)
goto out_free;
goto out_free;
rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__RELABELFROM, NULL, NULL);
+ FILESYSTEM__RELABELFROM, NULL);
if (rc)
goto out_free;
rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE, NULL, NULL);
+ FILESYSTEM__ASSOCIATE, NULL);
if (rc)
goto out_free;
return SECCLASS_FILE;
}
-static inline u16 socket_type_to_security_class(int family, int type)
+static inline u16 socket_type_to_security_class(int family, int type, int protocol)
{
switch (family) {
case PF_UNIX:
switch (type) {
case SOCK_STREAM:
+ case SOCK_SEQPACKET:
return SECCLASS_UNIX_STREAM_SOCKET;
case SOCK_DGRAM:
return SECCLASS_UNIX_DGRAM_SOCKET;
}
+ break;
case PF_INET:
case PF_INET6:
switch (type) {
case SOCK_RAW:
return SECCLASS_RAWIP_SOCKET;
}
+ break;
case PF_NETLINK:
- return SECCLASS_NETLINK_SOCKET;
+ switch (protocol) {
+ case NETLINK_ROUTE:
+ return SECCLASS_NETLINK_ROUTE_SOCKET;
+ case NETLINK_FIREWALL:
+ return SECCLASS_NETLINK_FIREWALL_SOCKET;
+ case NETLINK_TCPDIAG:
+ return SECCLASS_NETLINK_TCPDIAG_SOCKET;
+ case NETLINK_NFLOG:
+ return SECCLASS_NETLINK_NFLOG_SOCKET;
+ case NETLINK_XFRM:
+ return SECCLASS_NETLINK_XFRM_SOCKET;
+ case NETLINK_SELINUX:
+ return SECCLASS_NETLINK_SELINUX_SOCKET;
+ case NETLINK_AUDIT:
+ return SECCLASS_NETLINK_AUDIT_SOCKET;
+ case NETLINK_IP6_FW:
+ return SECCLASS_NETLINK_IP6FW_SOCKET;
+ case NETLINK_DNRTMSG:
+ return SECCLASS_NETLINK_DNRT_SOCKET;
+ default:
+ return SECCLASS_NETLINK_SOCKET;
+ }
case PF_PACKET:
return SECCLASS_PACKET_SOCKET;
case PF_KEY:
struct socket *sock = SOCKET_I(inode);
if (sock->sk) {
isec->sclass = socket_type_to_security_class(sock->sk->sk_family,
- sock->sk->sk_type);
+ sock->sk->sk_type,
+ sock->sk->sk_protocol);
} else {
isec->sclass = SECCLASS_SOCKET;
}
tsec1 = tsk1->security;
tsec2 = tsk2->security;
return avc_has_perm(tsec1->sid, tsec2->sid,
- SECCLASS_PROCESS, perms, &tsec2->avcr, NULL);
+ SECCLASS_PROCESS, perms, NULL);
}
/* Check whether a task is allowed to use a capability. */
ad.u.cap = cap;
return avc_has_perm(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY, CAP_TO_MASK(cap), NULL, &ad);
+ SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
}
/* Check whether a task is allowed to use a system operation. */
tsec = tsk->security;
return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
- SECCLASS_SYSTEM, perms, NULL, NULL);
+ SECCLASS_SYSTEM, perms, NULL);
}
/* Check whether a task has a particular permission to an inode.
- The 'aeref' parameter is optional and allows other AVC
- entry references to be passed (e.g. the one in the struct file).
The 'adp' parameter is optional and allows other audit
data to be passed (e.g. the dentry). */
int inode_has_perm(struct task_struct *tsk,
struct inode *inode,
u32 perms,
- struct avc_entry_ref *aeref,
struct avc_audit_data *adp)
{
struct task_security_struct *tsec;
ad.u.fs.inode = inode;
}
- return avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- perms, aeref ? aeref : &isec->avcr, adp);
+ return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.mnt = mnt;
ad.u.fs.dentry = dentry;
- return inode_has_perm(tsk, inode, av, NULL, &ad);
+ return inode_has_perm(tsk, inode, av, &ad);
}
/* Check whether a task can use an open file descriptor to
rc = avc_has_perm(tsec->sid, fsec->sid,
SECCLASS_FD,
FD__USE,
- &fsec->avcr, &ad);
+ &ad);
if (rc)
return rc;
}
/* av is zero if only checking access to the descriptor. */
if (av)
- return inode_has_perm(tsk, inode, av, &fsec->inode_avcr, &ad);
+ return inode_has_perm(tsk, inode, av, &ad);
return 0;
}
rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
- &dsec->avcr, &ad);
+ &ad);
if (rc)
return rc;
return rc;
}
- rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, NULL, &ad);
+ rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
return rc;
return avc_has_perm(newsid, sbsec->sid,
SECCLASS_FILESYSTEM,
- FILESYSTEM__ASSOCIATE, NULL, &ad);
+ FILESYSTEM__ASSOCIATE, &ad);
}
#define MAY_LINK 0
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
- av, &dsec->avcr, &ad);
+ rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
return 0;
}
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- av, &isec->avcr, &ad);
+ rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
return rc;
}
ad.u.fs.dentry = old_dentry;
rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
- DIR__REMOVE_NAME | DIR__SEARCH,
- &old_dsec->avcr, &ad);
+ DIR__REMOVE_NAME | DIR__SEARCH, &ad);
if (rc)
return rc;
rc = avc_has_perm(tsec->sid, old_isec->sid,
- old_isec->sclass,
- FILE__RENAME,
- &old_isec->avcr, &ad);
+ 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,
- old_isec->sclass,
- DIR__REPARENT,
- &old_isec->avcr, &ad);
+ old_isec->sclass, DIR__REPARENT, &ad);
if (rc)
return rc;
}
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,&new_dsec->avcr, &ad);
+ rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
if (new_dentry->d_inode) {
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
rc = avc_has_perm(tsec->sid, new_isec->sid,
new_isec->sclass,
- (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
- &new_isec->avcr, &ad);
+ (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
if (rc)
return rc;
}
tsec = tsk->security;
sbsec = sb->s_security;
return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- perms, NULL, ad);
+ perms, ad);
}
/* Convert a Linux mode and permission mask to an access vector. */
int inode_security_set_sid(struct inode *inode, u32 sid)
{
struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+ if (!sbsec->initialized) {
+ /* Defer initialization to selinux_complete_init. */
+ return 0;
+ }
down(&isec->sem);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
{
int error;
- error = task_has_perm(current, target, PROCESS__SETCAP);
+ error = secondary_ops->capset_check(target, effective, inheritable, permitted);
if (error)
return error;
- return secondary_ops->capset_check(target, effective, inheritable, permitted);
+ return task_has_perm(current, target, PROCESS__SETCAP);
}
static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- int error;
-
- error = task_has_perm(current, target, PROCESS__SETCAP);
- if (error)
- return;
-
secondary_ops->capset_set(target, effective, inheritable, permitted);
}
u32 tsid;
int rc;
+ rc = secondary_ops->sysctl(table, op);
+ if (rc)
+ return rc;
+
tsec = current->security;
rc = selinux_proc_get_sid(table->de, (op == 001) ?
* a bad coupling between this module and sysctl.c */
if(op == 001) {
error = avc_has_perm(tsec->sid, tsid,
- SECCLASS_DIR, DIR__SEARCH, NULL, NULL);
+ SECCLASS_DIR, DIR__SEARCH, NULL);
} else {
av = 0;
if (op & 004)
av |= FILE__WRITE;
if (av)
error = avc_has_perm(tsec->sid, tsid,
- SECCLASS_FILE, av, NULL, NULL);
+ SECCLASS_FILE, av, NULL);
}
return error;
return rc;
}
-static int selinux_quota_on(struct file *f)
+static int selinux_quota_on(struct dentry *dentry)
{
- return file_has_perm(current, f, FILE__QUOTAON);
+ return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
}
static int selinux_syslog(int type)
* mapping. 0 means there is enough memory for the allocation to
* succeed and -ENOMEM implies there is not.
*
- * We currently support three overcommit policies, which are set via the
- * vm.overcommit_memory sysctl. See Documentation/vm/overcommit-accounting
+ * Note that secondary_ops->capable and task_has_perm_noaudit return 0
+ * if the capability is granted, but __vm_enough_memory requires 1 if
+ * the capability is granted.
*
- * Strict overcommit modes added 2002 Feb 26 by Alan Cox.
- * Additional code 2002 Jul 20 by Robert Love.
+ * Do not audit the selinux permission check, as this is applied to all
+ * processes that allocate mappings.
*/
static int selinux_vm_enough_memory(long pages)
{
- unsigned long free, allowed;
- int rc;
+ int rc, cap_sys_admin = 0;
struct task_security_struct *tsec = current->security;
- vm_acct_memory(pages);
-
- /*
- * Sometimes we want to use more memory than we have
- */
- if (sysctl_overcommit_memory == 1)
- return 0;
-
- if (sysctl_overcommit_memory == 0) {
- free = get_page_cache_size();
- free += nr_free_pages();
- free += nr_swap_pages;
-
- /*
- * Any slabs which are created with the
- * SLAB_RECLAIM_ACCOUNT flag claim to have contents
- * which are reclaimable, under pressure. The dentry
- * cache and most inode caches should fall into this
- */
- free += atomic_read(&slab_reclaim_pages);
-
- /*
- * Leave the last 3% for privileged processes.
- * Don't audit the check, as it is applied to all processes
- * that allocate mappings.
- */
- rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
- if (!rc) {
- rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY,
- CAP_TO_MASK(CAP_SYS_ADMIN),
- NULL, NULL);
- }
- if (rc)
- free -= free / 32;
-
- if (free > pages)
- return 0;
- vm_unacct_memory(pages);
- return -ENOMEM;
- }
-
- allowed = (totalram_pages - hugetlb_total_pages())
- * sysctl_overcommit_ratio / 100;
- allowed += total_swap_pages;
-
- if (atomic_read(&vm_committed_space) < allowed)
- return 0;
+ 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),
+ NULL);
- vm_unacct_memory(pages);
+ if (rc == 0)
+ cap_sys_admin = 1;
- return -ENOMEM;
-}
-
-static int selinux_netlink_send(struct sk_buff *skb)
-{
- if (capable(CAP_NET_ADMIN))
- cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
- else
- NETLINK_CB(skb).eff_cap = 0;
- return 0;
-}
-
-static int selinux_netlink_recv(struct sk_buff *skb)
-{
- if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
- return -EPERM;
- return 0;
+ return __vm_enough_memory(pages, cap_sys_admin);
}
/* binprm security operations */
if (tsec->sid == newsid) {
rc = avc_has_perm(tsec->sid, isec->sid,
- SECCLASS_FILE, FILE__EXECUTE_NO_TRANS,
- &isec->avcr, &ad);
+ SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
/* Check permissions for the transition. */
rc = avc_has_perm(tsec->sid, newsid,
- SECCLASS_PROCESS, PROCESS__TRANSITION,
- NULL,
- &ad);
+ SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (rc)
return rc;
rc = avc_has_perm(newsid, isec->sid,
- SECCLASS_FILE, FILE__ENTRYPOINT,
- &isec->avcr, &ad);
+ SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
+ /* Clear any possibly unsafe personality bits on exec: */
+ current->personality &= ~PER_CLEAR_ON_SETID;
+
/* Set the security field to the new SID. */
bsec->sid = newsid;
}
static int selinux_bprm_check_security (struct linux_binprm *bprm)
{
- return 0;
+ return secondary_ops->bprm_check_security(bprm);
}
the two SIDs, i.e. ahp returns 0. */
atsecure = avc_has_perm(tsec->osid, tsec->sid,
SECCLASS_PROCESS,
- PROCESS__NOATSECURE, NULL, NULL);
+ PROCESS__NOATSECURE, NULL);
}
- /* Note that we must include the legacy uid/gid test below
- to retain it, as the new userland will simply use the
- value passed by AT_SECURE to decide whether to enable
- secure mode. */
- return ( atsecure || current->euid != current->uid ||
- current->egid != current->gid);
+ return (atsecure || secondary_ops->bprm_secureexec(bprm));
}
static void selinux_bprm_free_security(struct linux_binprm *bprm)
kfree(bsec);
}
-/* Create an open file that refers to the null device.
- Derived from the OpenWall LSM. */
-struct file *open_devnull(void)
-{
- struct inode *inode;
- struct dentry *dentry;
- struct file *file = NULL;
- struct inode_security_struct *isec;
- dev_t dev;
-
- inode = new_inode(current->fs->rootmnt->mnt_sb);
- if (!inode)
- goto out;
-
- dentry = dget(d_alloc_root(inode));
- if (!dentry)
- goto out_iput;
-
- file = get_empty_filp();
- if (!file)
- goto out_dput;
-
- dev = MKDEV(MEM_MAJOR, 3); /* null device */
-
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
- inode->i_blksize = PAGE_SIZE;
- inode->i_blocks = 0;
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_state = I_DIRTY; /* so that mark_inode_dirty won't touch us */
-
- isec = inode->i_security;
- isec->sid = SECINITSID_DEVNULL;
- isec->sclass = SECCLASS_CHR_FILE;
- isec->initialized = 1;
-
- file->f_flags = O_RDWR;
- file->f_mode = FMODE_READ | FMODE_WRITE;
- file->f_dentry = dentry;
- file->f_vfsmnt = mntget(current->fs->rootmnt);
- file->f_pos = 0;
-
- init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, dev);
- if (inode->i_fop->open(inode, file))
- goto out_fput;
-
-out:
- return file;
-out_fput:
- mntput(file->f_vfsmnt);
- put_filp(file);
-out_dput:
- dput(dentry);
-out_iput:
- iput(inode);
- file = NULL;
- goto out;
-}
+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)
{
struct avc_audit_data ad;
struct file *file, *devnull = NULL;
+ struct tty_struct *tty = current->signal->tty;
long j = -1;
+ if (tty) {
+ file_list_lock();
+ file = list_entry(tty->tty_files.next, typeof(*file), f_list);
+ if (file) {
+ /* Revalidate access to controlling tty.
+ Use inode_has_perm on the tty inode directly rather
+ than using file_has_perm, as this particular open
+ file may belong to another process and we are only
+ interested in the inode-based check here. */
+ struct inode *inode = file->f_dentry->d_inode;
+ if (inode_has_perm(current, inode,
+ FILE__READ | FILE__WRITE, NULL)) {
+ /* Reset controlling tty. */
+ current->signal->tty = NULL;
+ current->signal->tty_old_pgrp = 0;
+ }
+ }
+ file_list_unlock();
+ }
+
+ /* Revalidate access to inherited open files. */
+
AVC_AUDIT_DATA_INIT(&ad,FS);
spin_lock(&files->file_lock);
for (;;) {
- unsigned long set, i, fd;
+ unsigned long set, i;
+ int fd;
j++;
i = j * __NFDBITS;
if (devnull) {
atomic_inc(&devnull->f_count);
} else {
- devnull = open_devnull();
+ devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
if (!devnull) {
put_unused_fd(fd);
fput(file);
struct task_security_struct *tsec;
struct bprm_security_struct *bsec;
u32 sid;
- struct av_decision avd;
- struct itimerval itimer;
- struct rlimit *rlim, *initrlim;
- int rc, i;
+ int rc;
secondary_ops->bprm_apply_creds(bprm, unsafe);
sid = bsec->sid;
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_noaudit(tsec->sid, sid,
- SECCLASS_PROCESS, PROCESS__SHARE,
- NULL, &avd);
+ rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__SHARE, NULL);
if (rc) {
- task_unlock(current);
- avc_audit(tsec->sid, sid, SECCLASS_PROCESS,
- PROCESS__SHARE, &avd, rc, NULL);
- force_sig_specific(SIGKILL, current);
- goto lock_out;
+ bsec->unsafe = 1;
+ 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)) {
- rc = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
+ rc = avc_has_perm(tsec->ptrace_sid, sid,
SECCLASS_PROCESS, PROCESS__PTRACE,
- NULL, &avd);
- if (!rc)
- tsec->sid = sid;
- task_unlock(current);
- avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd, rc, NULL);
+ NULL);
if (rc) {
- force_sig_specific(SIGKILL, current);
- goto lock_out;
+ bsec->unsafe = 1;
+ return;
}
- } else {
- tsec->sid = sid;
- task_unlock(current);
- }
-
- /* Close files for which the new task SID is not authorized. */
- flush_unauthorized_files(current->files);
-
- /* Check whether the new SID can inherit signal state
- from the old SID. If not, clear itimers to avoid
- subsequent signal generation and flush and unblock
- signals. 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(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__SIGINH, NULL, NULL);
- if (rc) {
- memset(&itimer, 0, sizeof itimer);
- for (i = 0; i < 3; i++)
- do_setitimer(i, &itimer, NULL);
- flush_signals(current);
- spin_lock_irq(¤t->sighand->siglock);
- flush_signal_handlers(current, 1);
- sigemptyset(¤t->blocked);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
}
+ tsec->sid = sid;
+ }
+}
- /* 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(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__RLIMITINH, NULL, NULL);
- if (rc) {
- for (i = 0; i < RLIM_NLIMITS; i++) {
- rlim = current->rlim + i;
- initrlim = init_task.rlim+i;
- rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur);
- }
- }
+/*
+ * called after apply_creds without the task lock held
+ */
+static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec;
+ struct rlimit *rlim, *initrlim;
+ struct itimerval itimer;
+ struct bprm_security_struct *bsec;
+ int rc, i;
- /* Wake up the parent if it is waiting so that it can
- recheck wait permission to the new task SID. */
- wake_up_interruptible(¤t->parent->wait_chldexit);
+ tsec = current->security;
+ bsec = bprm->security;
-lock_out:
- task_lock(current);
+ if (bsec->unsafe) {
+ force_sig_specific(SIGKILL, current);
return;
}
+ if (tsec->osid == tsec->sid)
+ return;
+
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(current->files);
+
+ /* Check whether the new SID can inherit signal state
+ from the old SID. If not, clear itimers to avoid
+ subsequent signal generation and flush and unblock
+ signals. 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(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+ PROCESS__SIGINH, NULL);
+ if (rc) {
+ memset(&itimer, 0, sizeof itimer);
+ for (i = 0; i < 3; i++)
+ do_setitimer(i, &itimer, NULL);
+ flush_signals(current);
+ spin_lock_irq(¤t->sighand->siglock);
+ flush_signal_handlers(current, 1);
+ sigemptyset(¤t->blocked);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+ }
+
+ /* 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(tsec->osid, 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);
+ }
+ }
+
+ /* Wake up the parent if it is waiting so that it can
+ recheck wait permission to the new task SID. */
+ wake_up_interruptible(¤t->parent->signal->wait_chldexit);
}
/* superblock security operations */
unsigned long flags,
void * data)
{
+ int rc;
+
+ rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
+ if (rc)
+ return rc;
+
if (flags & MS_REMOUNT)
return superblock_has_perm(current, nd->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
static int selinux_umount(struct vfsmount *mnt, int flags)
{
+ int rc;
+
+ rc = secondary_ops->sb_umount(mnt, flags);
+ if (rc)
+ return rc;
+
return superblock_has_perm(current,mnt->mnt_sb,
FILESYSTEM__UNMOUNT,NULL);
}
static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
{
+ int rc;
+
+ rc = secondary_ops->inode_unlink(dir, dentry);
+ if (rc)
+ return rc;
return may_link(dir, dentry, MAY_UNLINK);
}
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
+ int rc;
+
+ rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
+ if (rc)
+ return rc;
+
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
static int selinux_inode_permission(struct inode *inode, int mask,
struct nameidata *nd)
{
+ int rc;
+
+ rc = secondary_ops->inode_permission(inode, mask, nd);
+ if (rc)
+ return rc;
+
if (!mask) {
/* No permission to check. Existence test. */
return 0;
}
return inode_has_perm(current, inode,
- file_mask_to_av(inode->i_mode, mask), NULL, NULL);
+ file_mask_to_av(inode->i_mode, mask), NULL);
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
+ int rc;
+
+ rc = secondary_ops->inode_setattr(dentry, iattr);
+ if (rc)
+ return rc;
+
+ if (iattr->ia_valid & ATTR_FORCE)
+ return 0;
+
if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME_SET | ATTR_MTIME_SET))
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
return -EOPNOTSUPP;
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.dentry = dentry;
rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- FILE__RELABELFROM,
- &isec->avcr, &ad);
+ FILE__RELABELFROM, &ad);
if (rc)
return rc;
return rc;
rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
- FILE__RELABELTO, NULL, &ad);
+ FILE__RELABELTO, &ad);
if (rc)
return rc;
sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE,
- NULL,
&ad);
}
return -EACCES;
}
-static int selinux_inode_getsecurity(struct dentry *dentry, const char *name, void *buffer, size_t size)
+static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size)
{
- struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
char *context;
unsigned len;
return len;
}
-static int selinux_inode_setsecurity(struct dentry *dentry, const char *name,
+static int selinux_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
- struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
u32 newsid;
int rc;
return 0;
}
-static int selinux_inode_listsecurity(struct dentry *dentry, char *buffer)
+static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
{
const int len = sizeof(XATTR_NAME_SELINUX);
- if (buffer)
+ if (buffer && len <= buffer_size)
memcpy(buffer, XATTR_NAME_SELINUX, len);
return len;
}
return error;
}
-static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
- u32 av;
+ if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+ * private file mapping that will also be writable.
+ * This has an additional check.
+ */
+ int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+ if (rc)
+ return rc;
+ }
if (file) {
/* read access is always possible with a mapping */
- av = FILE__READ;
+ u32 av = FILE__READ;
/* write access only matters if the mapping is shared */
- if ((flags & MAP_TYPE) == MAP_SHARED && (prot & PROT_WRITE))
+ if (shared && (prot & PROT_WRITE))
av |= FILE__WRITE;
if (prot & PROT_EXEC)
return 0;
}
+static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+{
+ int rc;
+
+ rc = secondary_ops->file_mmap(file, prot, flags);
+ if (rc)
+ return rc;
+
+ return file_map_prot_check(file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long prot)
{
- return selinux_file_mmap(vma->vm_file, prot, vma->vm_flags);
+ int rc;
+
+ rc = secondary_ops->file_mprotect(vma, prot);
+ if (rc)
+ return rc;
+
+ if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) {
+ /*
+ * We are making executable a file mapping that has
+ * had some COW done. Since pages might have been written,
+ * check ability to execute the possibly modified content.
+ * This typically should only occur for text relocations.
+ */
+ int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD);
+ if (rc)
+ return rc;
+ }
+
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
}
static int selinux_file_lock(struct file *file, unsigned int cmd)
}
static int selinux_file_send_sigiotask(struct task_struct *tsk,
- struct fown_struct *fown,
- int fd, int reason)
+ struct fown_struct *fown, int signum)
{
struct file *file;
u32 perm;
tsec = tsk->security;
fsec = file->f_security;
- if (!fown->signum)
+ if (!signum)
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
else
- perm = signal_to_av(fown->signum);
+ perm = signal_to_av(signum);
return avc_has_perm(fsec->fown_sid, tsec->sid,
- SECCLASS_PROCESS, perm, NULL, NULL);
+ SECCLASS_PROCESS, perm, NULL);
}
static int selinux_file_receive(struct file *file)
static int selinux_task_create(unsigned long clone_flags)
{
+ int rc;
+
+ rc = secondary_ops->task_create(clone_flags);
+ if (rc)
+ return rc;
+
return task_has_perm(current, current, PROCESS__FORK);
}
tsec2->exec_sid = tsec1->exec_sid;
tsec2->create_sid = tsec1->create_sid;
+ /* Retain ptracer SID across fork, if any.
+ This will be reset by the ptrace hook upon any
+ subsequent ptrace_attach operations. */
+ tsec2->ptrace_sid = tsec1->ptrace_sid;
+
return 0;
}
static int selinux_task_setnice(struct task_struct *p, int nice)
{
+ int rc;
+
+ rc = secondary_ops->task_setnice(p, nice);
+ if (rc)
+ return rc;
+
return task_has_perm(current,p, PROCESS__SETSCHED);
}
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
{
- struct rlimit *old_rlim = current->rlim + resource;
+ struct rlimit *old_rlim = current->signal->rlim + resource;
+ int rc;
+
+ rc = secondary_ops->task_setrlimit(resource, new_rlim);
+ if (rc)
+ return rc;
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
{
- struct task_security_struct *tsec1, *tsec2;
-
- tsec1 = current->security;
- tsec2 = p->security;
-
- /* No auditing from the setscheduler hook, since the runqueue lock
- is held and the system will deadlock if we try to log an audit
- message. */
- return avc_has_perm_noaudit(tsec1->sid, tsec2->sid,
- SECCLASS_PROCESS, PROCESS__SETSCHED,
- &tsec2->avcr, NULL);
+ return task_has_perm(current, p, PROCESS__SETSCHED);
}
static int selinux_task_getscheduler(struct task_struct *p)
static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
{
u32 perm;
+ int rc;
+
+ rc = secondary_ops->task_kill(p, info, sig);
+ if (rc)
+ return rc;
if (info && ((unsigned long)info == 1 ||
(unsigned long)info == 2 || SI_FROMKERNEL(info)))
/* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
{
- int offset, ihlen, ret;
- struct iphdr iph;
+ int offset, ihlen, ret = -EINVAL;
+ struct iphdr _iph, *ih;
offset = skb->nh.raw - skb->data;
- ret = skb_copy_bits(skb, offset, &iph, sizeof(iph));
- if (ret)
+ ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
+ if (ih == NULL)
goto out;
- ihlen = iph.ihl * 4;
- if (ihlen < sizeof(iph))
+ ihlen = ih->ihl * 4;
+ if (ihlen < sizeof(_iph))
goto out;
- ad->u.net.v4info.saddr = iph.saddr;
- ad->u.net.v4info.daddr = iph.daddr;
+ ad->u.net.v4info.saddr = ih->saddr;
+ ad->u.net.v4info.daddr = ih->daddr;
+ ret = 0;
- switch (iph.protocol) {
+ switch (ih->protocol) {
case IPPROTO_TCP: {
- struct tcphdr tcph;
+ struct tcphdr _tcph, *th;
- if (ntohs(iph.frag_off) & IP_OFFSET)
+ if (ntohs(ih->frag_off) & IP_OFFSET)
break;
offset += ihlen;
- if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
break;
- ad->u.net.sport = tcph.source;
- ad->u.net.dport = tcph.dest;
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
break;
}
case IPPROTO_UDP: {
- struct udphdr udph;
+ struct udphdr _udph, *uh;
- if (ntohs(iph.frag_off) & IP_OFFSET)
+ if (ntohs(ih->frag_off) & IP_OFFSET)
break;
offset += ihlen;
- if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
- break;
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
+ break;
- ad->u.net.sport = udph.source;
- ad->u.net.dport = udph.dest;
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
break;
}
static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
{
u8 nexthdr;
- int ret, offset;
- struct ipv6hdr ipv6h;
+ int ret = -EINVAL, offset;
+ struct ipv6hdr _ipv6h, *ip6;
offset = skb->nh.raw - skb->data;
- ret = skb_copy_bits(skb, offset, &ipv6h, sizeof(ipv6h));
- if (ret)
+ ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
+ if (ip6 == NULL)
goto out;
- ipv6_addr_copy(&ad->u.net.v6info.saddr, &ipv6h.saddr);
- ipv6_addr_copy(&ad->u.net.v6info.daddr, &ipv6h.daddr);
+ ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
+ ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
+ ret = 0;
- nexthdr = ipv6h.nexthdr;
- offset += sizeof(ipv6h);
+ nexthdr = ip6->nexthdr;
+ offset += sizeof(_ipv6h);
offset = ipv6_skip_exthdr(skb, offset, &nexthdr,
skb->tail - skb->head - offset);
if (offset < 0)
switch (nexthdr) {
case IPPROTO_TCP: {
- struct tcphdr tcph;
+ struct tcphdr _tcph, *th;
- if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
break;
- ad->u.net.sport = tcph.source;
- ad->u.net.dport = tcph.dest;
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
break;
}
case IPPROTO_UDP: {
- struct udphdr udph;
+ struct udphdr _udph, *uh;
- if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
break;
- ad->u.net.sport = udph.source;
- ad->u.net.dport = udph.dest;
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
break;
}
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = sock->sk;
- err = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
- perms, &isec->avcr, &ad);
+ err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
out:
return err;
tsec = current->security;
err = avc_has_perm(tsec->sid, tsec->sid,
- socket_type_to_security_class(family, type),
- SOCKET__CREATE, NULL, NULL);
+ socket_type_to_security_class(family, type,
+ protocol), SOCKET__CREATE, NULL);
out:
return err;
isec = SOCK_INODE(sock)->i_security;
tsec = current->security;
- isec->sclass = socket_type_to_security_class(family, type);
+ isec->sclass = socket_type_to_security_class(family, type, protocol);
isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
return;
goto out;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sport = htons(snum);
+ ad.u.net.family = family;
err = avc_has_perm(isec->sid, sid,
isec->sclass,
- SOCKET__NAME_BIND, NULL, &ad);
+ SOCKET__NAME_BIND, &ad);
if (err)
goto out;
}
ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);
err = avc_has_perm(isec->sid, sid,
- isec->sclass, node_perm, NULL, &ad);
+ isec->sclass, node_perm, &ad);
if (err)
goto out;
}
struct avc_audit_data ad;
int err;
+ err = secondary_ops->unix_stream_connect(sock, other, newsk);
+ if (err)
+ return err;
+
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
err = avc_has_perm(isec->sid, other_isec->sid,
isec->sclass,
- UNIX_STREAM_SOCKET__CONNECTTO,
- &other_isec->avcr, &ad);
+ UNIX_STREAM_SOCKET__CONNECTTO, &ad);
if (err)
return err;
ad.u.net.sk = other->sk;
err = avc_has_perm(isec->sid, other_isec->sid,
- isec->sclass,
- SOCKET__SENDTO,
- &other_isec->avcr, &ad);
+ isec->sclass, SOCKET__SENDTO, &ad);
if (err)
return err;
u16 family;
char *addrp;
int len, err = 0;
- u32 netif_perm, node_perm, node_sid, recv_perm = 0;
+ u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
+ u32 sock_sid = 0;
+ u16 sock_class = 0;
struct socket *sock;
- struct inode *inode;
struct net_device *dev;
- struct sel_netif *netif;
- struct netif_security_struct *nsec;
- struct inode_security_struct *isec;
struct avc_audit_data ad;
family = sk->sk_family;
if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
family = PF_INET;
- sock = sk->sk_socket;
-
- /* TCP control messages don't always have a socket. */
- if (!sock)
- goto out;
-
- inode = SOCK_INODE(sock);
- if (!inode)
- goto out;
+ read_lock_bh(&sk->sk_callback_lock);
+ sock = sk->sk_socket;
+ if (sock) {
+ struct inode *inode;
+ inode = SOCK_INODE(sock);
+ if (inode) {
+ struct inode_security_struct *isec;
+ isec = inode->i_security;
+ sock_sid = isec->sid;
+ sock_class = isec->sclass;
+ }
+ }
+ read_unlock_bh(&sk->sk_callback_lock);
+ if (!sock_sid)
+ goto out;
dev = skb->dev;
if (!dev)
goto out;
- netif = sel_netif_lookup(dev);
- if (IS_ERR(netif)) {
- err = PTR_ERR(netif);
+ err = sel_netif_sids(dev, &if_sid, NULL);
+ if (err)
goto out;
- }
-
- nsec = &netif->nsec;
- isec = inode->i_security;
- switch (isec->sclass) {
+ switch (sock_class) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_RECV;
node_perm = NODE__UDP_RECV;
ad.u.net.family = family;
err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
- if (err) {
- sel_netif_put(netif);
+ if (err)
goto out;
- }
- err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF,
- netif_perm, &nsec->avcr, &ad);
- sel_netif_put(netif);
+ err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad);
if (err)
goto out;
if (err)
goto out;
- err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad);
+ err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad);
if (err)
goto out;
if (err)
goto out;
- err = avc_has_perm(isec->sid, port_sid, isec->sclass,
- recv_perm, NULL, &ad);
+ err = avc_has_perm(sock_sid, port_sid,
+ sock_class, recv_perm, &ad);
}
out:
return err;
sk_free_security(sk);
}
+static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+ int err = 0;
+ u32 perm;
+ struct nlmsghdr *nlh;
+ struct socket *sock = sk->sk_socket;
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+
+ if (skb->len < NLMSG_SPACE(0)) {
+ err = -EINVAL;
+ goto out;
+ }
+ nlh = (struct nlmsghdr *)skb->data;
+
+ err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
+ if (err) {
+ /* Ignore */
+ if (err == -ENOENT)
+ err = 0;
+ goto out;
+ }
+
+ err = socket_has_perm(current, sock, perm);
+out:
+ return err;
+}
+
#ifdef CONFIG_NETFILTER
static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
{
char *addrp;
int len, err = NF_ACCEPT;
- u32 netif_perm, node_perm, node_sid, send_perm = 0;
+ u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
struct sock *sk;
struct socket *sock;
struct inode *inode;
- struct sel_netif *netif;
struct sk_buff *skb = *pskb;
- struct netif_security_struct *nsec;
struct inode_security_struct *isec;
struct avc_audit_data ad;
struct net_device *dev = (struct net_device *)out;
if (!inode)
goto out;
- netif = sel_netif_lookup(dev);
- if (IS_ERR(netif)) {
- err = NF_DROP;
+ err = sel_netif_sids(dev, &if_sid, NULL);
+ if (err)
goto out;
- }
-
- nsec = &netif->nsec;
+
isec = inode->i_security;
switch (isec->sclass) {
err = selinux_parse_skb(skb, &ad, &addrp,
&len, 0) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT) {
- sel_netif_put(netif);
+ if (err != NF_ACCEPT)
goto out;
- }
- err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF,
- netif_perm, &nsec->avcr, &ad) ? NF_DROP : NF_ACCEPT;
- sel_netif_put(netif);
+ err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF,
+ netif_perm, &ad) ? NF_DROP : NF_ACCEPT;
if (err != NF_ACCEPT)
goto out;
goto out;
err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE,
- node_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT;
+ node_perm, &ad) ? NF_DROP : NF_ACCEPT;
if (err != NF_ACCEPT)
goto out;
goto out;
err = avc_has_perm(isec->sid, port_sid, isec->sclass,
- send_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT;
+ send_perm, &ad) ? NF_DROP : NF_ACCEPT;
}
out:
#endif /* CONFIG_NETFILTER */
+#else
+
+static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+ return 0;
+}
+
#endif /* CONFIG_SECURITY_NETWORK */
+static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+ struct task_security_struct *tsec;
+ struct av_decision avd;
+ int err;
+
+ err = secondary_ops->netlink_send(sk, skb);
+ if (err)
+ return err;
+
+ tsec = current->security;
+
+ avd.allowed = 0;
+ avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY, ~0, &avd);
+ cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
+
+ if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
+ err = selinux_nlmsg_perm(sk, skb);
+
+ return err;
+}
+
+static int selinux_netlink_recv(struct sk_buff *skb)
+{
+ if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
static int ipc_alloc_security(struct task_struct *task,
struct kern_ipc_perm *perm,
u16 sclass)
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
- return avc_has_perm(tsec->sid, isec->sid, sclass,
- perms, &isec->avcr, &ad);
+ return avc_has_perm(tsec->sid, isec->sid, sclass, perms, &ad);
}
static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__CREATE, &isec->avcr, &ad);
+ MSGQ__CREATE, &ad);
if (rc) {
ipc_free_security(&msq->q_perm);
return rc;
ad.u.ipc_id = msq->q_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__ASSOCIATE, &isec->avcr, &ad);
+ MSGQ__ASSOCIATE, &ad);
}
static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
/* Can this process write to the queue? */
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
- MSGQ__WRITE, &isec->avcr, &ad);
+ MSGQ__WRITE, &ad);
if (!rc)
/* Can this process send the message */
rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__SEND,
- &msec->avcr, &ad);
+ 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, MSGQ__ENQUEUE,
- &isec->avcr, &ad);
+ SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
return rc;
}
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid,
- SECCLASS_MSGQ, MSGQ__READ,
- &isec->avcr, &ad);
+ SECCLASS_MSGQ, MSGQ__READ, &ad);
if (!rc)
rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__RECEIVE,
- &msec->avcr, &ad);
+ SECCLASS_MSG, MSG__RECEIVE, &ad);
return rc;
}
ad.u.ipc_id = shp->shm_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
- SHM__CREATE, &isec->avcr, &ad);
+ SHM__CREATE, &ad);
if (rc) {
ipc_free_security(&shp->shm_perm);
return rc;
ad.u.ipc_id = shp->shm_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
- SHM__ASSOCIATE, &isec->avcr, &ad);
+ SHM__ASSOCIATE, &ad);
}
/* Note, at this point, shp is locked down */
char __user *shmaddr, int shmflg)
{
u32 perms;
+ int rc;
+
+ rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
+ if (rc)
+ return rc;
if (shmflg & SHM_RDONLY)
perms = SHM__READ;
ad.u.ipc_id = sma->sem_perm.key;
rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
- SEM__CREATE, &isec->avcr, &ad);
+ SEM__CREATE, &ad);
if (rc) {
ipc_free_security(&sma->sem_perm);
return rc;
ad.u.ipc_id = sma->sem_perm.key;
return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
- SEM__ASSOCIATE, &isec->avcr, &ad);
+ SEM__ASSOCIATE, &ad);
}
/* Note, at this point, sma is locked down */
u32 sid = 0;
int error;
- if (current != p || !strcmp(name, "current")) {
+ if (current != p) {
/* SELinux only allows a process to change its own
- security attributes, and it only allows the process
- current SID to change via exec. */
+ security attributes. */
return -EACCES;
}
error = task_has_perm(current, p, PROCESS__SETEXEC);
else if (!strcmp(name, "fscreate"))
error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+ else if (!strcmp(name, "current"))
+ error = task_has_perm(current, p, PROCESS__SETCURRENT);
else
error = -EINVAL;
if (error)
tsec->exec_sid = sid;
else if (!strcmp(name, "fscreate"))
tsec->create_sid = sid;
+ else if (!strcmp(name, "current")) {
+ struct av_decision avd;
+
+ if (sid == 0)
+ return -EINVAL;
+
+ /* Only allow single threaded processes to change context */
+ if (atomic_read(&p->mm->mm_users) != 1) {
+ struct task_struct *g, *t;
+ struct mm_struct *mm = p->mm;
+ read_lock(&tasklist_lock);
+ do_each_thread(g, t)
+ if (t->mm == mm && t != p) {
+ read_unlock(&tasklist_lock);
+ return -EPERM;
+ }
+ while_each_thread(g, t);
+ read_unlock(&tasklist_lock);
+ }
+
+ /* Check permissions for the transition. */
+ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__DYNTRANSITION, NULL);
+ if (error)
+ return error;
+
+ /* Check for ptracing, and update the task SID if ok.
+ Otherwise, leave SID unchanged and fail. */
+ task_lock(p);
+ if (p->ptrace & PT_PTRACED) {
+ error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, &avd);
+ if (!error)
+ tsec->sid = sid;
+ task_unlock(p);
+ avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, &avd, error, NULL);
+ if (error)
+ return error;
+ } else {
+ tsec->sid = sid;
+ task_unlock(p);
+ }
+ }
else
return -EINVAL;
.bprm_alloc_security = selinux_bprm_alloc_security,
.bprm_free_security = selinux_bprm_free_security,
.bprm_apply_creds = selinux_bprm_apply_creds,
+ .bprm_post_apply_creds = selinux_bprm_post_apply_creds,
.bprm_set_security = selinux_bprm_set_security,
.bprm_check_security = selinux_bprm_check_security,
.bprm_secureexec = selinux_bprm_secureexec,