Merge branch 'for-linus3' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6
* 'for-linus3' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: SELinux: inline selinux_is_enabled in !CONFIG_SECURITY_SELINUX KEYS: Fix garbage collector KEYS: Unlock tasklist when exiting early from keyctl_session_to_parent CRED: Allow put_cred() to cope with a NULL groups list SELinux: flush the avc before disabling SELinux SELinux: seperate avc_cache flushing Creds: creds->security can be NULL is selinux is disabled
This commit is contained in:
commit
1824090496
@ -15,6 +15,7 @@
|
|||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/key.h>
|
#include <linux/key.h>
|
||||||
|
#include <linux/selinux.h>
|
||||||
#include <asm/atomic.h>
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
struct user_struct;
|
struct user_struct;
|
||||||
@ -182,11 +183,13 @@ static inline bool creds_are_invalid(const struct cred *cred)
|
|||||||
if (atomic_read(&cred->usage) < atomic_read(&cred->subscribers))
|
if (atomic_read(&cred->usage) < atomic_read(&cred->subscribers))
|
||||||
return true;
|
return true;
|
||||||
#ifdef CONFIG_SECURITY_SELINUX
|
#ifdef CONFIG_SECURITY_SELINUX
|
||||||
if ((unsigned long) cred->security < PAGE_SIZE)
|
if (selinux_is_enabled()) {
|
||||||
return true;
|
if ((unsigned long) cred->security < PAGE_SIZE)
|
||||||
if ((*(u32*)cred->security & 0xffffff00) ==
|
return true;
|
||||||
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
|
if ((*(u32 *)cred->security & 0xffffff00) ==
|
||||||
return true;
|
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,11 @@ void selinux_secmark_refcount_inc(void);
|
|||||||
* existing SECMARK targets has been removed/flushed.
|
* existing SECMARK targets has been removed/flushed.
|
||||||
*/
|
*/
|
||||||
void selinux_secmark_refcount_dec(void);
|
void selinux_secmark_refcount_dec(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selinux_is_enabled - is SELinux enabled?
|
||||||
|
*/
|
||||||
|
bool selinux_is_enabled(void);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline int selinux_string_to_sid(const char *str, u32 *sid)
|
static inline int selinux_string_to_sid(const char *str, u32 *sid)
|
||||||
@ -84,6 +89,10 @@ static inline void selinux_secmark_refcount_dec(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool selinux_is_enabled(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif /* CONFIG_SECURITY_SELINUX */
|
#endif /* CONFIG_SECURITY_SELINUX */
|
||||||
|
|
||||||
#endif /* _LINUX_SELINUX_H */
|
#endif /* _LINUX_SELINUX_H */
|
||||||
|
@ -147,7 +147,8 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
|||||||
key_put(cred->thread_keyring);
|
key_put(cred->thread_keyring);
|
||||||
key_put(cred->request_key_auth);
|
key_put(cred->request_key_auth);
|
||||||
release_tgcred(cred);
|
release_tgcred(cred);
|
||||||
put_group_info(cred->group_info);
|
if (cred->group_info)
|
||||||
|
put_group_info(cred->group_info);
|
||||||
free_uid(cred->user);
|
free_uid(cred->user);
|
||||||
kmem_cache_free(cred_jar, cred);
|
kmem_cache_free(cred_jar, cred);
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,10 @@ static void key_garbage_collector(struct work_struct *);
|
|||||||
static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
|
static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
|
||||||
static DECLARE_WORK(key_gc_work, key_garbage_collector);
|
static DECLARE_WORK(key_gc_work, key_garbage_collector);
|
||||||
static key_serial_t key_gc_cursor; /* the last key the gc considered */
|
static key_serial_t key_gc_cursor; /* the last key the gc considered */
|
||||||
|
static bool key_gc_again;
|
||||||
static unsigned long key_gc_executing;
|
static unsigned long key_gc_executing;
|
||||||
static time_t key_gc_next_run = LONG_MAX;
|
static time_t key_gc_next_run = LONG_MAX;
|
||||||
|
static time_t key_gc_new_timer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Schedule a garbage collection run
|
* Schedule a garbage collection run
|
||||||
@ -40,9 +42,7 @@ void key_schedule_gc(time_t gc_at)
|
|||||||
|
|
||||||
kenter("%ld", gc_at - now);
|
kenter("%ld", gc_at - now);
|
||||||
|
|
||||||
gc_at += key_gc_delay;
|
if (gc_at <= now) {
|
||||||
|
|
||||||
if (now >= gc_at) {
|
|
||||||
schedule_work(&key_gc_work);
|
schedule_work(&key_gc_work);
|
||||||
} else if (gc_at < key_gc_next_run) {
|
} else if (gc_at < key_gc_next_run) {
|
||||||
expires = jiffies + (gc_at - now) * HZ;
|
expires = jiffies + (gc_at - now) * HZ;
|
||||||
@ -112,16 +112,18 @@ static void key_garbage_collector(struct work_struct *work)
|
|||||||
struct rb_node *rb;
|
struct rb_node *rb;
|
||||||
key_serial_t cursor;
|
key_serial_t cursor;
|
||||||
struct key *key, *xkey;
|
struct key *key, *xkey;
|
||||||
time_t new_timer = LONG_MAX, limit;
|
time_t new_timer = LONG_MAX, limit, now;
|
||||||
|
|
||||||
kenter("");
|
now = current_kernel_time().tv_sec;
|
||||||
|
kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
|
||||||
|
|
||||||
if (test_and_set_bit(0, &key_gc_executing)) {
|
if (test_and_set_bit(0, &key_gc_executing)) {
|
||||||
key_schedule_gc(current_kernel_time().tv_sec);
|
key_schedule_gc(current_kernel_time().tv_sec + 1);
|
||||||
|
kleave(" [busy; deferring]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
limit = current_kernel_time().tv_sec;
|
limit = now;
|
||||||
if (limit > key_gc_delay)
|
if (limit > key_gc_delay)
|
||||||
limit -= key_gc_delay;
|
limit -= key_gc_delay;
|
||||||
else
|
else
|
||||||
@ -129,12 +131,19 @@ static void key_garbage_collector(struct work_struct *work)
|
|||||||
|
|
||||||
spin_lock(&key_serial_lock);
|
spin_lock(&key_serial_lock);
|
||||||
|
|
||||||
if (RB_EMPTY_ROOT(&key_serial_tree))
|
if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) {
|
||||||
goto reached_the_end;
|
spin_unlock(&key_serial_lock);
|
||||||
|
clear_bit(0, &key_gc_executing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
cursor = key_gc_cursor;
|
cursor = key_gc_cursor;
|
||||||
if (cursor < 0)
|
if (cursor < 0)
|
||||||
cursor = 0;
|
cursor = 0;
|
||||||
|
if (cursor > 0)
|
||||||
|
new_timer = key_gc_new_timer;
|
||||||
|
else
|
||||||
|
key_gc_again = false;
|
||||||
|
|
||||||
/* find the first key above the cursor */
|
/* find the first key above the cursor */
|
||||||
key = NULL;
|
key = NULL;
|
||||||
@ -160,35 +169,50 @@ static void key_garbage_collector(struct work_struct *work)
|
|||||||
|
|
||||||
/* trawl through the keys looking for keyrings */
|
/* trawl through the keys looking for keyrings */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (key->expiry > 0 && key->expiry < new_timer)
|
if (key->expiry > now && key->expiry < new_timer) {
|
||||||
|
kdebug("will expire %x in %ld",
|
||||||
|
key_serial(key), key->expiry - now);
|
||||||
new_timer = key->expiry;
|
new_timer = key->expiry;
|
||||||
|
}
|
||||||
|
|
||||||
if (key->type == &key_type_keyring &&
|
if (key->type == &key_type_keyring &&
|
||||||
key_gc_keyring(key, limit)) {
|
key_gc_keyring(key, limit))
|
||||||
/* the gc ate our lock */
|
/* the gc had to release our lock so that the keyring
|
||||||
schedule_work(&key_gc_work);
|
* could be modified, so we have to get it again */
|
||||||
goto no_unlock;
|
goto gc_released_our_lock;
|
||||||
}
|
|
||||||
|
|
||||||
rb = rb_next(&key->serial_node);
|
rb = rb_next(&key->serial_node);
|
||||||
if (!rb) {
|
if (!rb)
|
||||||
key_gc_cursor = 0;
|
goto reached_the_end;
|
||||||
break;
|
|
||||||
}
|
|
||||||
key = rb_entry(rb, struct key, serial_node);
|
key = rb_entry(rb, struct key, serial_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
gc_released_our_lock:
|
||||||
spin_unlock(&key_serial_lock);
|
kdebug("gc_released_our_lock");
|
||||||
no_unlock:
|
key_gc_new_timer = new_timer;
|
||||||
|
key_gc_again = true;
|
||||||
clear_bit(0, &key_gc_executing);
|
clear_bit(0, &key_gc_executing);
|
||||||
if (new_timer < LONG_MAX)
|
schedule_work(&key_gc_work);
|
||||||
key_schedule_gc(new_timer);
|
kleave(" [continue]");
|
||||||
|
|
||||||
kleave("");
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* when we reach the end of the run, we set the timer for the next one */
|
||||||
reached_the_end:
|
reached_the_end:
|
||||||
|
kdebug("reached_the_end");
|
||||||
|
spin_unlock(&key_serial_lock);
|
||||||
|
key_gc_new_timer = new_timer;
|
||||||
key_gc_cursor = 0;
|
key_gc_cursor = 0;
|
||||||
goto out;
|
clear_bit(0, &key_gc_executing);
|
||||||
|
|
||||||
|
if (key_gc_again) {
|
||||||
|
/* there may have been a key that expired whilst we were
|
||||||
|
* scanning, so if we discarded any links we should do another
|
||||||
|
* scan */
|
||||||
|
new_timer = now + 1;
|
||||||
|
key_schedule_gc(new_timer);
|
||||||
|
} else if (new_timer < LONG_MAX) {
|
||||||
|
new_timer += key_gc_delay;
|
||||||
|
key_schedule_gc(new_timer);
|
||||||
|
}
|
||||||
|
kleave(" [end]");
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,7 @@ int key_negate_and_link(struct key *key,
|
|||||||
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
|
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
|
||||||
now = current_kernel_time();
|
now = current_kernel_time();
|
||||||
key->expiry = now.tv_sec + timeout;
|
key->expiry = now.tv_sec + timeout;
|
||||||
key_schedule_gc(key->expiry);
|
key_schedule_gc(key->expiry + key_gc_delay);
|
||||||
|
|
||||||
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
|
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
|
||||||
awaken = 1;
|
awaken = 1;
|
||||||
@ -909,7 +909,7 @@ void key_revoke(struct key *key)
|
|||||||
time = now.tv_sec;
|
time = now.tv_sec;
|
||||||
if (key->revoked_at == 0 || key->revoked_at > time) {
|
if (key->revoked_at == 0 || key->revoked_at > time) {
|
||||||
key->revoked_at = time;
|
key->revoked_at = time;
|
||||||
key_schedule_gc(key->revoked_at);
|
key_schedule_gc(key->revoked_at + key_gc_delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
up_write(&key->sem);
|
up_write(&key->sem);
|
||||||
|
@ -1115,7 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
key->expiry = expiry;
|
key->expiry = expiry;
|
||||||
key_schedule_gc(key->expiry);
|
key_schedule_gc(key->expiry + key_gc_delay);
|
||||||
|
|
||||||
up_write(&key->sem);
|
up_write(&key->sem);
|
||||||
key_put(key);
|
key_put(key);
|
||||||
@ -1319,6 +1319,7 @@ long keyctl_session_to_parent(void)
|
|||||||
already_same:
|
already_same:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
not_permitted:
|
not_permitted:
|
||||||
|
write_unlock_irq(&tasklist_lock);
|
||||||
put_cred(cred);
|
put_cred(cred);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -1019,18 +1019,18 @@ void keyring_gc(struct key *keyring, time_t limit)
|
|||||||
struct key *key;
|
struct key *key;
|
||||||
int loop, keep, max;
|
int loop, keep, max;
|
||||||
|
|
||||||
kenter("%x", key_serial(keyring));
|
kenter("{%x,%s}", key_serial(keyring), keyring->description);
|
||||||
|
|
||||||
down_write(&keyring->sem);
|
down_write(&keyring->sem);
|
||||||
|
|
||||||
klist = keyring->payload.subscriptions;
|
klist = keyring->payload.subscriptions;
|
||||||
if (!klist)
|
if (!klist)
|
||||||
goto just_return;
|
goto no_klist;
|
||||||
|
|
||||||
/* work out how many subscriptions we're keeping */
|
/* work out how many subscriptions we're keeping */
|
||||||
keep = 0;
|
keep = 0;
|
||||||
for (loop = klist->nkeys - 1; loop >= 0; loop--)
|
for (loop = klist->nkeys - 1; loop >= 0; loop--)
|
||||||
if (!key_is_dead(klist->keys[loop], limit));
|
if (!key_is_dead(klist->keys[loop], limit))
|
||||||
keep++;
|
keep++;
|
||||||
|
|
||||||
if (keep == klist->nkeys)
|
if (keep == klist->nkeys)
|
||||||
@ -1041,7 +1041,7 @@ void keyring_gc(struct key *keyring, time_t limit)
|
|||||||
new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
|
new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!new)
|
if (!new)
|
||||||
goto just_return;
|
goto nomem;
|
||||||
new->maxkeys = max;
|
new->maxkeys = max;
|
||||||
new->nkeys = 0;
|
new->nkeys = 0;
|
||||||
new->delkey = 0;
|
new->delkey = 0;
|
||||||
@ -1081,7 +1081,21 @@ void keyring_gc(struct key *keyring, time_t limit)
|
|||||||
discard_new:
|
discard_new:
|
||||||
new->nkeys = keep;
|
new->nkeys = keep;
|
||||||
keyring_clear_rcu_disposal(&new->rcu);
|
keyring_clear_rcu_disposal(&new->rcu);
|
||||||
|
up_write(&keyring->sem);
|
||||||
|
kleave(" [discard]");
|
||||||
|
return;
|
||||||
|
|
||||||
just_return:
|
just_return:
|
||||||
up_write(&keyring->sem);
|
up_write(&keyring->sem);
|
||||||
kleave(" [no]");
|
kleave(" [no dead]");
|
||||||
|
return;
|
||||||
|
|
||||||
|
no_klist:
|
||||||
|
up_write(&keyring->sem);
|
||||||
|
kleave(" [no_klist]");
|
||||||
|
return;
|
||||||
|
|
||||||
|
nomem:
|
||||||
|
up_write(&keyring->sem);
|
||||||
|
kleave(" [oom]");
|
||||||
}
|
}
|
||||||
|
@ -709,18 +709,16 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* avc_ss_reset - Flush the cache and revalidate migrated permissions.
|
* avc_flush - Flush the cache
|
||||||
* @seqno: policy sequence number
|
|
||||||
*/
|
*/
|
||||||
int avc_ss_reset(u32 seqno)
|
static void avc_flush(void)
|
||||||
{
|
{
|
||||||
struct avc_callback_node *c;
|
|
||||||
int i, rc = 0, tmprc;
|
|
||||||
unsigned long flag;
|
|
||||||
struct avc_node *node;
|
|
||||||
struct hlist_head *head;
|
struct hlist_head *head;
|
||||||
struct hlist_node *next;
|
struct hlist_node *next;
|
||||||
|
struct avc_node *node;
|
||||||
spinlock_t *lock;
|
spinlock_t *lock;
|
||||||
|
unsigned long flag;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
|
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
|
||||||
head = &avc_cache.slots[i];
|
head = &avc_cache.slots[i];
|
||||||
@ -737,6 +735,18 @@ int avc_ss_reset(u32 seqno)
|
|||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
spin_unlock_irqrestore(lock, flag);
|
spin_unlock_irqrestore(lock, flag);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* avc_ss_reset - Flush the cache and revalidate migrated permissions.
|
||||||
|
* @seqno: policy sequence number
|
||||||
|
*/
|
||||||
|
int avc_ss_reset(u32 seqno)
|
||||||
|
{
|
||||||
|
struct avc_callback_node *c;
|
||||||
|
int rc = 0, tmprc;
|
||||||
|
|
||||||
|
avc_flush();
|
||||||
|
|
||||||
for (c = avc_callbacks; c; c = c->next) {
|
for (c = avc_callbacks; c; c = c->next) {
|
||||||
if (c->events & AVC_CALLBACK_RESET) {
|
if (c->events & AVC_CALLBACK_RESET) {
|
||||||
@ -858,6 +868,8 @@ u32 avc_policy_seqno(void)
|
|||||||
|
|
||||||
void avc_disable(void)
|
void avc_disable(void)
|
||||||
{
|
{
|
||||||
|
avc_flush();
|
||||||
|
synchronize_rcu();
|
||||||
if (avc_node_cachep)
|
if (avc_node_cachep)
|
||||||
kmem_cache_destroy(avc_node_cachep);
|
kmem_cache_destroy(avc_node_cachep);
|
||||||
}
|
}
|
||||||
|
@ -63,3 +63,9 @@ void selinux_secmark_refcount_dec(void)
|
|||||||
atomic_dec(&selinux_secmark_refcount);
|
atomic_dec(&selinux_secmark_refcount);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(selinux_secmark_refcount_dec);
|
EXPORT_SYMBOL_GPL(selinux_secmark_refcount_dec);
|
||||||
|
|
||||||
|
bool selinux_is_enabled(void)
|
||||||
|
{
|
||||||
|
return selinux_enabled;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(selinux_is_enabled);
|
||||||
|
Loading…
Reference in New Issue
Block a user