Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6: security: unexport mmap_min_addr SELinux: use SECINITSID_NETMSG instead of SECINITSID_UNLABELED for NetLabel security: Protection for exploiting null dereference using mmap SELinux: Use %lu for inode->i_no when printing avc SELinux: allow preemption between transition permission checks selinux: introduce schedule points in policydb_destroy() selinux: add selinuxfs structure for object class discovery selinux: change sel_make_dir() to specify inode counter. selinux: rename sel_remove_bools() for more general usage. selinux: add support for querying object classes and permissions from the running policy
This commit is contained in:
commit
bb50cbbd4b
20 changed files with 504 additions and 86 deletions
|
@ -31,6 +31,7 @@ Currently, these files are in /proc/sys/vm:
|
|||
- min_unmapped_ratio
|
||||
- min_slab_ratio
|
||||
- panic_on_oom
|
||||
- mmap_min_address
|
||||
|
||||
==============================================================
|
||||
|
||||
|
@ -216,3 +217,17 @@ above-mentioned.
|
|||
The default value is 0.
|
||||
1 and 2 are for failover of clustering. Please select either
|
||||
according to your policy of failover.
|
||||
|
||||
==============================================================
|
||||
|
||||
mmap_min_addr
|
||||
|
||||
This file indicates the amount of address space which a user process will
|
||||
be restricted from mmaping. Since kernel null dereference bugs could
|
||||
accidentally operate based on the information in the first couple of pages
|
||||
of memory userspace processes should not be allowed to write to them. By
|
||||
default this value is set to 0 and no protections will be enforced by the
|
||||
security module. Setting this value to something like 64k will allow the
|
||||
vast majority of applications to work correctly and provide defense in depth
|
||||
against future potential kernel bugs.
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ struct xfrm_user_sec_ctx;
|
|||
extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
|
||||
extern int cap_netlink_recv(struct sk_buff *skb, int cap);
|
||||
|
||||
extern unsigned long mmap_min_addr;
|
||||
/*
|
||||
* Values used in the task_security_ops calls
|
||||
*/
|
||||
|
@ -1241,8 +1242,9 @@ struct security_operations {
|
|||
int (*file_ioctl) (struct file * file, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
int (*file_mmap) (struct file * file,
|
||||
unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags);
|
||||
unsigned long reqprot, unsigned long prot,
|
||||
unsigned long flags, unsigned long addr,
|
||||
unsigned long addr_only);
|
||||
int (*file_mprotect) (struct vm_area_struct * vma,
|
||||
unsigned long reqprot,
|
||||
unsigned long prot);
|
||||
|
@ -1814,9 +1816,12 @@ static inline int security_file_ioctl (struct file *file, unsigned int cmd,
|
|||
|
||||
static inline int security_file_mmap (struct file *file, unsigned long reqprot,
|
||||
unsigned long prot,
|
||||
unsigned long flags)
|
||||
unsigned long flags,
|
||||
unsigned long addr,
|
||||
unsigned long addr_only)
|
||||
{
|
||||
return security_ops->file_mmap (file, reqprot, prot, flags);
|
||||
return security_ops->file_mmap (file, reqprot, prot, flags, addr,
|
||||
addr_only);
|
||||
}
|
||||
|
||||
static inline int security_file_mprotect (struct vm_area_struct *vma,
|
||||
|
@ -2489,7 +2494,9 @@ static inline int security_file_ioctl (struct file *file, unsigned int cmd,
|
|||
|
||||
static inline int security_file_mmap (struct file *file, unsigned long reqprot,
|
||||
unsigned long prot,
|
||||
unsigned long flags)
|
||||
unsigned long flags,
|
||||
unsigned long addr,
|
||||
unsigned long addr_only)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -949,6 +949,16 @@ static ctl_table vm_table[] = {
|
|||
.strategy = &sysctl_jiffies,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "mmap_min_addr",
|
||||
.data = &mmap_min_addr,
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_doulongvec_minmax,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_X86_32) || \
|
||||
(defined(CONFIG_SUPERH) && defined(CONFIG_VSYSCALL))
|
||||
{
|
||||
|
|
|
@ -1023,10 +1023,10 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
|
|||
}
|
||||
}
|
||||
|
||||
error = security_file_mmap(file, reqprot, prot, flags);
|
||||
error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
||||
/* Clear old maps */
|
||||
error = -ENOMEM;
|
||||
munmap_back:
|
||||
|
|
13
mm/mremap.c
13
mm/mremap.c
|
@ -291,6 +291,10 @@ unsigned long do_mremap(unsigned long addr,
|
|||
if ((addr <= new_addr) && (addr+old_len) > new_addr)
|
||||
goto out;
|
||||
|
||||
ret = security_file_mmap(0, 0, 0, 0, new_addr, 1);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = do_munmap(mm, new_addr, new_len);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
@ -390,8 +394,13 @@ unsigned long do_mremap(unsigned long addr,
|
|||
|
||||
new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
|
||||
vma->vm_pgoff, map_flags);
|
||||
ret = new_addr;
|
||||
if (new_addr & ~PAGE_MASK)
|
||||
if (new_addr & ~PAGE_MASK) {
|
||||
ret = new_addr;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = security_file_mmap(0, 0, 0, 0, new_addr, 1);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
ret = move_vma(vma, addr, old_len, new_len, new_addr);
|
||||
|
|
|
@ -639,7 +639,7 @@ static int validate_mmap_request(struct file *file,
|
|||
}
|
||||
|
||||
/* allow the security API to have its say */
|
||||
ret = security_file_mmap(file, reqprot, prot, flags);
|
||||
ret = security_file_mmap(file, reqprot, prot, flags, addr, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -420,8 +420,12 @@ static int dummy_file_ioctl (struct file *file, unsigned int command,
|
|||
|
||||
static int dummy_file_mmap (struct file *file, unsigned long reqprot,
|
||||
unsigned long prot,
|
||||
unsigned long flags)
|
||||
unsigned long flags,
|
||||
unsigned long addr,
|
||||
unsigned long addr_only)
|
||||
{
|
||||
if (addr < mmap_min_addr)
|
||||
return -EACCES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ extern struct security_operations dummy_security_ops;
|
|||
extern void security_fixup_ops(struct security_operations *ops);
|
||||
|
||||
struct security_operations *security_ops; /* Initialized to NULL */
|
||||
unsigned long mmap_min_addr; /* 0 means no protection */
|
||||
|
||||
static inline int verify(struct security_operations *ops)
|
||||
{
|
||||
|
|
|
@ -586,7 +586,7 @@ void avc_audit(u32 ssid, u32 tsid,
|
|||
}
|
||||
}
|
||||
if (inode)
|
||||
audit_log_format(ab, " dev=%s ino=%ld",
|
||||
audit_log_format(ab, " dev=%s ino=%lu",
|
||||
inode->i_sb->s_id,
|
||||
inode->i_ino);
|
||||
break;
|
||||
|
@ -832,6 +832,7 @@ int avc_ss_reset(u32 seqno)
|
|||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @requested: requested permissions, interpreted based on @tclass
|
||||
* @flags: AVC_STRICT or 0
|
||||
* @avd: access vector decisions
|
||||
*
|
||||
* Check the AVC to determine whether the @requested permissions are granted
|
||||
|
@ -846,8 +847,9 @@ int avc_ss_reset(u32 seqno)
|
|||
* should be released for the auditing.
|
||||
*/
|
||||
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
struct av_decision *avd)
|
||||
u16 tclass, u32 requested,
|
||||
unsigned flags,
|
||||
struct av_decision *avd)
|
||||
{
|
||||
struct avc_node *node;
|
||||
struct avc_entry entry, *p_ae;
|
||||
|
@ -874,7 +876,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
|||
denied = requested & ~(p_ae->avd.allowed);
|
||||
|
||||
if (!requested || denied) {
|
||||
if (selinux_enforcing)
|
||||
if (selinux_enforcing || (flags & AVC_STRICT))
|
||||
rc = -EACCES;
|
||||
else
|
||||
if (node)
|
||||
|
@ -909,7 +911,7 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
|
|||
struct av_decision avd;
|
||||
int rc;
|
||||
|
||||
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd);
|
||||
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
|
||||
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1592,9 +1592,10 @@ static int selinux_vm_enough_memory(long pages)
|
|||
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);
|
||||
SECCLASS_CAPABILITY,
|
||||
CAP_TO_MASK(CAP_SYS_ADMIN),
|
||||
0,
|
||||
NULL);
|
||||
|
||||
if (rc == 0)
|
||||
cap_sys_admin = 1;
|
||||
|
@ -2568,12 +2569,16 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
|
|||
}
|
||||
|
||||
static int selinux_file_mmap(struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long addr, unsigned long addr_only)
|
||||
{
|
||||
int rc;
|
||||
int rc = 0;
|
||||
u32 sid = ((struct task_security_struct*)(current->security))->sid;
|
||||
|
||||
rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
|
||||
if (rc)
|
||||
if (addr < mmap_min_addr)
|
||||
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
|
||||
MEMPROTECT__MMAP_ZERO, NULL);
|
||||
if (rc || addr_only)
|
||||
return rc;
|
||||
|
||||
if (selinux_checkreqprot)
|
||||
|
@ -3124,17 +3129,19 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
|
|||
/**
|
||||
* selinux_skb_extlbl_sid - Determine the external label of a packet
|
||||
* @skb: the packet
|
||||
* @base_sid: the SELinux SID to use as a context for MLS only external labels
|
||||
* @sid: the packet's SID
|
||||
*
|
||||
* Description:
|
||||
* Check the various different forms of external packet labeling and determine
|
||||
* the external SID for the packet.
|
||||
* the external SID for the packet. If only one form of external labeling is
|
||||
* present then it is used, if both labeled IPsec and NetLabel labels are
|
||||
* present then the SELinux type information is taken from the labeled IPsec
|
||||
* SA and the MLS sensitivity label information is taken from the NetLabel
|
||||
* security attributes. This bit of "magic" is done in the call to
|
||||
* selinux_netlbl_skbuff_getsid().
|
||||
*
|
||||
*/
|
||||
static void selinux_skb_extlbl_sid(struct sk_buff *skb,
|
||||
u32 base_sid,
|
||||
u32 *sid)
|
||||
static void selinux_skb_extlbl_sid(struct sk_buff *skb, u32 *sid)
|
||||
{
|
||||
u32 xfrm_sid;
|
||||
u32 nlbl_sid;
|
||||
|
@ -3142,10 +3149,9 @@ static void selinux_skb_extlbl_sid(struct sk_buff *skb,
|
|||
selinux_skb_xfrm_sid(skb, &xfrm_sid);
|
||||
if (selinux_netlbl_skbuff_getsid(skb,
|
||||
(xfrm_sid == SECSID_NULL ?
|
||||
base_sid : xfrm_sid),
|
||||
SECINITSID_NETMSG : xfrm_sid),
|
||||
&nlbl_sid) != 0)
|
||||
nlbl_sid = SECSID_NULL;
|
||||
|
||||
*sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
|
||||
}
|
||||
|
||||
|
@ -3690,7 +3696,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
|
|||
if (sock && sock->sk->sk_family == PF_UNIX)
|
||||
selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
|
||||
else if (skb)
|
||||
selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peer_secid);
|
||||
selinux_skb_extlbl_sid(skb, &peer_secid);
|
||||
|
||||
if (peer_secid == SECSID_NULL)
|
||||
err = -EINVAL;
|
||||
|
@ -3751,7 +3757,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
|
|||
u32 newsid;
|
||||
u32 peersid;
|
||||
|
||||
selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid);
|
||||
selinux_skb_extlbl_sid(skb, &peersid);
|
||||
if (peersid == SECSID_NULL) {
|
||||
req->secid = sksec->sid;
|
||||
req->peer_secid = SECSID_NULL;
|
||||
|
@ -3789,7 +3795,7 @@ static void selinux_inet_conn_established(struct sock *sk,
|
|||
{
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
|
||||
selinux_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid);
|
||||
selinux_skb_extlbl_sid(skb, &sksec->peer_sid);
|
||||
}
|
||||
|
||||
static void selinux_req_classify_flow(const struct request_sock *req,
|
||||
|
@ -4626,7 +4632,7 @@ static int selinux_setprocattr(struct task_struct *p,
|
|||
if (p->ptrace & PT_PTRACED) {
|
||||
error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
|
||||
SECCLASS_PROCESS,
|
||||
PROCESS__PTRACE, &avd);
|
||||
PROCESS__PTRACE, 0, &avd);
|
||||
if (!error)
|
||||
tsec->sid = sid;
|
||||
task_unlock(p);
|
||||
|
|
|
@ -158,3 +158,4 @@
|
|||
S_(SECCLASS_KEY, KEY__CREATE, "create")
|
||||
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind")
|
||||
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
|
||||
S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
|
||||
|
|
|
@ -823,3 +823,4 @@
|
|||
#define DCCP_SOCKET__NAME_BIND 0x00200000UL
|
||||
#define DCCP_SOCKET__NODE_BIND 0x00400000UL
|
||||
#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL
|
||||
#define MEMPROTECT__MMAP_ZERO 0x00000001UL
|
||||
|
|
|
@ -102,9 +102,11 @@ void avc_audit(u32 ssid, u32 tsid,
|
|||
u16 tclass, u32 requested,
|
||||
struct av_decision *avd, int result, struct avc_audit_data *auditdata);
|
||||
|
||||
#define AVC_STRICT 1 /* Ignore permissive mode. */
|
||||
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
struct av_decision *avd);
|
||||
u16 tclass, u32 requested,
|
||||
unsigned flags,
|
||||
struct av_decision *avd);
|
||||
|
||||
int avc_has_perm(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
|
|
|
@ -63,3 +63,4 @@
|
|||
S_("key")
|
||||
S_(NULL)
|
||||
S_("dccp_socket")
|
||||
S_("memprotect")
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#define SECCLASS_PACKET 57
|
||||
#define SECCLASS_KEY 58
|
||||
#define SECCLASS_DCCP_SOCKET 60
|
||||
#define SECCLASS_MEMPROTECT 61
|
||||
|
||||
/*
|
||||
* Security identifier indices for initial entities
|
||||
|
|
|
@ -41,6 +41,7 @@ extern int selinux_mls_enabled;
|
|||
|
||||
int security_load_policy(void * data, size_t len);
|
||||
|
||||
#define SEL_VEC_MAX 32
|
||||
struct av_decision {
|
||||
u32 allowed;
|
||||
u32 decided;
|
||||
|
@ -87,6 +88,9 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
|
|||
|
||||
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
|
||||
|
||||
int security_get_classes(char ***classes, int *nclasses);
|
||||
int security_get_permissions(char *class, char ***perms, int *nperms);
|
||||
|
||||
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
|
||||
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
|
||||
#define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */
|
||||
|
|
|
@ -158,9 +158,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
|
|||
netlbl_secattr_init(&secattr);
|
||||
rc = netlbl_skbuff_getattr(skb, &secattr);
|
||||
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
|
||||
rc = security_netlbl_secattr_to_sid(&secattr,
|
||||
base_sid,
|
||||
sid);
|
||||
rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid);
|
||||
else
|
||||
*sid = SECSID_NULL;
|
||||
netlbl_secattr_destroy(&secattr);
|
||||
|
@ -198,7 +196,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
|
|||
if (netlbl_sock_getattr(sk, &secattr) == 0 &&
|
||||
secattr.flags != NETLBL_SECATTR_NONE &&
|
||||
security_netlbl_secattr_to_sid(&secattr,
|
||||
SECINITSID_UNLABELED,
|
||||
SECINITSID_NETMSG,
|
||||
&nlbl_peer_sid) == 0)
|
||||
sksec->peer_sid = nlbl_peer_sid;
|
||||
netlbl_secattr_destroy(&secattr);
|
||||
|
@ -295,38 +293,32 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
|
|||
struct avc_audit_data *ad)
|
||||
{
|
||||
int rc;
|
||||
u32 netlbl_sid;
|
||||
u32 recv_perm;
|
||||
u32 nlbl_sid;
|
||||
u32 perm;
|
||||
|
||||
rc = selinux_netlbl_skbuff_getsid(skb,
|
||||
SECINITSID_UNLABELED,
|
||||
&netlbl_sid);
|
||||
rc = selinux_netlbl_skbuff_getsid(skb, SECINITSID_NETMSG, &nlbl_sid);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
if (netlbl_sid == SECSID_NULL)
|
||||
return 0;
|
||||
if (nlbl_sid == SECSID_NULL)
|
||||
nlbl_sid = SECINITSID_UNLABELED;
|
||||
|
||||
switch (sksec->sclass) {
|
||||
case SECCLASS_UDP_SOCKET:
|
||||
recv_perm = UDP_SOCKET__RECVFROM;
|
||||
perm = UDP_SOCKET__RECVFROM;
|
||||
break;
|
||||
case SECCLASS_TCP_SOCKET:
|
||||
recv_perm = TCP_SOCKET__RECVFROM;
|
||||
perm = TCP_SOCKET__RECVFROM;
|
||||
break;
|
||||
default:
|
||||
recv_perm = RAWIP_SOCKET__RECVFROM;
|
||||
perm = RAWIP_SOCKET__RECVFROM;
|
||||
}
|
||||
|
||||
rc = avc_has_perm(sksec->sid,
|
||||
netlbl_sid,
|
||||
sksec->sclass,
|
||||
recv_perm,
|
||||
ad);
|
||||
rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
|
||||
netlbl_skbuff_err(skb, rc);
|
||||
if (nlbl_sid != SECINITSID_UNLABELED)
|
||||
netlbl_skbuff_err(skb, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,10 @@ static struct dentry *bool_dir = NULL;
|
|||
static int bool_num = 0;
|
||||
static int *bool_pending_values = NULL;
|
||||
|
||||
/* global data for classes */
|
||||
static struct dentry *class_dir = NULL;
|
||||
static unsigned long last_class_ino;
|
||||
|
||||
extern void selnl_notify_setenforce(int val);
|
||||
|
||||
/* Check whether a task is allowed to use a security operation. */
|
||||
|
@ -106,6 +110,7 @@ static unsigned long sel_last_ino = SEL_INO_NEXT - 1;
|
|||
|
||||
#define SEL_INITCON_INO_OFFSET 0x01000000
|
||||
#define SEL_BOOL_INO_OFFSET 0x02000000
|
||||
#define SEL_CLASS_INO_OFFSET 0x04000000
|
||||
#define SEL_INO_MASK 0x00ffffff
|
||||
|
||||
#define TMPBUFLEN 12
|
||||
|
@ -237,6 +242,11 @@ static const struct file_operations sel_policyvers_ops = {
|
|||
|
||||
/* declaration for sel_write_load */
|
||||
static int sel_make_bools(void);
|
||||
static int sel_make_classes(void);
|
||||
|
||||
/* declaration for sel_make_class_dirs */
|
||||
static int sel_make_dir(struct inode *dir, struct dentry *dentry,
|
||||
unsigned long *ino);
|
||||
|
||||
static ssize_t sel_read_mls(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
@ -287,10 +297,18 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
|
|||
goto out;
|
||||
|
||||
ret = sel_make_bools();
|
||||
if (ret) {
|
||||
length = ret;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = sel_make_classes();
|
||||
if (ret)
|
||||
length = ret;
|
||||
else
|
||||
length = count;
|
||||
|
||||
out1:
|
||||
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
|
||||
"policy loaded auid=%u",
|
||||
audit_get_loginuid(current->audit_context));
|
||||
|
@ -940,9 +958,8 @@ static const struct file_operations sel_commit_bools_ops = {
|
|||
.write = sel_commit_bools_write,
|
||||
};
|
||||
|
||||
/* delete booleans - partial revoke() from
|
||||
* fs/proc/generic.c proc_kill_inodes */
|
||||
static void sel_remove_bools(struct dentry *de)
|
||||
/* partial revoke() from fs/proc/generic.c proc_kill_inodes */
|
||||
static void sel_remove_entries(struct dentry *de)
|
||||
{
|
||||
struct list_head *p, *node;
|
||||
struct super_block *sb = de->d_sb;
|
||||
|
@ -998,7 +1015,7 @@ static int sel_make_bools(void)
|
|||
kfree(bool_pending_values);
|
||||
bool_pending_values = NULL;
|
||||
|
||||
sel_remove_bools(dir);
|
||||
sel_remove_entries(dir);
|
||||
|
||||
if (!(page = (char*)get_zeroed_page(GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
@ -1048,7 +1065,7 @@ static int sel_make_bools(void)
|
|||
return ret;
|
||||
err:
|
||||
kfree(values);
|
||||
sel_remove_bools(dir);
|
||||
sel_remove_entries(dir);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1294,7 +1311,227 @@ static int sel_make_initcon_files(struct dentry *dir)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sel_make_dir(struct inode *dir, struct dentry *dentry)
|
||||
static inline unsigned int sel_div(unsigned long a, unsigned long b)
|
||||
{
|
||||
return a / b - (a % b < 0);
|
||||
}
|
||||
|
||||
static inline unsigned long sel_class_to_ino(u16 class)
|
||||
{
|
||||
return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET;
|
||||
}
|
||||
|
||||
static inline u16 sel_ino_to_class(unsigned long ino)
|
||||
{
|
||||
return sel_div(ino & SEL_INO_MASK, SEL_VEC_MAX + 1);
|
||||
}
|
||||
|
||||
static inline unsigned long sel_perm_to_ino(u16 class, u32 perm)
|
||||
{
|
||||
return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET;
|
||||
}
|
||||
|
||||
static inline u32 sel_ino_to_perm(unsigned long ino)
|
||||
{
|
||||
return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1);
|
||||
}
|
||||
|
||||
static ssize_t sel_read_class(struct file * file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t rc, len;
|
||||
char *page;
|
||||
unsigned long ino = file->f_path.dentry->d_inode->i_ino;
|
||||
|
||||
page = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino));
|
||||
rc = simple_read_from_buffer(buf, count, ppos, page, len);
|
||||
free_page((unsigned long)page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations sel_class_ops = {
|
||||
.read = sel_read_class,
|
||||
};
|
||||
|
||||
static ssize_t sel_read_perm(struct file * file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t rc, len;
|
||||
char *page;
|
||||
unsigned long ino = file->f_path.dentry->d_inode->i_ino;
|
||||
|
||||
page = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = snprintf(page, PAGE_SIZE,"%d", sel_ino_to_perm(ino));
|
||||
rc = simple_read_from_buffer(buf, count, ppos, page, len);
|
||||
free_page((unsigned long)page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations sel_perm_ops = {
|
||||
.read = sel_read_perm,
|
||||
};
|
||||
|
||||
static int sel_make_perm_files(char *objclass, int classvalue,
|
||||
struct dentry *dir)
|
||||
{
|
||||
int i, rc = 0, nperms;
|
||||
char **perms;
|
||||
|
||||
rc = security_get_permissions(objclass, &perms, &nperms);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < nperms; i++) {
|
||||
struct inode *inode;
|
||||
struct dentry *dentry;
|
||||
|
||||
dentry = d_alloc_name(dir, perms[i]);
|
||||
if (!dentry) {
|
||||
rc = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
|
||||
if (!inode) {
|
||||
rc = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
inode->i_fop = &sel_perm_ops;
|
||||
/* i+1 since perm values are 1-indexed */
|
||||
inode->i_ino = sel_perm_to_ino(classvalue, i+1);
|
||||
d_add(dentry, inode);
|
||||
}
|
||||
|
||||
out1:
|
||||
for (i = 0; i < nperms; i++)
|
||||
kfree(perms[i]);
|
||||
kfree(perms);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sel_make_class_dir_entries(char *classname, int index,
|
||||
struct dentry *dir)
|
||||
{
|
||||
struct dentry *dentry = NULL;
|
||||
struct inode *inode = NULL;
|
||||
int rc;
|
||||
|
||||
dentry = d_alloc_name(dir, "index");
|
||||
if (!dentry) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
|
||||
if (!inode) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
inode->i_fop = &sel_class_ops;
|
||||
inode->i_ino = sel_class_to_ino(index);
|
||||
d_add(dentry, inode);
|
||||
|
||||
dentry = d_alloc_name(dir, "perms");
|
||||
if (!dentry) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = sel_make_dir(dir->d_inode, dentry, &last_class_ino);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = sel_make_perm_files(classname, index, dentry);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void sel_remove_classes(void)
|
||||
{
|
||||
struct list_head *class_node;
|
||||
|
||||
list_for_each(class_node, &class_dir->d_subdirs) {
|
||||
struct dentry *class_subdir = list_entry(class_node,
|
||||
struct dentry, d_u.d_child);
|
||||
struct list_head *class_subdir_node;
|
||||
|
||||
list_for_each(class_subdir_node, &class_subdir->d_subdirs) {
|
||||
struct dentry *d = list_entry(class_subdir_node,
|
||||
struct dentry, d_u.d_child);
|
||||
|
||||
if (d->d_inode)
|
||||
if (d->d_inode->i_mode & S_IFDIR)
|
||||
sel_remove_entries(d);
|
||||
}
|
||||
|
||||
sel_remove_entries(class_subdir);
|
||||
}
|
||||
|
||||
sel_remove_entries(class_dir);
|
||||
}
|
||||
|
||||
static int sel_make_classes(void)
|
||||
{
|
||||
int rc = 0, nclasses, i;
|
||||
char **classes;
|
||||
|
||||
/* delete any existing entries */
|
||||
sel_remove_classes();
|
||||
|
||||
rc = security_get_classes(&classes, &nclasses);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
/* +2 since classes are 1-indexed */
|
||||
last_class_ino = sel_class_to_ino(nclasses+2);
|
||||
|
||||
for (i = 0; i < nclasses; i++) {
|
||||
struct dentry *class_name_dir;
|
||||
|
||||
class_name_dir = d_alloc_name(class_dir, classes[i]);
|
||||
if (!class_name_dir) {
|
||||
rc = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
rc = sel_make_dir(class_dir->d_inode, class_name_dir,
|
||||
&last_class_ino);
|
||||
if (rc)
|
||||
goto out1;
|
||||
|
||||
/* i+1 since class values are 1-indexed */
|
||||
rc = sel_make_class_dir_entries(classes[i], i+1,
|
||||
class_name_dir);
|
||||
if (rc)
|
||||
goto out1;
|
||||
}
|
||||
|
||||
out1:
|
||||
for (i = 0; i < nclasses; i++)
|
||||
kfree(classes[i]);
|
||||
kfree(classes);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sel_make_dir(struct inode *dir, struct dentry *dentry,
|
||||
unsigned long *ino)
|
||||
{
|
||||
int ret = 0;
|
||||
struct inode *inode;
|
||||
|
@ -1306,7 +1543,7 @@ static int sel_make_dir(struct inode *dir, struct dentry *dentry)
|
|||
}
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_ino = ++sel_last_ino;
|
||||
inode->i_ino = ++(*ino);
|
||||
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
||||
inc_nlink(inode);
|
||||
d_add(dentry, inode);
|
||||
|
@ -1352,7 +1589,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = sel_make_dir(root_inode, dentry);
|
||||
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -1385,7 +1622,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = sel_make_dir(root_inode, dentry);
|
||||
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -1399,7 +1636,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = sel_make_dir(root_inode, dentry);
|
||||
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -1407,6 +1644,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
dentry = d_alloc_name(sb->s_root, "class");
|
||||
if (!dentry) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
class_dir = dentry;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
err:
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
|
@ -598,6 +599,7 @@ void policydb_destroy(struct policydb *p)
|
|||
struct range_trans *rt, *lrt = NULL;
|
||||
|
||||
for (i = 0; i < SYM_NUM; i++) {
|
||||
cond_resched();
|
||||
hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
|
||||
hashtab_destroy(p->symtab[i].table);
|
||||
}
|
||||
|
@ -612,6 +614,7 @@ void policydb_destroy(struct policydb *p)
|
|||
avtab_destroy(&p->te_avtab);
|
||||
|
||||
for (i = 0; i < OCON_NUM; i++) {
|
||||
cond_resched();
|
||||
c = p->ocontexts[i];
|
||||
while (c) {
|
||||
ctmp = c;
|
||||
|
@ -623,6 +626,7 @@ void policydb_destroy(struct policydb *p)
|
|||
|
||||
g = p->genfs;
|
||||
while (g) {
|
||||
cond_resched();
|
||||
kfree(g->fstype);
|
||||
c = g->head;
|
||||
while (c) {
|
||||
|
@ -639,18 +643,21 @@ void policydb_destroy(struct policydb *p)
|
|||
cond_policydb_destroy(p);
|
||||
|
||||
for (tr = p->role_tr; tr; tr = tr->next) {
|
||||
cond_resched();
|
||||
kfree(ltr);
|
||||
ltr = tr;
|
||||
}
|
||||
kfree(ltr);
|
||||
|
||||
for (ra = p->role_allow; ra; ra = ra -> next) {
|
||||
cond_resched();
|
||||
kfree(lra);
|
||||
lra = ra;
|
||||
}
|
||||
kfree(lra);
|
||||
|
||||
for (rt = p->range_tr; rt; rt = rt -> next) {
|
||||
cond_resched();
|
||||
if (lrt) {
|
||||
ebitmap_destroy(&lrt->target_range.level[0].cat);
|
||||
ebitmap_destroy(&lrt->target_range.level[1].cat);
|
||||
|
|
|
@ -1587,19 +1587,18 @@ int security_get_user_sids(u32 fromsid,
|
|||
u32 *nel)
|
||||
{
|
||||
struct context *fromcon, usercon;
|
||||
u32 *mysids, *mysids2, sid;
|
||||
u32 *mysids = NULL, *mysids2, sid;
|
||||
u32 mynel = 0, maxnel = SIDS_NEL;
|
||||
struct user_datum *user;
|
||||
struct role_datum *role;
|
||||
struct av_decision avd;
|
||||
struct ebitmap_node *rnode, *tnode;
|
||||
int rc = 0, i, j;
|
||||
|
||||
if (!ss_initialized) {
|
||||
*sids = NULL;
|
||||
*nel = 0;
|
||||
*sids = NULL;
|
||||
*nel = 0;
|
||||
|
||||
if (!ss_initialized)
|
||||
goto out;
|
||||
}
|
||||
|
||||
POLICY_RDLOCK;
|
||||
|
||||
|
@ -1635,17 +1634,9 @@ int security_get_user_sids(u32 fromsid,
|
|||
if (mls_setup_user_range(fromcon, user, &usercon))
|
||||
continue;
|
||||
|
||||
rc = context_struct_compute_av(fromcon, &usercon,
|
||||
SECCLASS_PROCESS,
|
||||
PROCESS__TRANSITION,
|
||||
&avd);
|
||||
if (rc || !(avd.allowed & PROCESS__TRANSITION))
|
||||
continue;
|
||||
rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
|
||||
if (rc) {
|
||||
kfree(mysids);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
}
|
||||
if (mynel < maxnel) {
|
||||
mysids[mynel++] = sid;
|
||||
} else {
|
||||
|
@ -1653,7 +1644,6 @@ int security_get_user_sids(u32 fromsid,
|
|||
mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
|
||||
if (!mysids2) {
|
||||
rc = -ENOMEM;
|
||||
kfree(mysids);
|
||||
goto out_unlock;
|
||||
}
|
||||
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
|
||||
|
@ -1664,11 +1654,32 @@ int security_get_user_sids(u32 fromsid,
|
|||
}
|
||||
}
|
||||
|
||||
*sids = mysids;
|
||||
*nel = mynel;
|
||||
|
||||
out_unlock:
|
||||
POLICY_RDUNLOCK;
|
||||
if (rc || !mynel) {
|
||||
kfree(mysids);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
|
||||
if (!mysids2) {
|
||||
rc = -ENOMEM;
|
||||
kfree(mysids);
|
||||
goto out;
|
||||
}
|
||||
for (i = 0, j = 0; i < mynel; i++) {
|
||||
rc = avc_has_perm_noaudit(fromsid, mysids[i],
|
||||
SECCLASS_PROCESS,
|
||||
PROCESS__TRANSITION, AVC_STRICT,
|
||||
NULL);
|
||||
if (!rc)
|
||||
mysids2[j++] = mysids[i];
|
||||
cond_resched();
|
||||
}
|
||||
rc = 0;
|
||||
kfree(mysids);
|
||||
*sids = mysids2;
|
||||
*nel = j;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
@ -1996,6 +2007,101 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int get_classes_callback(void *k, void *d, void *args)
|
||||
{
|
||||
struct class_datum *datum = d;
|
||||
char *name = k, **classes = args;
|
||||
int value = datum->value - 1;
|
||||
|
||||
classes[value] = kstrdup(name, GFP_ATOMIC);
|
||||
if (!classes[value])
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int security_get_classes(char ***classes, int *nclasses)
|
||||
{
|
||||
int rc = -ENOMEM;
|
||||
|
||||
POLICY_RDLOCK;
|
||||
|
||||
*nclasses = policydb.p_classes.nprim;
|
||||
*classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
|
||||
if (!*classes)
|
||||
goto out;
|
||||
|
||||
rc = hashtab_map(policydb.p_classes.table, get_classes_callback,
|
||||
*classes);
|
||||
if (rc < 0) {
|
||||
int i;
|
||||
for (i = 0; i < *nclasses; i++)
|
||||
kfree((*classes)[i]);
|
||||
kfree(*classes);
|
||||
}
|
||||
|
||||
out:
|
||||
POLICY_RDUNLOCK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int get_permissions_callback(void *k, void *d, void *args)
|
||||
{
|
||||
struct perm_datum *datum = d;
|
||||
char *name = k, **perms = args;
|
||||
int value = datum->value - 1;
|
||||
|
||||
perms[value] = kstrdup(name, GFP_ATOMIC);
|
||||
if (!perms[value])
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int security_get_permissions(char *class, char ***perms, int *nperms)
|
||||
{
|
||||
int rc = -ENOMEM, i;
|
||||
struct class_datum *match;
|
||||
|
||||
POLICY_RDLOCK;
|
||||
|
||||
match = hashtab_search(policydb.p_classes.table, class);
|
||||
if (!match) {
|
||||
printk(KERN_ERR "%s: unrecognized class %s\n",
|
||||
__FUNCTION__, class);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*nperms = match->permissions.nprim;
|
||||
*perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
|
||||
if (!*perms)
|
||||
goto out;
|
||||
|
||||
if (match->comdatum) {
|
||||
rc = hashtab_map(match->comdatum->permissions.table,
|
||||
get_permissions_callback, *perms);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = hashtab_map(match->permissions.table, get_permissions_callback,
|
||||
*perms);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
out:
|
||||
POLICY_RDUNLOCK;
|
||||
return rc;
|
||||
|
||||
err:
|
||||
POLICY_RDUNLOCK;
|
||||
for (i = 0; i < *nperms; i++)
|
||||
kfree((*perms)[i]);
|
||||
kfree(*perms);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct selinux_audit_rule {
|
||||
u32 au_seqno;
|
||||
struct context au_ctxt;
|
||||
|
|
Loading…
Reference in a new issue