2008-07-07 16:51:05

by James Morris

[permalink] [raw]
Subject: SELinux patches for 2.6.26


Please review these SELinux patches for the 2.6.26 kernel.

fs/proc/base.c | 9
fs/proc/task_mmu.c | 6
fs/proc/task_nommu.c | 2
include/linux/ptrace.h | 8
include/linux/security.h | 16 -
kernel/ptrace.c | 15 -
security/commoncap.c | 3
security/dummy.c | 3
security/security.c | 11
security/selinux/hooks.c | 156 +++++++-----
security/selinux/include/audit.h | 4
security/selinux/include/avc.h | 15 -
security/selinux/include/objsec.h | 1
security/selinux/include/security.h | 7
security/selinux/netnode.c | 1
security/selinux/netport.c | 3
security/selinux/selinuxfs.c | 15 -
security/selinux/ss/avtab.c | 2
security/selinux/ss/context.h | 27 ++
security/selinux/ss/mls.c | 19 -
security/selinux/ss/mls.h | 3
security/selinux/ss/policydb.c | 15 -
security/selinux/ss/services.c | 450 ++++++++++++++++++++++--------------
security/selinux/ss/sidtab.c | 76 ++----
security/selinux/ss/sidtab.h | 7
security/smack/smack_lsm.c | 5
26 files changed, 518 insertions(+), 361 deletions(-)


2008-07-07 16:50:12

by James Morris

[permalink] [raw]
Subject: [PATCH 18/20] SELinux: use do_each_thread as a proper do/while block

Use do_each_thread as a proper do/while block. Sparse complained.

Signed-off-by: James Morris <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
---
security/selinux/hooks.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6e8d0e9..4130d64 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5196,12 +5196,12 @@ static int selinux_setprocattr(struct task_struct *p,
struct task_struct *g, *t;
struct mm_struct *mm = p->mm;
read_lock(&tasklist_lock);
- do_each_thread(g, t)
+ do_each_thread(g, t) {
if (t->mm == mm && t != p) {
read_unlock(&tasklist_lock);
return -EPERM;
}
- while_each_thread(g, t);
+ } while_each_thread(g, t);
read_unlock(&tasklist_lock);
}

--
1.5.5.1

2008-07-07 16:50:48

by James Morris

[permalink] [raw]
Subject: [PATCH 04/20] SELinux: reorder inode_security_struct to increase objs/slab on 64bit

From: Richard Kennedy <[email protected]>

reorder inode_security_struct to remove padding on 64 bit builds

size reduced from 72 to 64 bytes increasing objects per slab to 64.

Signed-off-by: Richard Kennedy <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/include/objsec.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 032c235..5f0be19 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -43,8 +43,8 @@ struct inode_security_struct {
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */
- struct mutex lock;
unsigned char inherit; /* inherit SID from parent entry */
+ struct mutex lock;
};

struct file_security_struct {
--
1.5.5.1

2008-07-07 16:51:27

by James Morris

[permalink] [raw]
Subject: [PATCH 19/20] security: fix return of void-valued expressions

Fix several warnings generated by sparse of the form
"returning void-valued expression".

Signed-off-by: James Morris <[email protected]>
Acked-by: Casey Schaufler <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
---
security/security.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/security/security.c b/security/security.c
index c4507ce..2c0a587 100644
--- a/security/security.c
+++ b/security/security.c
@@ -895,7 +895,7 @@ EXPORT_SYMBOL(security_secctx_to_secid);

void security_release_secctx(char *secdata, u32 seclen)
{
- return security_ops->release_secctx(secdata, seclen);
+ security_ops->release_secctx(secdata, seclen);
}
EXPORT_SYMBOL(security_release_secctx);

@@ -1012,12 +1012,12 @@ int security_sk_alloc(struct sock *sk, int family, gfp_t priority)

void security_sk_free(struct sock *sk)
{
- return security_ops->sk_free_security(sk);
+ security_ops->sk_free_security(sk);
}

void security_sk_clone(const struct sock *sk, struct sock *newsk)
{
- return security_ops->sk_clone_security(sk, newsk);
+ security_ops->sk_clone_security(sk, newsk);
}

void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
--
1.5.5.1

2008-07-07 16:51:41

by James Morris

[permalink] [raw]
Subject: [PATCH 17/20] SELinux: remove unused and shadowed addrlen variable

Remove unused and shadowed addrlen variable. Picked up by sparse.

Signed-off-by: James Morris <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
Acked-by: Paul Moore <[email protected]>
---
security/selinux/hooks.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f530008..6e8d0e9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3669,7 +3669,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
struct sockaddr_in6 *addr6 = NULL;
unsigned short snum;
struct sock *sk = sock->sk;
- u32 sid, node_perm, addrlen;
+ u32 sid, node_perm;

tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
@@ -3677,12 +3677,10 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (family == PF_INET) {
addr4 = (struct sockaddr_in *)address;
snum = ntohs(addr4->sin_port);
- addrlen = sizeof(addr4->sin_addr.s_addr);
addrp = (char *)&addr4->sin_addr.s_addr;
} else {
addr6 = (struct sockaddr_in6 *)address;
snum = ntohs(addr6->sin6_port);
- addrlen = sizeof(addr6->sin6_addr.s6_addr);
addrp = (char *)&addr6->sin6_addr.s6_addr;
}

--
1.5.5.1

2008-07-07 16:51:56

by James Morris

[permalink] [raw]
Subject: [PATCH 15/20] selinux: change handling of invalid classes (Was: Re: 2.6.26-rc5-mm1 selinux whine)

From: Stephen Smalley <[email protected]>

On Mon, 2008-06-09 at 01:24 -0700, Andrew Morton wrote:
> Getting a few of these with FC5:
>
> SELinux: context_struct_compute_av: unrecognized class 69
> SELinux: context_struct_compute_av: unrecognized class 69
>
> one came out when I logged in.
>
> No other symptoms, yet.

Change handling of invalid classes by SELinux, reporting class values
unknown to the kernel as errors (w/ ratelimit applied) and handling
class values unknown to policy as normal denials.

Signed-off-by: Stephen Smalley <[email protected]>
Acked-by: Eric Paris <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/ss/services.c | 16 +++++++++++++---
1 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 543fd0f..04c0b70 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -407,9 +407,19 @@ static int context_struct_compute_av(struct context *scontext,
return 0;

inval_class:
- printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", __func__,
- tclass);
- return -EINVAL;
+ if (!tclass || tclass > kdefs->cts_len ||
+ !kdefs->class_to_string[tclass]) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "SELinux: %s: unrecognized class %d\n",
+ __func__, tclass);
+ return -EINVAL;
+ }
+
+ /*
+ * Known to the kernel, but not to the policy.
+ * Handle as a denial (allowed is 0).
+ */
+ return 0;
}

/*
--
1.5.5.1

2008-07-07 16:52:18

by James Morris

[permalink] [raw]
Subject: [PATCH 13/20] SELinux: fix off by 1 reference of class_to_string in context_struct_compute_av

From: Eric Paris <[email protected]>

The class_to_string array is referenced by tclass. My code mistakenly
was using tclass - 1. If the proceeding class is a userspace class
rather than kernel class this may cause a denial/EINVAL even if unknown
handling is set to allow. The bug shouldn't be allowing excess
privileges since those are given based on the contents of another array
which should be correctly referenced.

At this point in time its pretty unlikely this is going to cause
problems. The most recently added kernel classes which could be
affected are association, dccp_socket, and peer. Its pretty unlikely
any policy with handle_unknown=allow doesn't have association and
dccp_socket undefined (they've been around longer than unknown handling)
and peer is conditionalized on a policy cap which should only be defined
if that class exists in policy.

Signed-off-by: Eric Paris <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/ss/services.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index d06df33..f26a8ca 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -325,7 +325,7 @@ static int context_struct_compute_av(struct context *scontext,
goto inval_class;
if (unlikely(tclass > policydb.p_classes.nprim))
if (tclass > kdefs->cts_len ||
- !kdefs->class_to_string[tclass - 1] ||
+ !kdefs->class_to_string[tclass] ||
!policydb.allow_unknown)
goto inval_class;

--
1.5.5.1

2008-07-07 16:52:38

by James Morris

[permalink] [raw]
Subject: [PATCH 12/20] SELinux: open code sidtab lock

Open code sidtab lock to make Andrew Morton happy.

Signed-off-by: James Morris <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
---
security/selinux/ss/sidtab.c | 18 +++++++-----------
1 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index ba35416..a81ded1 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -14,10 +14,6 @@
#define SIDTAB_HASH(sid) \
(sid & SIDTAB_HASH_MASK)

-#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock)
-#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x)
-#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x)
-
int sidtab_init(struct sidtab *s)
{
int i;
@@ -30,7 +26,7 @@ int sidtab_init(struct sidtab *s)
s->nel = 0;
s->next_sid = 1;
s->shutdown = 0;
- INIT_SIDTAB_LOCK(s);
+ spin_lock_init(&s->lock);
return 0;
}

@@ -180,7 +176,7 @@ int sidtab_context_to_sid(struct sidtab *s,

sid = sidtab_search_context(s, context);
if (!sid) {
- SIDTAB_LOCK(s, flags);
+ spin_lock_irqsave(&s->lock, flags);
/* Rescan now that we hold the lock. */
sid = sidtab_search_context(s, context);
if (sid)
@@ -199,7 +195,7 @@ int sidtab_context_to_sid(struct sidtab *s,
if (ret)
s->next_sid--;
unlock_out:
- SIDTAB_UNLOCK(s, flags);
+ spin_unlock_irqrestore(&s->lock, flags);
}

if (ret)
@@ -264,19 +260,19 @@ void sidtab_set(struct sidtab *dst, struct sidtab *src)
{
unsigned long flags;

- SIDTAB_LOCK(src, flags);
+ spin_lock_irqsave(&src->lock, flags);
dst->htable = src->htable;
dst->nel = src->nel;
dst->next_sid = src->next_sid;
dst->shutdown = 0;
- SIDTAB_UNLOCK(src, flags);
+ spin_unlock_irqrestore(&src->lock, flags);
}

void sidtab_shutdown(struct sidtab *s)
{
unsigned long flags;

- SIDTAB_LOCK(s, flags);
+ spin_lock_irqsave(&s->lock, flags);
s->shutdown = 1;
- SIDTAB_UNLOCK(s, flags);
+ spin_unlock_irqrestore(&s->lock, flags);
}
--
1.5.5.1

2008-07-07 16:52:53

by James Morris

[permalink] [raw]
Subject: [PATCH 08/20] selinux: simplify ioctl checking

From: Stephen Smalley <[email protected]>

Simplify and improve the robustness of the SELinux ioctl checking by
using the "access mode" bits of the ioctl command to determine the
permission check rather than dealing with individual command values.
This removes any knowledge of specific ioctl commands from SELinux
and follows the same guidance we gave to Smack earlier.

Signed-off-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/hooks.c | 48 +++++++--------------------------------------
1 files changed, 8 insertions(+), 40 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 91b666a..f530008 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -42,9 +42,7 @@
#include <linux/fdtable.h>
#include <linux/namei.h>
#include <linux/mount.h>
-#include <linux/ext2_fs.h>
#include <linux/proc_fs.h>
-#include <linux/kd.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/tty.h>
@@ -2903,46 +2901,16 @@ static void selinux_file_free_security(struct file *file)
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- int error = 0;
-
- 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(current, file, FILE__GETATTR);
- break;
-
- case EXT2_IOC_SETFLAGS:
- /* fall through */
- case EXT2_IOC_SETVERSION:
- error = file_has_perm(current, file, FILE__SETATTR);
- break;
-
- /* sys_ioctl() checks */
- case FIONBIO:
- /* fall through */
- case FIOASYNC:
- error = file_has_perm(current, file, 0);
- break;
+ u32 av = 0;

- case KDSKBENT:
- case KDSKBSENT:
- error = task_has_capability(current, CAP_SYS_TTY_CONFIG);
- break;
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ av |= FILE__WRITE;
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ av |= FILE__READ;
+ if (!av)
+ av = FILE__IOCTL;

- /* default case assumes that the command will go
- * to the file's ioctl() function.
- */
- default:
- error = file_has_perm(current, file, FILE__IOCTL);
- }
- return error;
+ return file_has_perm(current, file, av);
}

static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
--
1.5.5.1

2008-07-07 16:53:16

by James Morris

[permalink] [raw]
Subject: [PATCH 07/20] SELinux: enable processes with mac_admin to get the raw inode contexts

From: Stephen Smalley <[email protected]>

Enable processes with CAP_MAC_ADMIN + mac_admin permission in policy
to get undefined contexts on inodes. This extends the support for
deferred mapping of security contexts in order to permit restorecon
and similar programs to see the raw file contexts unknown to the
system policy in order to check them.

Signed-off-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/hooks.c | 27 +++++++++++++++++++++++----
1 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 4be1563..91b666a 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2754,9 +2754,7 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
}

/*
- * Copy the in-core inode security context value to the user. If the
- * getxattr() prior to this succeeded, check to see if we need to
- * canonicalize the value to be finally returned to the user.
+ * Copy the inode security context value to the user.
*
* Permission check is handled by selinux_inode_getxattr hook.
*/
@@ -2765,12 +2763,33 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
u32 size;
int error;
char *context = NULL;
+ struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec = inode->i_security;

if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;

- error = security_sid_to_context(isec->sid, &context, &size);
+ /*
+ * If the caller has CAP_MAC_ADMIN, then get the raw context
+ * value even if it is not defined by current policy; otherwise,
+ * use the in-core value under current policy.
+ * Use the non-auditing forms of the permission checks since
+ * getxattr may be called by unprivileged processes commonly
+ * and lack of permission just means that we fall back to the
+ * in-core context value, not a denial.
+ */
+ error = secondary_ops->capable(current, CAP_MAC_ADMIN);
+ if (!error)
+ error = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY2,
+ CAPABILITY2__MAC_ADMIN,
+ 0,
+ NULL);
+ if (!error)
+ error = security_sid_to_context_force(isec->sid, &context,
+ &size);
+ else
+ error = security_sid_to_context(isec->sid, &context, &size);
if (error)
return error;
error = size;
--
1.5.5.1

2008-07-07 16:53:32

by James Morris

[permalink] [raw]
Subject: [PATCH 09/20] selinux: fix endianness bug in network node address handling

From: Stephen Smalley <[email protected]>

Fix an endianness bug in the handling of network node addresses by
SELinux. This yields no change on little endian hardware but fixes
the incorrect handling on big endian hardware. The network node
addresses are stored in network order in memory by checkpolicy, not in
cpu/host order, and thus should not have cpu_to_le32/le32_to_cpu
conversions applied upon policy write/read unlike other data in the
policy.

Bug reported by John Weeks of Sun, who noticed that binary policy
files built from the same policy source on x86 and sparc differed and
tracked it down to the ipv4 address handling in checkpolicy.

Signed-off-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/ss/policydb.c | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 84f8cc7..2391761 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1478,7 +1478,8 @@ int policydb_read(struct policydb *p, void *fp)
struct ocontext *l, *c, *newc;
struct genfs *genfs_p, *genfs, *newgenfs;
int i, j, rc;
- __le32 buf[8];
+ __le32 buf[4];
+ u32 nodebuf[8];
u32 len, len2, config, nprim, nel, nel2;
char *policydb_str;
struct policydb_compat_info *info;
@@ -1749,11 +1750,11 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
break;
case OCON_NODE:
- rc = next_entry(buf, fp, sizeof(u32) * 2);
+ rc = next_entry(nodebuf, fp, sizeof(u32) * 2);
if (rc < 0)
goto bad;
- c->u.node.addr = le32_to_cpu(buf[0]);
- c->u.node.mask = le32_to_cpu(buf[1]);
+ c->u.node.addr = nodebuf[0]; /* network order */
+ c->u.node.mask = nodebuf[1]; /* network order */
rc = context_read_and_validate(&c->context[0], p, fp);
if (rc)
goto bad;
@@ -1782,13 +1783,13 @@ int policydb_read(struct policydb *p, void *fp)
case OCON_NODE6: {
int k;

- rc = next_entry(buf, fp, sizeof(u32) * 8);
+ rc = next_entry(nodebuf, fp, sizeof(u32) * 8);
if (rc < 0)
goto bad;
for (k = 0; k < 4; k++)
- c->u.node6.addr[k] = le32_to_cpu(buf[k]);
+ c->u.node6.addr[k] = nodebuf[k];
for (k = 0; k < 4; k++)
- c->u.node6.mask[k] = le32_to_cpu(buf[k+4]);
+ c->u.node6.mask[k] = nodebuf[k+4];
if (context_read_and_validate(&c->context[0], p, fp))
goto bad;
break;
--
1.5.5.1

2008-07-07 16:53:49

by James Morris

[permalink] [raw]
Subject: [PATCH 11/20] SELinux: open code load_mutex

Open code load_mutex as suggested by Andrew Morton.

Signed-off-by: James Morris <[email protected]>
---
security/selinux/ss/services.c | 21 +++++++++------------
1 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index e8ec54d..d06df33 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -71,10 +71,7 @@ int selinux_policycap_openperm;
extern const struct selinux_class_perm selinux_class_perm;

static DEFINE_RWLOCK(policy_rwlock);
-
static DEFINE_MUTEX(load_mutex);
-#define LOAD_LOCK mutex_lock(&load_mutex)
-#define LOAD_UNLOCK mutex_unlock(&load_mutex)

static struct sidtab sidtab;
struct policydb policydb;
@@ -1456,17 +1453,17 @@ int security_load_policy(void *data, size_t len)
int rc = 0;
struct policy_file file = { data, len }, *fp = &file;

- LOAD_LOCK;
+ mutex_lock(&load_mutex);

if (!ss_initialized) {
avtab_cache_init();
if (policydb_read(&policydb, fp)) {
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);
avtab_cache_destroy();
return -EINVAL;
}
if (policydb_load_isids(&policydb, &sidtab)) {
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);
policydb_destroy(&policydb);
avtab_cache_destroy();
return -EINVAL;
@@ -1475,7 +1472,7 @@ int security_load_policy(void *data, size_t len)
if (validate_classes(&policydb)) {
printk(KERN_ERR
"SELinux: the definition of a class is incorrect\n");
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);
sidtab_destroy(&sidtab);
policydb_destroy(&policydb);
avtab_cache_destroy();
@@ -1485,7 +1482,7 @@ int security_load_policy(void *data, size_t len)
policydb_loaded_version = policydb.policyvers;
ss_initialized = 1;
seqno = ++latest_granting;
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);
selinux_complete_init();
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
@@ -1499,12 +1496,12 @@ int security_load_policy(void *data, size_t len)
#endif

if (policydb_read(&newpolicydb, fp)) {
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);
return -EINVAL;
}

if (sidtab_init(&newsidtab)) {
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);
policydb_destroy(&newpolicydb);
return -ENOMEM;
}
@@ -1552,7 +1549,7 @@ int security_load_policy(void *data, size_t len)
seqno = ++latest_granting;
policydb_loaded_version = policydb.policyvers;
write_unlock_irq(&policy_rwlock);
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);

/* Free the old policydb and SID table. */
policydb_destroy(&oldpolicydb);
@@ -1566,7 +1563,7 @@ int security_load_policy(void *data, size_t len)
return 0;

err:
- LOAD_UNLOCK;
+ mutex_unlock(&load_mutex);
sidtab_destroy(&newsidtab);
policydb_destroy(&newpolicydb);
return rc;
--
1.5.5.1

2008-07-07 16:54:13

by James Morris

[permalink] [raw]
Subject: [PATCH 16/20] SELinux: more user friendly unknown handling printk

From: Eric Paris <[email protected]>

I've gotten complaints and reports about people not understanding the
meaning of the current unknown class/perm handling the kernel emits on
every policy load. Hopefully this will make make it clear to everyone
the meaning of the message and won't waste a printk the user won't care
about anyway on systems where the kernel and the policy agree on
everything.

Signed-off-by: Eric Paris <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/selinuxfs.c | 5 -----
security/selinux/ss/services.c | 7 +++++++
2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 07a5db6..69c9dcc 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -356,11 +356,6 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
length = count;

out1:
-
- printk(KERN_INFO "SELinux: policy loaded with handle_unknown=%s\n",
- (security_get_reject_unknown() ? "reject" :
- (security_get_allow_unknown() ? "allow" : "deny")));
-
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
"policy loaded auid=%u ses=%u",
audit_get_loginuid(current),
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 04c0b70..b52f923 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1171,6 +1171,7 @@ static int validate_classes(struct policydb *p)
const struct selinux_class_perm *kdefs = &selinux_class_perm;
const char *def_class, *def_perm, *pol_class;
struct symtab *perms;
+ bool print_unknown_handle = 0;

if (p->allow_unknown) {
u32 num_classes = kdefs->cts_len;
@@ -1191,6 +1192,7 @@ static int validate_classes(struct policydb *p)
return -EINVAL;
if (p->allow_unknown)
p->undefined_perms[i-1] = ~0U;
+ print_unknown_handle = 1;
continue;
}
pol_class = p->p_class_val_to_name[i-1];
@@ -1220,6 +1222,7 @@ static int validate_classes(struct policydb *p)
return -EINVAL;
if (p->allow_unknown)
p->undefined_perms[class_val-1] |= perm_val;
+ print_unknown_handle = 1;
continue;
}
perdatum = hashtab_search(perms->table, def_perm);
@@ -1267,6 +1270,7 @@ static int validate_classes(struct policydb *p)
return -EINVAL;
if (p->allow_unknown)
p->undefined_perms[class_val-1] |= (1 << j);
+ print_unknown_handle = 1;
continue;
}
perdatum = hashtab_search(perms->table, def_perm);
@@ -1284,6 +1288,9 @@ static int validate_classes(struct policydb *p)
}
}
}
+ if (print_unknown_handle)
+ printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n",
+ (security_get_allow_unknown() ? "allowed" : "denied"));
return 0;
}

--
1.5.5.1

2008-07-07 16:54:46

by James Morris

[permalink] [raw]
Subject: [PATCH 02/20] SELinux: fix sleeping allocation in security_context_to_sid

From: Stephen Smalley <[email protected]>

Fix a sleeping function called from invalid context bug by moving allocation
to the callers prior to taking the policy rdlock.

Signed-off-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/ss/services.c | 70 +++++++++++++++++++++++-----------------
1 files changed, 40 insertions(+), 30 deletions(-)

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index b86ac9d..2d5e5a3 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -730,15 +730,16 @@ int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len)
return security_sid_to_context_core(sid, scontext, scontext_len, 1);
}

+/*
+ * Caveat: Mutates scontext.
+ */
static int string_to_context_struct(struct policydb *pol,
struct sidtab *sidtabp,
- const char *scontext,
+ char *scontext,
u32 scontext_len,
struct context *ctx,
- u32 def_sid,
- gfp_t gfp_flags)
+ u32 def_sid)
{
- char *scontext2 = NULL;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
@@ -747,19 +748,10 @@ static int string_to_context_struct(struct policydb *pol,

context_init(ctx);

- /* Copy the string so that we can modify the copy as we parse it. */
- scontext2 = kmalloc(scontext_len+1, gfp_flags);
- if (!scontext2) {
- rc = -ENOMEM;
- goto out;
- }
- memcpy(scontext2, scontext, scontext_len);
- scontext2[scontext_len] = 0;
-
/* Parse the security context. */

rc = -EINVAL;
- scontextp = (char *) scontext2;
+ scontextp = (char *) scontext;

/* Extract the user. */
p = scontextp;
@@ -809,7 +801,7 @@ static int string_to_context_struct(struct policydb *pol,
if (rc)
goto out;

- if ((p - scontext2) < scontext_len) {
+ if ((p - scontext) < scontext_len) {
rc = -EINVAL;
goto out;
}
@@ -822,7 +814,6 @@ static int string_to_context_struct(struct policydb *pol,
}
rc = 0;
out:
- kfree(scontext2);
return rc;
}

@@ -830,6 +821,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
u32 *sid, u32 def_sid, gfp_t gfp_flags,
int force)
{
+ char *scontext2, *str = NULL;
struct context context;
int rc = 0;

@@ -839,27 +831,38 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
for (i = 1; i < SECINITSID_NUM; i++) {
if (!strcmp(initial_sid_to_string[i], scontext)) {
*sid = i;
- goto out;
+ return 0;
}
}
*sid = SECINITSID_KERNEL;
- goto out;
+ return 0;
}
*sid = SECSID_NULL;

+ /* Copy the string so that we can modify the copy as we parse it. */
+ scontext2 = kmalloc(scontext_len+1, gfp_flags);
+ if (!scontext2)
+ return -ENOMEM;
+ memcpy(scontext2, scontext, scontext_len);
+ scontext2[scontext_len] = 0;
+
+ if (force) {
+ /* Save another copy for storing in uninterpreted form */
+ str = kstrdup(scontext2, gfp_flags);
+ if (!str) {
+ kfree(scontext2);
+ return -ENOMEM;
+ }
+ }
+
POLICY_RDLOCK;
rc = string_to_context_struct(&policydb, &sidtab,
- scontext, scontext_len,
- &context, def_sid, gfp_flags);
+ scontext2, scontext_len,
+ &context, def_sid);
if (rc == -EINVAL && force) {
- context.str = kmalloc(scontext_len+1, gfp_flags);
- if (!context.str) {
- rc = -ENOMEM;
- goto out;
- }
- memcpy(context.str, scontext, scontext_len);
- context.str[scontext_len] = 0;
+ context.str = str;
context.len = scontext_len;
+ str = NULL;
} else if (rc)
goto out;
rc = sidtab_context_to_sid(&sidtab, &context, sid);
@@ -867,6 +870,8 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
context_destroy(&context);
out:
POLICY_RDUNLOCK;
+ kfree(scontext2);
+ kfree(str);
return rc;
}

@@ -1339,9 +1344,14 @@ static int convert_context(u32 key,

if (c->str) {
struct context ctx;
- rc = string_to_context_struct(args->newp, NULL, c->str,
- c->len, &ctx, SECSID_NULL,
- GFP_KERNEL);
+ s = kstrdup(c->str, GFP_KERNEL);
+ if (!s) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = string_to_context_struct(args->newp, NULL, s,
+ c->len, &ctx, SECSID_NULL);
+ kfree(s);
if (!rc) {
printk(KERN_INFO
"SELinux: Context %s became valid (mapped).\n",
--
1.5.5.1

2008-07-07 16:54:31

by James Morris

[permalink] [raw]
Subject: [PATCH 14/20] SELinux: drop load_mutex in security_load_policy

From: Eric Paris <[email protected]>

We used to protect against races of policy load in security_load_policy
by using the load_mutex. Since then we have added a new mutex,
sel_mutex, in sel_write_load() which is always held across all calls to
security_load_policy we are covered and can safely just drop this one.

Signed-off-by: Eric Paris <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/ss/services.c | 14 +-------------
1 files changed, 1 insertions(+), 13 deletions(-)

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index f26a8ca..543fd0f 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -71,7 +71,6 @@ int selinux_policycap_openperm;
extern const struct selinux_class_perm selinux_class_perm;

static DEFINE_RWLOCK(policy_rwlock);
-static DEFINE_MUTEX(load_mutex);

static struct sidtab sidtab;
struct policydb policydb;
@@ -1453,17 +1452,13 @@ int security_load_policy(void *data, size_t len)
int rc = 0;
struct policy_file file = { data, len }, *fp = &file;

- mutex_lock(&load_mutex);
-
if (!ss_initialized) {
avtab_cache_init();
if (policydb_read(&policydb, fp)) {
- mutex_unlock(&load_mutex);
avtab_cache_destroy();
return -EINVAL;
}
if (policydb_load_isids(&policydb, &sidtab)) {
- mutex_unlock(&load_mutex);
policydb_destroy(&policydb);
avtab_cache_destroy();
return -EINVAL;
@@ -1472,7 +1467,6 @@ int security_load_policy(void *data, size_t len)
if (validate_classes(&policydb)) {
printk(KERN_ERR
"SELinux: the definition of a class is incorrect\n");
- mutex_unlock(&load_mutex);
sidtab_destroy(&sidtab);
policydb_destroy(&policydb);
avtab_cache_destroy();
@@ -1482,7 +1476,6 @@ int security_load_policy(void *data, size_t len)
policydb_loaded_version = policydb.policyvers;
ss_initialized = 1;
seqno = ++latest_granting;
- mutex_unlock(&load_mutex);
selinux_complete_init();
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
@@ -1495,13 +1488,10 @@ int security_load_policy(void *data, size_t len)
sidtab_hash_eval(&sidtab, "sids");
#endif

- if (policydb_read(&newpolicydb, fp)) {
- mutex_unlock(&load_mutex);
+ if (policydb_read(&newpolicydb, fp))
return -EINVAL;
- }

if (sidtab_init(&newsidtab)) {
- mutex_unlock(&load_mutex);
policydb_destroy(&newpolicydb);
return -ENOMEM;
}
@@ -1549,7 +1539,6 @@ int security_load_policy(void *data, size_t len)
seqno = ++latest_granting;
policydb_loaded_version = policydb.policyvers;
write_unlock_irq(&policy_rwlock);
- mutex_unlock(&load_mutex);

/* Free the old policydb and SID table. */
policydb_destroy(&oldpolicydb);
@@ -1563,7 +1552,6 @@ int security_load_policy(void *data, size_t len)
return 0;

err:
- mutex_unlock(&load_mutex);
sidtab_destroy(&newsidtab);
policydb_destroy(&newpolicydb);
return rc;
--
1.5.5.1

2008-07-07 16:55:00

by James Morris

[permalink] [raw]
Subject: [PATCH 20/20] SELinux: allow fstype unknown to policy to use xattrs if present

From: Eric Paris <[email protected]>

Currently if a FS is mounted for which SELinux policy does not define an
fs_use_* that FS will either be genfs labeled or not labeled at all.
This decision is based on the existence of a genfscon rule in policy and
is irrespective of the capabilities of the filesystem itself. This
patch allows the kernel to check if the filesystem supports security
xattrs and if so will use those if there is no fs_use_* rule in policy.
An fstype with a no fs_use_* rule but with a genfs rule will use xattrs
if available and will follow the genfs rule.

This can be particularly interesting for things like ecryptfs which
actually overlays a real underlying FS. If we define excryptfs in
policy to use xattrs we will likely get this wrong at times, so with
this path we just don't need to define it!

Overlay ecryptfs on top of NFS with no xattr support:
SELinux: initialized (dev ecryptfs, type ecryptfs), uses genfs_contexts
Overlay ecryptfs on top of ext4 with xattr support:
SELinux: initialized (dev ecryptfs, type ecryptfs), uses xattr

It is also useful as the kernel adds new FS we don't need to add them in
policy if they support xattrs and that is how we want to handle them.

Signed-off-by: Eric Paris <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/hooks.c | 22 +++++++++++++++++-----
security/selinux/include/security.h | 2 +-
security/selinux/ss/services.c | 27 +++++++++++++++++++--------
3 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 4130d64..85f74f6 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -556,13 +556,15 @@ static int selinux_set_mnt_opts(struct super_block *sb,
struct task_security_struct *tsec = current->security;
struct superblock_security_struct *sbsec = sb->s_security;
const char *name = sb->s_type->name;
- struct inode *inode = sbsec->sb->s_root->d_inode;
- struct inode_security_struct *root_isec = inode->i_security;
+ struct dentry *root = sb->s_root;
+ struct inode *root_inode = root->d_inode;
+ struct inode_security_struct *root_isec = root_inode->i_security;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
u32 defcontext_sid = 0;
char **mount_options = opts->mnt_opts;
int *flags = opts->mnt_opts_flags;
int num_opts = opts->num_mnt_opts;
+ bool can_xattr = false;

mutex_lock(&sbsec->lock);

@@ -666,14 +668,24 @@ static int selinux_set_mnt_opts(struct super_block *sb,
goto out;
}

- if (strcmp(sb->s_type->name, "proc") == 0)
+ if (strcmp(name, "proc") == 0)
sbsec->proc = 1;

+ /*
+ * test if the fs supports xattrs, fs_use might make use of this if the
+ * fs has no definition in policy.
+ */
+ if (root_inode->i_op->getxattr) {
+ rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+ if (rc >= 0 || rc == -ENODATA)
+ can_xattr = true;
+ }
+
/* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ rc = security_fs_use(name, &sbsec->behavior, &sbsec->sid, can_xattr);
if (rc) {
printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
- __func__, sb->s_type->name, rc);
+ __func__, name, rc);
goto out;
}

diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 7c54300..44cba2e 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -136,7 +136,7 @@ int security_get_allow_unknown(void);
#define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */

int security_fs_use(const char *fstype, unsigned int *behavior,
- u32 *sid);
+ u32 *sid, bool can_xattr);

int security_genfs_sid(const char *fstype, char *name, u16 sclass,
u32 *sid);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index b52f923..8e42da1 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1934,7 +1934,8 @@ out:
int security_fs_use(
const char *fstype,
unsigned int *behavior,
- u32 *sid)
+ u32 *sid,
+ bool can_xattr)
{
int rc = 0;
struct ocontext *c;
@@ -1948,6 +1949,7 @@ int security_fs_use(
c = c->next;
}

+ /* look for labeling behavior defined in policy */
if (c) {
*behavior = c->v.behavior;
if (!c->sid[0]) {
@@ -1958,14 +1960,23 @@ int security_fs_use(
goto out;
}
*sid = c->sid[0];
+ goto out;
+ }
+
+ /* labeling behavior not in policy, use xattrs if possible */
+ if (can_xattr) {
+ *behavior = SECURITY_FS_USE_XATTR;
+ *sid = SECINITSID_FS;
+ goto out;
+ }
+
+ /* no behavior in policy and can't use xattrs, try GENFS */
+ rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
+ if (rc) {
+ *behavior = SECURITY_FS_USE_NONE;
+ rc = 0;
} else {
- rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
- if (rc) {
- *behavior = SECURITY_FS_USE_NONE;
- rc = 0;
- } else {
- *behavior = SECURITY_FS_USE_GENFS;
- }
+ *behavior = SECURITY_FS_USE_GENFS;
}

out:
--
1.5.5.1

2008-07-07 16:55:44

by James Morris

[permalink] [raw]
Subject: [PATCH 03/20] SELinux: keep the code clean formating and syntax

From: Eric Paris <[email protected]>

Formatting and syntax changes

whitespace, tabs to spaces, trailing space
put open { on same line as struct def
remove unneeded {} after if statements
change printk("Lu") to printk("llu")
convert asm/uaccess.h to linux/uaacess.h includes
remove unnecessary asm/bug.h includes
convert all users of simple_strtol to strict_strtol

Signed-off-by: Eric Paris <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/hooks.c | 18 +++++++++++-------
security/selinux/include/audit.h | 4 ++--
security/selinux/include/avc.h | 15 +++++++--------
security/selinux/netnode.c | 1 -
security/selinux/netport.c | 3 +--
security/selinux/selinuxfs.c | 10 +++++++---
security/selinux/ss/avtab.c | 2 +-
security/selinux/ss/mls.c | 8 ++++----
security/selinux/ss/services.c | 4 ++--
9 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 59c6e98..eca70f4 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -53,7 +53,7 @@
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
#include <net/net_namespace.h>
#include <net/netlabel.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/ioctls.h>
#include <asm/atomic.h>
#include <linux/bitops.h>
@@ -104,7 +104,9 @@ int selinux_enforcing;

static int __init enforcing_setup(char *str)
{
- selinux_enforcing = simple_strtol(str, NULL, 0);
+ unsigned long enforcing;
+ if (!strict_strtoul(str, 0, &enforcing))
+ selinux_enforcing = enforcing ? 1 : 0;
return 1;
}
__setup("enforcing=", enforcing_setup);
@@ -115,7 +117,9 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;

static int __init selinux_enabled_setup(char *str)
{
- selinux_enabled = simple_strtol(str, NULL, 0);
+ unsigned long enabled;
+ if (!strict_strtoul(str, 0, &enabled))
+ selinux_enabled = enabled ? 1 : 0;
return 1;
}
__setup("selinux=", selinux_enabled_setup);
@@ -594,7 +598,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
*/
if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
&& (num_opts == 0))
- goto out;
+ goto out;

/*
* parse the mount options, check if they are valid sids.
@@ -2695,7 +2699,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
}

static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size,
+ const void *value, size_t size,
int flags)
{
struct inode *inode = dentry->d_inode;
@@ -5390,7 +5394,7 @@ static struct security_operations selinux_ops = {
.inode_listsecurity = selinux_inode_listsecurity,
.inode_need_killpriv = selinux_inode_need_killpriv,
.inode_killpriv = selinux_inode_killpriv,
- .inode_getsecid = selinux_inode_getsecid,
+ .inode_getsecid = selinux_inode_getsecid,

.file_permission = selinux_file_permission,
.file_alloc_security = selinux_file_alloc_security,
@@ -5431,7 +5435,7 @@ static struct security_operations selinux_ops = {
.task_to_inode = selinux_task_to_inode,

.ipc_permission = selinux_ipc_permission,
- .ipc_getsecid = selinux_ipc_getsecid,
+ .ipc_getsecid = selinux_ipc_getsecid,

.msg_msg_alloc_security = selinux_msg_msg_alloc_security,
.msg_msg_free_security = selinux_msg_msg_free_security,
diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h
index 6c8b9ef..1bdf973 100644
--- a/security/selinux/include/audit.h
+++ b/security/selinux/include/audit.h
@@ -1,7 +1,7 @@
/*
* SELinux support for the Audit LSM hooks
*
- * Most of below header was moved from include/linux/selinux.h which
+ * Most of below header was moved from include/linux/selinux.h which
* is released under below copyrights:
*
* Author: James Morris <[email protected]>
@@ -52,7 +52,7 @@ void selinux_audit_rule_free(void *rule);
* -errno on failure.
*/
int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule,
- struct audit_context *actx);
+ struct audit_context *actx);

/**
* selinux_audit_rule_known - check to see if rule contains selinux fields.
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 8e23d7a..7b9769f 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -75,13 +75,12 @@ struct avc_audit_data {

/* Initialize an AVC audit data structure. */
#define AVC_AUDIT_DATA_INIT(_d,_t) \
- { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
+ { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }

/*
* AVC statistics
*/
-struct avc_cache_stats
-{
+struct avc_cache_stats {
unsigned int lookups;
unsigned int hits;
unsigned int misses;
@@ -97,8 +96,8 @@ struct avc_cache_stats
void __init avc_init(void);

void avc_audit(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct av_decision *avd, int result, struct avc_audit_data *auditdata);
+ 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,
@@ -107,8 +106,8 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
struct av_decision *avd);

int avc_has_perm(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct avc_audit_data *auditdata);
+ u16 tclass, u32 requested,
+ struct avc_audit_data *auditdata);

u32 avc_policy_seqno(void);

@@ -122,7 +121,7 @@ u32 avc_policy_seqno(void);
#define AVC_CALLBACK_AUDITDENY_DISABLE 128

int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
- u16 tclass, u32 perms,
+ u16 tclass, u32 perms,
u32 *out_retained),
u32 events, u32 ssid, u32 tsid,
u16 tclass, u32 perms);
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index b6ccd09..7100072 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -38,7 +38,6 @@
#include <linux/ipv6.h>
#include <net/ip.h>
#include <net/ipv6.h>
-#include <asm/bug.h>

#include "netnode.h"
#include "objsec.h"
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 90b4cff..fe7fba6 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -37,7 +37,6 @@
#include <linux/ipv6.h>
#include <net/ip.h>
#include <net/ipv6.h>
-#include <asm/bug.h>

#include "netport.h"
#include "objsec.h"
@@ -272,7 +271,7 @@ static __init int sel_netport_init(void)
}

ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET,
- SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
if (ret != 0)
panic("avc_add_callback() failed, error %d\n", ret);

diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index ac1ccc1..07a5db6 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -27,7 +27,7 @@
#include <linux/seq_file.h>
#include <linux/percpu.h>
#include <linux/audit.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>

/* selinuxfs pseudo filesystem for exporting the security policy API.
Based on the proc code and the fs/nfsd/nfsctl.c code. */
@@ -57,14 +57,18 @@ int selinux_compat_net = SELINUX_COMPAT_NET_VALUE;

static int __init checkreqprot_setup(char *str)
{
- selinux_checkreqprot = simple_strtoul(str, NULL, 0) ? 1 : 0;
+ unsigned long checkreqprot;
+ if (!strict_strtoul(str, 0, &checkreqprot))
+ selinux_checkreqprot = checkreqprot ? 1 : 0;
return 1;
}
__setup("checkreqprot=", checkreqprot_setup);

static int __init selinux_compat_net_setup(char *str)
{
- selinux_compat_net = simple_strtoul(str, NULL, 0) ? 1 : 0;
+ unsigned long compat_net;
+ if (!strict_strtoul(str, 0, &compat_net))
+ selinux_compat_net = compat_net ? 1 : 0;
return 1;
}
__setup("selinux_compat_net=", selinux_compat_net_setup);
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 9e66263..a1be97f 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -311,7 +311,7 @@ void avtab_hash_eval(struct avtab *h, char *tag)
}

printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, "
- "longest chain length %d sum of chain length^2 %Lu\n",
+ "longest chain length %d sum of chain length^2 %llu\n",
tag, h->nel, slots_used, h->nslot, max_chain_len,
chain2_len_sum);
}
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index a6ca058..77d745d 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -437,13 +437,13 @@ int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
struct mls_level *usercon_clr = &(usercon->range.level[1]);

/* Honor the user's default level if we can */
- if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
+ if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
*usercon_sen = *user_def;
- } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
+ else if (mls_level_between(fromcon_sen, user_def, user_clr))
*usercon_sen = *fromcon_sen;
- } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
+ else if (mls_level_between(fromcon_clr, user_low, user_def))
*usercon_sen = *user_low;
- } else
+ else
return -EINVAL;

/* Lower the clearance of available contexts
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 2d5e5a3..0696aad 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2531,7 +2531,7 @@ int selinux_audit_rule_known(struct audit_krule *rule)
}

int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
- struct audit_context *actx)
+ struct audit_context *actx)
{
struct context *ctxt;
struct mls_level *level;
@@ -2645,7 +2645,7 @@ out:
static int (*aurule_callback)(void) = audit_update_lsm_rules;

static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
- u16 class, u32 perms, u32 *retained)
+ u16 class, u32 perms, u32 *retained)
{
int err = 0;

--
1.5.5.1

2008-07-07 16:56:00

by James Morris

[permalink] [raw]
Subject: [PATCH 10/20] SELinux: open code policy_rwlock

Open code policy_rwlock, as suggested by Andrew Morton.

Signed-off-by: James Morris <[email protected]>
Acked-by: Stephen Smalley <[email protected]>
---
security/selinux/ss/services.c | 108 +++++++++++++++++++---------------------
1 files changed, 52 insertions(+), 56 deletions(-)

diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 0696aad..e8ec54d 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -71,10 +71,6 @@ int selinux_policycap_openperm;
extern const struct selinux_class_perm selinux_class_perm;

static DEFINE_RWLOCK(policy_rwlock);
-#define POLICY_RDLOCK read_lock(&policy_rwlock)
-#define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
-#define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
-#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)

static DEFINE_MUTEX(load_mutex);
#define LOAD_LOCK mutex_lock(&load_mutex)
@@ -429,7 +425,7 @@ int security_permissive_sid(u32 sid)
u32 type;
int rc;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

context = sidtab_search(&sidtab, sid);
BUG_ON(!context);
@@ -441,7 +437,7 @@ int security_permissive_sid(u32 sid)
*/
rc = ebitmap_get_bit(&policydb.permissive_map, type);

- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -486,7 +482,7 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
if (!ss_initialized)
return 0;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

/*
* Remap extended Netlink classes for old policy versions.
@@ -543,7 +539,7 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
}

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -578,7 +574,7 @@ int security_compute_av(u32 ssid,
return 0;
}

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

scontext = sidtab_search(&sidtab, ssid);
if (!scontext) {
@@ -598,7 +594,7 @@ int security_compute_av(u32 ssid,
rc = context_struct_compute_av(scontext, tcontext, tclass,
requested, avd);
out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -691,7 +687,7 @@ static int security_sid_to_context_core(u32 sid, char **scontext,
rc = -EINVAL;
goto out;
}
- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);
if (force)
context = sidtab_search_force(&sidtab, sid);
else
@@ -704,7 +700,7 @@ static int security_sid_to_context_core(u32 sid, char **scontext,
}
rc = context_struct_to_string(context, scontext, scontext_len);
out_unlock:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
out:
return rc;

@@ -855,7 +851,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
}
}

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);
rc = string_to_context_struct(&policydb, &sidtab,
scontext2, scontext_len,
&context, def_sid);
@@ -869,7 +865,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
if (rc)
context_destroy(&context);
out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
kfree(scontext2);
kfree(str);
return rc;
@@ -981,7 +977,7 @@ static int security_compute_sid(u32 ssid,

context_init(&newcontext);

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

scontext = sidtab_search(&sidtab, ssid);
if (!scontext) {
@@ -1086,7 +1082,7 @@ static int security_compute_sid(u32 ssid,
/* Obtain the sid for the context. */
rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
out_unlock:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
context_destroy(&newcontext);
out:
return rc;
@@ -1549,13 +1545,13 @@ int security_load_policy(void *data, size_t len)
sidtab_set(&oldsidtab, &sidtab);

/* Install the new policydb and SID table. */
- POLICY_WRLOCK;
+ write_lock_irq(&policy_rwlock);
memcpy(&policydb, &newpolicydb, sizeof policydb);
sidtab_set(&sidtab, &newsidtab);
security_load_policycaps();
seqno = ++latest_granting;
policydb_loaded_version = policydb.policyvers;
- POLICY_WRUNLOCK;
+ write_unlock_irq(&policy_rwlock);
LOAD_UNLOCK;

/* Free the old policydb and SID table. */
@@ -1588,7 +1584,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
struct ocontext *c;
int rc = 0;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

c = policydb.ocontexts[OCON_PORT];
while (c) {
@@ -1613,7 +1609,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
}

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -1627,7 +1623,7 @@ int security_netif_sid(char *name, u32 *if_sid)
int rc = 0;
struct ocontext *c;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

c = policydb.ocontexts[OCON_NETIF];
while (c) {
@@ -1654,7 +1650,7 @@ int security_netif_sid(char *name, u32 *if_sid)
*if_sid = SECINITSID_NETIF;

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -1686,7 +1682,7 @@ int security_node_sid(u16 domain,
int rc = 0;
struct ocontext *c;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

switch (domain) {
case AF_INET: {
@@ -1741,7 +1737,7 @@ int security_node_sid(u16 domain,
}

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -1780,7 +1776,7 @@ int security_get_user_sids(u32 fromsid,
if (!ss_initialized)
goto out;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

context_init(&usercon);

@@ -1833,7 +1829,7 @@ int security_get_user_sids(u32 fromsid,
}

out_unlock:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
if (rc || !mynel) {
kfree(mysids);
goto out;
@@ -1886,7 +1882,7 @@ int security_genfs_sid(const char *fstype,
while (path[0] == '/' && path[1] == '/')
path++;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
cmp = strcmp(fstype, genfs->fstype);
@@ -1923,7 +1919,7 @@ int security_genfs_sid(const char *fstype,

*sid = c->sid[0];
out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -1941,7 +1937,7 @@ int security_fs_use(
int rc = 0;
struct ocontext *c;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

c = policydb.ocontexts[OCON_FSUSE];
while (c) {
@@ -1971,7 +1967,7 @@ int security_fs_use(
}

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -1979,7 +1975,7 @@ int security_get_bools(int *len, char ***names, int **values)
{
int i, rc = -ENOMEM;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);
*names = NULL;
*values = NULL;

@@ -2009,7 +2005,7 @@ int security_get_bools(int *len, char ***names, int **values)
}
rc = 0;
out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
err:
if (*names) {
@@ -2027,7 +2023,7 @@ int security_set_bools(int len, int *values)
int lenp, seqno = 0;
struct cond_node *cur;

- POLICY_WRLOCK;
+ write_lock_irq(&policy_rwlock);

lenp = policydb.p_bools.nprim;
if (len != lenp) {
@@ -2061,7 +2057,7 @@ int security_set_bools(int len, int *values)
seqno = ++latest_granting;

out:
- POLICY_WRUNLOCK;
+ write_unlock_irq(&policy_rwlock);
if (!rc) {
avc_ss_reset(seqno);
selnl_notify_policyload(seqno);
@@ -2075,7 +2071,7 @@ int security_get_bool_value(int bool)
int rc = 0;
int len;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

len = policydb.p_bools.nprim;
if (bool >= len) {
@@ -2085,7 +2081,7 @@ int security_get_bool_value(int bool)

rc = policydb.bool_val_to_struct[bool]->state;
out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -2140,7 +2136,7 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)

context_init(&newcon);

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);
context1 = sidtab_search(&sidtab, sid);
if (!context1) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
@@ -2182,7 +2178,7 @@ bad:
}

out_unlock:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
context_destroy(&newcon);
out:
return rc;
@@ -2239,7 +2235,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
return 0;
}

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
if (!nlbl_ctx) {
@@ -2258,7 +2254,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);

out_slowpath:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
if (rc == 0)
/* at present NetLabel SIDs/labels really only carry MLS
* information so if the MLS portion of the NetLabel SID
@@ -2288,7 +2284,7 @@ int security_get_classes(char ***classes, int *nclasses)
{
int rc = -ENOMEM;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

*nclasses = policydb.p_classes.nprim;
*classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
@@ -2305,7 +2301,7 @@ int security_get_classes(char ***classes, int *nclasses)
}

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}

@@ -2327,7 +2323,7 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
int rc = -ENOMEM, i;
struct class_datum *match;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

match = hashtab_search(policydb.p_classes.table, class);
if (!match) {
@@ -2355,11 +2351,11 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
goto err;

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;

err:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
for (i = 0; i < *nperms; i++)
kfree((*perms)[i]);
kfree(*perms);
@@ -2390,9 +2386,9 @@ int security_policycap_supported(unsigned int req_cap)
{
int rc;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);
rc = ebitmap_get_bit(&policydb.policycaps, req_cap);
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);

return rc;
}
@@ -2456,7 +2452,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)

context_init(&tmprule->au_ctxt);

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

tmprule->au_seqno = latest_granting;

@@ -2493,7 +2489,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
break;
}

- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);

if (rc) {
selinux_audit_rule_free(tmprule);
@@ -2544,7 +2540,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
return -ENOENT;
}

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

if (rule->au_seqno < latest_granting) {
audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
@@ -2638,7 +2634,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
}

out:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return match;
}

@@ -2726,7 +2722,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
return 0;
}

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);

if (secattr->flags & NETLBL_SECATTR_CACHE) {
*sid = *(u32 *)secattr->cache->data;
@@ -2771,7 +2767,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
}

netlbl_secattr_to_sid_return:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
netlbl_secattr_to_sid_return_cleanup:
ebitmap_destroy(&ctx_new.range.level[0].cat);
@@ -2796,7 +2792,7 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
if (!ss_initialized)
return 0;

- POLICY_RDLOCK;
+ read_lock(&policy_rwlock);
ctx = sidtab_search(&sidtab, sid);
if (ctx == NULL)
goto netlbl_sid_to_secattr_failure;
@@ -2807,12 +2803,12 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
rc = mls_export_netlbl_cat(ctx, secattr);
if (rc != 0)
goto netlbl_sid_to_secattr_failure;
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);

return 0;

netlbl_sid_to_secattr_failure:
- POLICY_RDUNLOCK;
+ read_unlock(&policy_rwlock);
return rc;
}
#endif /* CONFIG_NETLABEL */
--
1.5.5.1

2008-07-07 16:56:24

by James Morris

[permalink] [raw]
Subject: [PATCH 06/20] Security: split proc ptrace checking into read vs. attach

From: Stephen Smalley <[email protected]>

Enable security modules to distinguish reading of process state via
proc from full ptrace access by renaming ptrace_may_attach to
ptrace_may_access and adding a mode argument indicating whether only
read access or full attach access is requested. This allows security
modules to permit access to reading process state without granting
full ptrace access. The base DAC/capability checking remains unchanged.

Read access to /proc/pid/mem continues to apply a full ptrace attach
check since check_mem_permission() already requires the current task
to already be ptracing the target. The other ptrace checks within
proc for elements like environ, maps, and fds are changed to pass the
read mode instead of attach.

In the SELinux case, we model such reading of process state as a
reading of a proc file labeled with the target process' label. This
enables SELinux policy to permit such reading of process state without
permitting control or manipulation of the target process, as there are
a number of cases where programs probe for such information via proc
but do not need to be able to control the target (e.g. procps,
lsof, PolicyKit, ConsoleKit). At present we have to choose between
allowing full ptrace in policy (more permissive than required/desired)
or breaking functionality (or in some cases just silencing the denials
via dontaudit rules but this can hide genuine attacks).

This version of the patch incorporates comments from Casey Schaufler
(change/replace existing ptrace_may_attach interface, pass access
mode), and Chris Wright (provide greater consistency in the checking).

Note that like their predecessors __ptrace_may_attach and
ptrace_may_attach, the __ptrace_may_access and ptrace_may_access
interfaces use different return value conventions from each other (0
or -errno vs. 1 or 0). I retained this difference to avoid any
changes to the caller logic but made the difference clearer by
changing the latter interface to return a bool rather than an int and
by adding a comment about it to ptrace.h for any future callers.

Signed-off-by: Stephen Smalley <[email protected]>
Acked-by: Chris Wright <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
fs/proc/base.c | 9 +++++----
fs/proc/task_mmu.c | 6 +++---
fs/proc/task_nommu.c | 2 +-
include/linux/ptrace.h | 8 ++++++--
include/linux/security.h | 16 +++++++++++-----
kernel/ptrace.c | 15 ++++++++-------
security/commoncap.c | 3 ++-
security/dummy.c | 3 ++-
security/security.c | 5 +++--
security/selinux/hooks.c | 13 +++++++++++--
security/smack/smack_lsm.c | 5 +++--
11 files changed, 55 insertions(+), 30 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 3b45537..58c3e6a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -233,7 +233,7 @@ static int check_mem_permission(struct task_struct *task)
*/
if (task->parent == current && (task->ptrace & PT_PTRACED) &&
task_is_stopped_or_traced(task) &&
- ptrace_may_attach(task))
+ ptrace_may_access(task, PTRACE_MODE_ATTACH))
return 0;

/*
@@ -251,7 +251,8 @@ struct mm_struct *mm_for_maps(struct task_struct *task)
task_lock(task);
if (task->mm != mm)
goto out;
- if (task->mm != current->mm && __ptrace_may_attach(task) < 0)
+ if (task->mm != current->mm &&
+ __ptrace_may_access(task, PTRACE_MODE_READ) < 0)
goto out;
task_unlock(task);
return mm;
@@ -518,7 +519,7 @@ static int proc_fd_access_allowed(struct inode *inode)
*/
task = get_proc_task(inode);
if (task) {
- allowed = ptrace_may_attach(task);
+ allowed = ptrace_may_access(task, PTRACE_MODE_READ);
put_task_struct(task);
}
return allowed;
@@ -904,7 +905,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
if (!task)
goto out_no_task;

- if (!ptrace_may_attach(task))
+ if (!ptrace_may_access(task, PTRACE_MODE_READ))
goto out;

ret = -ENOMEM;
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index c492449..164bd9f 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -210,7 +210,7 @@ static int show_map(struct seq_file *m, void *v)
dev_t dev = 0;
int len;

- if (maps_protect && !ptrace_may_attach(task))
+ if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
return -EACCES;

if (file) {
@@ -646,7 +646,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
goto out;

ret = -EACCES;
- if (!ptrace_may_attach(task))
+ if (!ptrace_may_access(task, PTRACE_MODE_READ))
goto out_task;

ret = -EINVAL;
@@ -747,7 +747,7 @@ static int show_numa_map_checked(struct seq_file *m, void *v)
struct proc_maps_private *priv = m->private;
struct task_struct *task = priv->task;

- if (maps_protect && !ptrace_may_attach(task))
+ if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
return -EACCES;

return show_numa_map(m, v);
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 4b4f9cc..5d84e71 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -113,7 +113,7 @@ static int show_map(struct seq_file *m, void *_vml)
struct proc_maps_private *priv = m->private;
struct task_struct *task = priv->task;

- if (maps_protect && !ptrace_may_attach(task))
+ if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
return -EACCES;

return nommu_vma_show(m, vml->vma);
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index f98501b..c6f5f9d 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -95,8 +95,12 @@ extern void __ptrace_link(struct task_struct *child,
struct task_struct *new_parent);
extern void __ptrace_unlink(struct task_struct *child);
extern void ptrace_untrace(struct task_struct *child);
-extern int ptrace_may_attach(struct task_struct *task);
-extern int __ptrace_may_attach(struct task_struct *task);
+#define PTRACE_MODE_READ 1
+#define PTRACE_MODE_ATTACH 2
+/* Returns 0 on success, -errno on denial. */
+extern int __ptrace_may_access(struct task_struct *task, unsigned int mode);
+/* Returns true on success, false on denial. */
+extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);

static inline int ptrace_reparented(struct task_struct *child)
{
diff --git a/include/linux/security.h b/include/linux/security.h
index 50737c7..62bd80c 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -46,7 +46,8 @@ struct audit_krule;
*/
extern int cap_capable(struct task_struct *tsk, int cap);
extern int cap_settime(struct timespec *ts, struct timezone *tz);
-extern int cap_ptrace(struct task_struct *parent, struct task_struct *child);
+extern int cap_ptrace(struct task_struct *parent, struct task_struct *child,
+ unsigned int mode);
extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
@@ -1170,6 +1171,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* attributes would be changed by the execve.
* @parent contains the task_struct structure for parent process.
* @child contains the task_struct structure for child process.
+ * @mode contains the PTRACE_MODE flags indicating the form of access.
* Return 0 if permission is granted.
* @capget:
* Get the @effective, @inheritable, and @permitted capability sets for
@@ -1295,7 +1297,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
struct security_operations {
char name[SECURITY_NAME_MAX + 1];

- int (*ptrace) (struct task_struct *parent, struct task_struct *child);
+ int (*ptrace) (struct task_struct *parent, struct task_struct *child,
+ unsigned int mode);
int (*capget) (struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted);
@@ -1573,7 +1576,8 @@ extern struct dentry *securityfs_create_dir(const char *name, struct dentry *par
extern void securityfs_remove(struct dentry *dentry);

/* Security operations */
-int security_ptrace(struct task_struct *parent, struct task_struct *child);
+int security_ptrace(struct task_struct *parent, struct task_struct *child,
+ unsigned int mode);
int security_capget(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
@@ -1755,9 +1759,11 @@ static inline int security_init(void)
return 0;
}

-static inline int security_ptrace(struct task_struct *parent, struct task_struct *child)
+static inline int security_ptrace(struct task_struct *parent,
+ struct task_struct *child,
+ unsigned int mode)
{
- return cap_ptrace(parent, child);
+ return cap_ptrace(parent, child, mode);
}

static inline int security_capget(struct task_struct *target,
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 6c19e94..e337390 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -121,7 +121,7 @@ int ptrace_check_attach(struct task_struct *child, int kill)
return ret;
}

-int __ptrace_may_attach(struct task_struct *task)
+int __ptrace_may_access(struct task_struct *task, unsigned int mode)
{
/* May we inspect the given task?
* This check is used both for attaching with ptrace
@@ -148,16 +148,16 @@ int __ptrace_may_attach(struct task_struct *task)
if (!dumpable && !capable(CAP_SYS_PTRACE))
return -EPERM;

- return security_ptrace(current, task);
+ return security_ptrace(current, task, mode);
}

-int ptrace_may_attach(struct task_struct *task)
+bool ptrace_may_access(struct task_struct *task, unsigned int mode)
{
int err;
task_lock(task);
- err = __ptrace_may_attach(task);
+ err = __ptrace_may_access(task, mode);
task_unlock(task);
- return !err;
+ return (!err ? true : false);
}

int ptrace_attach(struct task_struct *task)
@@ -195,7 +195,7 @@ repeat:
/* the same process cannot be attached many times */
if (task->ptrace & PT_PTRACED)
goto bad;
- retval = __ptrace_may_attach(task);
+ retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
if (retval)
goto bad;

@@ -494,7 +494,8 @@ int ptrace_traceme(void)
*/
task_lock(current);
if (!(current->ptrace & PT_PTRACED)) {
- ret = security_ptrace(current->parent, current);
+ ret = security_ptrace(current->parent, current,
+ PTRACE_MODE_ATTACH);
/*
* Set the ptrace bit in the process ptrace flags.
*/
diff --git a/security/commoncap.c b/security/commoncap.c
index 33d3433..0b6537a 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -63,7 +63,8 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
return 0;
}

-int cap_ptrace (struct task_struct *parent, struct task_struct *child)
+int cap_ptrace (struct task_struct *parent, struct task_struct *child,
+ unsigned int mode)
{
/* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
if (!cap_issubset(child->cap_permitted, parent->cap_permitted) &&
diff --git a/security/dummy.c b/security/dummy.c
index b891688..1db712d 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -30,7 +30,8 @@
#include <linux/prctl.h>
#include <linux/securebits.h>

-static int dummy_ptrace (struct task_struct *parent, struct task_struct *child)
+static int dummy_ptrace (struct task_struct *parent, struct task_struct *child,
+ unsigned int mode)
{
return 0;
}
diff --git a/security/security.c b/security/security.c
index 59838a9..c4507ce 100644
--- a/security/security.c
+++ b/security/security.c
@@ -161,9 +161,10 @@ int mod_reg_security(const char *name, struct security_operations *ops)

/* Security operations */

-int security_ptrace(struct task_struct *parent, struct task_struct *child)
+int security_ptrace(struct task_struct *parent, struct task_struct *child,
+ unsigned int mode)
{
- return security_ops->ptrace(parent, child);
+ return security_ops->ptrace(parent, child, mode);
}

int security_capget(struct task_struct *target,
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index eca70f4..4be1563 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1686,14 +1686,23 @@ static inline u32 file_to_av(struct file *file)

/* Hook functions begin here. */

-static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
+static int selinux_ptrace(struct task_struct *parent,
+ struct task_struct *child,
+ unsigned int mode)
{
int rc;

- rc = secondary_ops->ptrace(parent, child);
+ rc = secondary_ops->ptrace(parent, child, mode);
if (rc)
return rc;

+ if (mode == PTRACE_MODE_READ) {
+ struct task_security_struct *tsec = parent->security;
+ struct task_security_struct *csec = child->security;
+ return avc_has_perm(tsec->sid, csec->sid,
+ SECCLASS_FILE, FILE__READ, NULL);
+ }
+
return task_has_perm(parent, child, PROCESS__PTRACE);
}

diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 4a09293..3c7150b 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -95,11 +95,12 @@ struct inode_smack *new_inode_smack(char *smack)
*
* Do the capability checks, and require read and write.
*/
-static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp)
+static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp,
+ unsigned int mode)
{
int rc;

- rc = cap_ptrace(ptp, ctp);
+ rc = cap_ptrace(ptp, ctp, mode);
if (rc != 0)
return rc;

--
1.5.5.1

2008-07-07 16:56:37

by James Morris

[permalink] [raw]
Subject: [PATCH 01/20] selinux: support deferred mapping of contexts

From: Stephen Smalley <[email protected]>

Introduce SELinux support for deferred mapping of security contexts in
the SID table upon policy reload, and use this support for inode
security contexts when the context is not yet valid under the current
policy. Only processes with CAP_MAC_ADMIN + mac_admin permission in
policy can set undefined security contexts on inodes. Inodes with
such undefined contexts are treated as having the unlabeled context
until the context becomes valid upon a policy reload that defines the
context. Context invalidation upon policy reload also uses this
support to save the context information in the SID table and later
recover it upon a subsequent policy reload that defines the context
again.

This support is to enable package managers and similar programs to set
down file contexts unknown to the system policy at the time the file
is created in order to better support placing loadable policy modules
in packages and to support build systems that need to create images of
different distro releases with different policies w/o requiring all of
the contexts to be defined or legal in the build host policy.

With this patch applied, the following sequence is possible, although
in practice it is recommended that this permission only be allowed to
specific program domains such as the package manager.

# rmdir baz
# rm bar
# touch bar
# chcon -t foo_exec_t bar # foo_exec_t is not yet defined
chcon: failed to change context of `bar' to `system_u:object_r:foo_exec_t': Invalid argument
# mkdir -Z system_u:object_r:foo_exec_t baz
mkdir: failed to set default file creation context to `system_u:object_r:foo_exec_t': Invalid argument
# cat setundefined.te
policy_module(setundefined, 1.0)
require {
type unconfined_t;
type unlabeled_t;
}
files_type(unlabeled_t)
allow unconfined_t self:capability2 mac_admin;
# make -f /usr/share/selinux/devel/Makefile setundefined.pp
# semodule -i setundefined.pp
# chcon -t foo_exec_t bar # foo_exec_t is not yet defined
# mkdir -Z system_u:object_r:foo_exec_t baz
# ls -Zd bar baz
-rw-r--r-- root root system_u:object_r:unlabeled_t bar
drwxr-xr-x root root system_u:object_r:unlabeled_t baz
# cat foo.te
policy_module(foo, 1.0)
type foo_exec_t;
files_type(foo_exec_t)
# make -f /usr/share/selinux/devel/Makefile foo.pp
# semodule -i foo.pp # defines foo_exec_t
# ls -Zd bar baz
-rw-r--r-- root root user_u:object_r:foo_exec_t bar
drwxr-xr-x root root system_u:object_r:foo_exec_t baz
# semodule -r foo
# ls -Zd bar baz
-rw-r--r-- root root system_u:object_r:unlabeled_t bar
drwxr-xr-x root root system_u:object_r:unlabeled_t baz
# semodule -i foo.pp
# ls -Zd bar baz
-rw-r--r-- root root user_u:object_r:foo_exec_t bar
drwxr-xr-x root root system_u:object_r:foo_exec_t baz
# semodule -r setundefined foo
# chcon -t foo_exec_t bar # no longer defined and not allowed
chcon: failed to change context of `bar' to `system_u:object_r:foo_exec_t': Invalid argument
# rmdir baz
# mkdir -Z system_u:object_r:foo_exec_t baz
mkdir: failed to set default file creation context to `system_u:object_r:foo_exec_t': Invalid argument

Signed-off-by: Stephen Smalley <[email protected]>
Signed-off-by: James Morris <[email protected]>
---
security/selinux/hooks.c | 20 +++-
security/selinux/include/security.h | 5 +
security/selinux/ss/context.h | 27 ++++-
security/selinux/ss/mls.c | 11 +-
security/selinux/ss/mls.h | 3 +-
security/selinux/ss/services.c | 245 ++++++++++++++++++++++++----------
security/selinux/ss/sidtab.c | 58 +++------
security/selinux/ss/sidtab.h | 7 +-
8 files changed, 248 insertions(+), 128 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1c864c0..59c6e98 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2495,7 +2495,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
}

if (value && len) {
- rc = security_sid_to_context(newsid, &context, &clen);
+ rc = security_sid_to_context_force(newsid, &context, &clen);
if (rc) {
kfree(namep);
return rc;
@@ -2669,6 +2669,11 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
return rc;

rc = security_context_to_sid(value, size, &newsid);
+ if (rc == -EINVAL) {
+ if (!capable(CAP_MAC_ADMIN))
+ return rc;
+ rc = security_context_to_sid_force(value, size, &newsid);
+ }
if (rc)
return rc;

@@ -2703,10 +2708,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
return;
}

- rc = security_context_to_sid(value, size, &newsid);
+ rc = security_context_to_sid_force(value, size, &newsid);
if (rc) {
- printk(KERN_WARNING "%s: unable to obtain SID for context "
- "%s, rc=%d\n", __func__, (char *)value, -rc);
+ printk(KERN_ERR "SELinux: unable to map context to SID"
+ "for (%s, %lu), rc=%d\n",
+ inode->i_sb->s_id, inode->i_ino, -rc);
return;
}

@@ -5153,6 +5159,12 @@ static int selinux_setprocattr(struct task_struct *p,
size--;
}
error = security_context_to_sid(value, size, &sid);
+ if (error == -EINVAL && !strcmp(name, "fscreate")) {
+ if (!capable(CAP_MAC_ADMIN))
+ return error;
+ error = security_context_to_sid_force(value, size,
+ &sid);
+ }
if (error)
return error;
}
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index ad30ac4..7c54300 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -93,12 +93,17 @@ int security_change_sid(u32 ssid, u32 tsid,
int security_sid_to_context(u32 sid, char **scontext,
u32 *scontext_len);

+int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);
+
int security_context_to_sid(const char *scontext, u32 scontext_len,
u32 *out_sid);

int security_context_to_sid_default(const char *scontext, u32 scontext_len,
u32 *out_sid, u32 def_sid, gfp_t gfp_flags);

+int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+ u32 *sid);
+
int security_get_user_sids(u32 callsid, char *username,
u32 **sids, u32 *nel);

diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index b9a6f7f..658c2bd 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -28,6 +28,8 @@ struct context {
u32 role;
u32 type;
struct mls_range range;
+ char *str; /* string representation if context cannot be mapped. */
+ u32 len; /* length of string in bytes */
};

static inline void mls_context_init(struct context *c)
@@ -106,20 +108,43 @@ static inline void context_init(struct context *c)

static inline int context_cpy(struct context *dst, struct context *src)
{
+ int rc;
+
dst->user = src->user;
dst->role = src->role;
dst->type = src->type;
- return mls_context_cpy(dst, src);
+ if (src->str) {
+ dst->str = kstrdup(src->str, GFP_ATOMIC);
+ if (!dst->str)
+ return -ENOMEM;
+ dst->len = src->len;
+ } else {
+ dst->str = NULL;
+ dst->len = 0;
+ }
+ rc = mls_context_cpy(dst, src);
+ if (rc) {
+ kfree(dst->str);
+ return rc;
+ }
+ return 0;
}

static inline void context_destroy(struct context *c)
{
c->user = c->role = c->type = 0;
+ kfree(c->str);
+ c->str = NULL;
+ c->len = 0;
mls_context_destroy(c);
}

static inline int context_cmp(struct context *c1, struct context *c2)
{
+ if (c1->len && c2->len)
+ return (c1->len == c2->len && !strcmp(c1->str, c2->str));
+ if (c1->len || c2->len)
+ return 0;
return ((c1->user == c2->user) &&
(c1->role == c2->role) &&
(c1->type == c2->type) &&
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 8b1706b..a6ca058 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -239,7 +239,8 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
* Policy read-lock must be held for sidtab lookup.
*
*/
-int mls_context_to_sid(char oldc,
+int mls_context_to_sid(struct policydb *pol,
+ char oldc,
char **scontext,
struct context *context,
struct sidtab *s,
@@ -286,7 +287,7 @@ int mls_context_to_sid(char oldc,
*p++ = 0;

for (l = 0; l < 2; l++) {
- levdatum = hashtab_search(policydb.p_levels.table, scontextp);
+ levdatum = hashtab_search(pol->p_levels.table, scontextp);
if (!levdatum) {
rc = -EINVAL;
goto out;
@@ -311,7 +312,7 @@ int mls_context_to_sid(char oldc,
*rngptr++ = 0;
}

- catdatum = hashtab_search(policydb.p_cats.table,
+ catdatum = hashtab_search(pol->p_cats.table,
scontextp);
if (!catdatum) {
rc = -EINVAL;
@@ -327,7 +328,7 @@ int mls_context_to_sid(char oldc,
if (rngptr) {
int i;

- rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
+ rngdatum = hashtab_search(pol->p_cats.table, rngptr);
if (!rngdatum) {
rc = -EINVAL;
goto out;
@@ -395,7 +396,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
if (!tmpstr) {
rc = -ENOMEM;
} else {
- rc = mls_context_to_sid(':', &tmpstr, context,
+ rc = mls_context_to_sid(&policydb, ':', &tmpstr, context,
NULL, SECSID_NULL);
kfree(freestr);
}
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 0fdf625..1276715 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -30,7 +30,8 @@ int mls_context_isvalid(struct policydb *p, struct context *c);
int mls_range_isvalid(struct policydb *p, struct mls_range *r);
int mls_level_isvalid(struct policydb *p, struct mls_level *l);

-int mls_context_to_sid(char oldc,
+int mls_context_to_sid(struct policydb *p,
+ char oldc,
char **scontext,
struct context *context,
struct sidtab *s,
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index dcc2e1c..b86ac9d 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -616,6 +616,14 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
*scontext = NULL;
*scontext_len = 0;

+ if (context->len) {
+ *scontext_len = context->len;
+ *scontext = kstrdup(context->str, GFP_ATOMIC);
+ if (!(*scontext))
+ return -ENOMEM;
+ return 0;
+ }
+
/* Compute the size of the context. */
*scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
*scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
@@ -655,17 +663,8 @@ const char *security_get_initial_sid_context(u32 sid)
return initial_sid_to_string[sid];
}

-/**
- * security_sid_to_context - Obtain a context for a given SID.
- * @sid: security identifier, SID
- * @scontext: security context
- * @scontext_len: length in bytes
- *
- * Write the string representation of the context associated with @sid
- * into a dynamically allocated string of the correct size. Set @scontext
- * to point to this string and set @scontext_len to the length of the string.
- */
-int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
+static int security_sid_to_context_core(u32 sid, char **scontext,
+ u32 *scontext_len, int force)
{
struct context *context;
int rc = 0;
@@ -693,7 +692,10 @@ int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
goto out;
}
POLICY_RDLOCK;
- context = sidtab_search(&sidtab, sid);
+ if (force)
+ context = sidtab_search_force(&sidtab, sid);
+ else
+ context = sidtab_search(&sidtab, sid);
if (!context) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, sid);
@@ -708,36 +710,44 @@ out:

}

-static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
- u32 *sid, u32 def_sid, gfp_t gfp_flags)
+/**
+ * security_sid_to_context - Obtain a context for a given SID.
+ * @sid: security identifier, SID
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ *
+ * Write the string representation of the context associated with @sid
+ * into a dynamically allocated string of the correct size. Set @scontext
+ * to point to this string and set @scontext_len to the length of the string.
+ */
+int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
{
- char *scontext2;
- struct context context;
+ return security_sid_to_context_core(sid, scontext, scontext_len, 0);
+}
+
+int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len)
+{
+ return security_sid_to_context_core(sid, scontext, scontext_len, 1);
+}
+
+static int string_to_context_struct(struct policydb *pol,
+ struct sidtab *sidtabp,
+ const char *scontext,
+ u32 scontext_len,
+ struct context *ctx,
+ u32 def_sid,
+ gfp_t gfp_flags)
+{
+ char *scontext2 = NULL;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *scontextp, *p, oldc;
int rc = 0;

- if (!ss_initialized) {
- int i;
+ context_init(ctx);

- for (i = 1; i < SECINITSID_NUM; i++) {
- if (!strcmp(initial_sid_to_string[i], scontext)) {
- *sid = i;
- goto out;
- }
- }
- *sid = SECINITSID_KERNEL;
- goto out;
- }
- *sid = SECSID_NULL;
-
- /* Copy the string so that we can modify the copy as we parse it.
- The string should already by null terminated, but we append a
- null suffix to the copy to avoid problems with the existing
- attr package, which doesn't view the null terminator as part
- of the attribute value. */
+ /* Copy the string so that we can modify the copy as we parse it. */
scontext2 = kmalloc(scontext_len+1, gfp_flags);
if (!scontext2) {
rc = -ENOMEM;
@@ -746,11 +756,6 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
memcpy(scontext2, scontext, scontext_len);
scontext2[scontext_len] = 0;

- context_init(&context);
- *sid = SECSID_NULL;
-
- POLICY_RDLOCK;
-
/* Parse the security context. */

rc = -EINVAL;
@@ -762,15 +767,15 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
p++;

if (*p == 0)
- goto out_unlock;
+ goto out;

*p++ = 0;

- usrdatum = hashtab_search(policydb.p_users.table, scontextp);
+ usrdatum = hashtab_search(pol->p_users.table, scontextp);
if (!usrdatum)
- goto out_unlock;
+ goto out;

- context.user = usrdatum->value;
+ ctx->user = usrdatum->value;

/* Extract role. */
scontextp = p;
@@ -778,14 +783,14 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
p++;

if (*p == 0)
- goto out_unlock;
+ goto out;

*p++ = 0;

- role = hashtab_search(policydb.p_roles.table, scontextp);
+ role = hashtab_search(pol->p_roles.table, scontextp);
if (!role)
- goto out_unlock;
- context.role = role->value;
+ goto out;
+ ctx->role = role->value;

/* Extract type. */
scontextp = p;
@@ -794,33 +799,74 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
oldc = *p;
*p++ = 0;

- typdatum = hashtab_search(policydb.p_types.table, scontextp);
+ typdatum = hashtab_search(pol->p_types.table, scontextp);
if (!typdatum)
- goto out_unlock;
+ goto out;

- context.type = typdatum->value;
+ ctx->type = typdatum->value;

- rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid);
+ rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid);
if (rc)
- goto out_unlock;
+ goto out;

if ((p - scontext2) < scontext_len) {
rc = -EINVAL;
- goto out_unlock;
+ goto out;
}

/* Check the validity of the new context. */
- if (!policydb_context_isvalid(&policydb, &context)) {
+ if (!policydb_context_isvalid(pol, ctx)) {
rc = -EINVAL;
- goto out_unlock;
+ context_destroy(ctx);
+ goto out;
}
- /* Obtain the new sid. */
- rc = sidtab_context_to_sid(&sidtab, &context, sid);
-out_unlock:
- POLICY_RDUNLOCK;
- context_destroy(&context);
+ rc = 0;
+out:
kfree(scontext2);
+ return rc;
+}
+
+static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
+ u32 *sid, u32 def_sid, gfp_t gfp_flags,
+ int force)
+{
+ struct context context;
+ int rc = 0;
+
+ if (!ss_initialized) {
+ int i;
+
+ for (i = 1; i < SECINITSID_NUM; i++) {
+ if (!strcmp(initial_sid_to_string[i], scontext)) {
+ *sid = i;
+ goto out;
+ }
+ }
+ *sid = SECINITSID_KERNEL;
+ goto out;
+ }
+ *sid = SECSID_NULL;
+
+ POLICY_RDLOCK;
+ rc = string_to_context_struct(&policydb, &sidtab,
+ scontext, scontext_len,
+ &context, def_sid, gfp_flags);
+ if (rc == -EINVAL && force) {
+ context.str = kmalloc(scontext_len+1, gfp_flags);
+ if (!context.str) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy(context.str, scontext, scontext_len);
+ context.str[scontext_len] = 0;
+ context.len = scontext_len;
+ } else if (rc)
+ goto out;
+ rc = sidtab_context_to_sid(&sidtab, &context, sid);
+ if (rc)
+ context_destroy(&context);
out:
+ POLICY_RDUNLOCK;
return rc;
}

@@ -838,7 +884,7 @@ out:
int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
{
return security_context_to_sid_core(scontext, scontext_len,
- sid, SECSID_NULL, GFP_KERNEL);
+ sid, SECSID_NULL, GFP_KERNEL, 0);
}

/**
@@ -855,6 +901,7 @@ int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
* The default SID is passed to the MLS layer to be used to allow
* kernel labeling of the MLS field if the MLS field is not present
* (for upgrading to MLS without full relabel).
+ * Implicitly forces adding of the context even if it cannot be mapped yet.
* Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
* memory is available, or 0 on success.
*/
@@ -862,7 +909,14 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len,
u32 *sid, u32 def_sid, gfp_t gfp_flags)
{
return security_context_to_sid_core(scontext, scontext_len,
- sid, def_sid, gfp_flags);
+ sid, def_sid, gfp_flags, 1);
+}
+
+int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+ u32 *sid)
+{
+ return security_context_to_sid_core(scontext, scontext_len,
+ sid, SECSID_NULL, GFP_KERNEL, 1);
}

static int compute_sid_handle_invalid_context(
@@ -1246,9 +1300,12 @@ static inline int convert_context_handle_invalid_context(struct context *context
char *s;
u32 len;

- context_struct_to_string(context, &s, &len);
- printk(KERN_ERR "SELinux: context %s is invalid\n", s);
- kfree(s);
+ if (!context_struct_to_string(context, &s, &len)) {
+ printk(KERN_WARNING
+ "SELinux: Context %s would be invalid if enforcing\n",
+ s);
+ kfree(s);
+ }
}
return rc;
}
@@ -1280,6 +1337,32 @@ static int convert_context(u32 key,

args = p;

+ if (c->str) {
+ struct context ctx;
+ rc = string_to_context_struct(args->newp, NULL, c->str,
+ c->len, &ctx, SECSID_NULL,
+ GFP_KERNEL);
+ if (!rc) {
+ printk(KERN_INFO
+ "SELinux: Context %s became valid (mapped).\n",
+ c->str);
+ /* Replace string with mapped representation. */
+ kfree(c->str);
+ memcpy(c, &ctx, sizeof(*c));
+ goto out;
+ } else if (rc == -EINVAL) {
+ /* Retain string representation for later mapping. */
+ rc = 0;
+ goto out;
+ } else {
+ /* Other error condition, e.g. ENOMEM. */
+ printk(KERN_ERR
+ "SELinux: Unable to map context %s, rc = %d.\n",
+ c->str, -rc);
+ goto out;
+ }
+ }
+
rc = context_cpy(&oldc, c);
if (rc)
goto out;
@@ -1319,13 +1402,21 @@ static int convert_context(u32 key,
}

context_destroy(&oldc);
+ rc = 0;
out:
return rc;
bad:
- context_struct_to_string(&oldc, &s, &len);
+ /* Map old representation to string and save it. */
+ if (context_struct_to_string(&oldc, &s, &len))
+ return -ENOMEM;
context_destroy(&oldc);
- printk(KERN_ERR "SELinux: invalidating context %s\n", s);
- kfree(s);
+ context_destroy(c);
+ c->str = s;
+ c->len = len;
+ printk(KERN_INFO
+ "SELinux: Context %s became invalid (unmapped).\n",
+ c->str);
+ rc = 0;
goto out;
}

@@ -1406,7 +1497,11 @@ int security_load_policy(void *data, size_t len)
return -EINVAL;
}

- sidtab_init(&newsidtab);
+ if (sidtab_init(&newsidtab)) {
+ LOAD_UNLOCK;
+ policydb_destroy(&newpolicydb);
+ return -ENOMEM;
+ }

/* Verify that the kernel defined classes are correct. */
if (validate_classes(&newpolicydb)) {
@@ -1429,11 +1524,15 @@ int security_load_policy(void *data, size_t len)
goto err;
}

- /* Convert the internal representations of contexts
- in the new SID table and remove invalid SIDs. */
+ /*
+ * Convert the internal representations of contexts
+ * in the new SID table.
+ */
args.oldp = &policydb;
args.newp = &newpolicydb;
- sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
+ rc = sidtab_map(&newsidtab, convert_context, &args);
+ if (rc)
+ goto err;

/* Save the old policydb and SID table to free later. */
memcpy(&oldpolicydb, &policydb, sizeof policydb);
@@ -1673,6 +1772,8 @@ int security_get_user_sids(u32 fromsid,

POLICY_RDLOCK;

+ context_init(&usercon);
+
fromcon = sidtab_search(&sidtab, fromsid);
if (!fromcon) {
rc = -EINVAL;
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index 4a516ff..ba35416 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -86,7 +86,7 @@ out:
return rc;
}

-struct context *sidtab_search(struct sidtab *s, u32 sid)
+static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
{
int hvalue;
struct sidtab_node *cur;
@@ -99,7 +99,10 @@ struct context *sidtab_search(struct sidtab *s, u32 sid)
while (cur != NULL && sid > cur->sid)
cur = cur->next;

- if (cur == NULL || sid != cur->sid) {
+ if (force && cur && sid == cur->sid && cur->context.len)
+ return &cur->context;
+
+ if (cur == NULL || sid != cur->sid || cur->context.len) {
/* Remap invalid SIDs to the unlabeled SID. */
sid = SECINITSID_UNLABELED;
hvalue = SIDTAB_HASH(sid);
@@ -113,6 +116,16 @@ struct context *sidtab_search(struct sidtab *s, u32 sid)
return &cur->context;
}

+struct context *sidtab_search(struct sidtab *s, u32 sid)
+{
+ return sidtab_search_core(s, sid, 0);
+}
+
+struct context *sidtab_search_force(struct sidtab *s, u32 sid)
+{
+ return sidtab_search_core(s, sid, 1);
+}
+
int sidtab_map(struct sidtab *s,
int (*apply) (u32 sid,
struct context *context,
@@ -138,43 +151,6 @@ out:
return rc;
}

-void sidtab_map_remove_on_error(struct sidtab *s,
- int (*apply) (u32 sid,
- struct context *context,
- void *args),
- void *args)
-{
- int i, ret;
- struct sidtab_node *last, *cur, *temp;
-
- if (!s)
- return;
-
- for (i = 0; i < SIDTAB_SIZE; i++) {
- last = NULL;
- cur = s->htable[i];
- while (cur != NULL) {
- ret = apply(cur->sid, &cur->context, args);
- if (ret) {
- if (last)
- last->next = cur->next;
- else
- s->htable[i] = cur->next;
- temp = cur;
- cur = cur->next;
- context_destroy(&temp->context);
- kfree(temp);
- s->nel--;
- } else {
- last = cur;
- cur = cur->next;
- }
- }
- }
-
- return;
-}
-
static inline u32 sidtab_search_context(struct sidtab *s,
struct context *context)
{
@@ -215,6 +191,10 @@ int sidtab_context_to_sid(struct sidtab *s,
goto unlock_out;
}
sid = s->next_sid++;
+ if (context->len)
+ printk(KERN_INFO
+ "SELinux: Context %s is not valid (left unmapped).\n",
+ context->str);
ret = sidtab_insert(s, sid, context);
if (ret)
s->next_sid--;
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index 2fe9dfa..64ea5b1 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -32,6 +32,7 @@ struct sidtab {
int sidtab_init(struct sidtab *s);
int sidtab_insert(struct sidtab *s, u32 sid, struct context *context);
struct context *sidtab_search(struct sidtab *s, u32 sid);
+struct context *sidtab_search_force(struct sidtab *s, u32 sid);

int sidtab_map(struct sidtab *s,
int (*apply) (u32 sid,
@@ -39,12 +40,6 @@ int sidtab_map(struct sidtab *s,
void *args),
void *args);

-void sidtab_map_remove_on_error(struct sidtab *s,
- int (*apply) (u32 sid,
- struct context *context,
- void *args),
- void *args);
-
int sidtab_context_to_sid(struct sidtab *s,
struct context *context,
u32 *sid);
--
1.5.5.1

2008-07-07 17:44:42

by James Morris

[permalink] [raw]
Subject: [PATCH 05/20] SELinux: remove inherit field from inode_security_struct

Remove inherit field from inode_security_struct, per Stephen Smalley:
"Let's just drop inherit altogether - dead field."

Signed-off-by: James Morris <[email protected]>
---
security/selinux/include/objsec.h | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 5f0be19..91070ab 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -43,7 +43,6 @@ struct inode_security_struct {
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */
- unsigned char inherit; /* inherit SID from parent entry */
struct mutex lock;
};

--
1.5.5.1

2008-07-07 18:23:47

by Theodore Ts'o

[permalink] [raw]
Subject: Re: SELinux patches for 2.6.26

On Tue, Jul 08, 2008 at 01:42:02AM +0900, James Morris wrote:
>
> Please review these SELinux patches for the 2.6.26 kernel.
>

You mean 2.6.27 kernel, right?

- Ted

2008-07-07 22:49:33

by James Morris

[permalink] [raw]
Subject: Re: SELinux patches for 2.6.26

On Mon, 7 Jul 2008, Theodore Tso wrote:

> On Tue, Jul 08, 2008 at 01:42:02AM +0900, James Morris wrote:
> >
> > Please review these SELinux patches for the 2.6.26 kernel.
> >
>
> You mean 2.6.27 kernel, right?

Indeed.


- James
--
James Morris
<[email protected]>