Commit 18240904 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus3' of...

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
parents f86054c2 8a478905
...@@ -15,6 +15,7 @@ ...@@ -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) ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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 *); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, ...@@ -709,18 +709,16 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
} }
/** /**
* 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) ...@@ -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) ...@@ -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) ...@@ -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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment