Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 157342
b: refs/heads/master
c: 5d13544
h: refs/heads/master
v: v3
  • Loading branch information
David Howells authored and James Morris committed Sep 2, 2009
1 parent f9c3aa2 commit 3ae06e9
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 7 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f041ae2f99d49adc914153a34a2d0e14e4389d90
refs/heads/master: 5d135440faf7db8d566de0c6fab36b16cf9cfc3b
19 changes: 18 additions & 1 deletion trunk/Documentation/keys.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This document has the following sections:
- Notes on accessing payload contents
- Defining a key type
- Request-key callback service
- Key access filesystem
- Garbage collection


============
Expand Down Expand Up @@ -113,6 +113,9 @@ Each key has a number of attributes:

(*) Dead. The key's type was unregistered, and so the key is now useless.

Keys in the last three states are subject to garbage collection. See the
section on "Garbage collection".


====================
KEY SERVICE OVERVIEW
Expand Down Expand Up @@ -1231,3 +1234,17 @@ by executing:

In this case, the program isn't required to actually attach the key to a ring;
the rings are provided for reference.


==================
GARBAGE COLLECTION
==================

Dead keys (for which the type has been removed) will be automatically unlinked
from those keyrings that point to them and deleted as soon as possible by a
background garbage collector.

Similarly, revoked and expired keys will be garbage collected, but only after a
certain amount of time has passed. This time is set as a number of seconds in:

/proc/sys/kernel/keys/gc_delay
5 changes: 4 additions & 1 deletion trunk/include/linux/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ struct key {
struct rw_semaphore sem; /* change vs change sem */
struct key_user *user; /* owner of this key */
void *security; /* security data for this key */
time_t expiry; /* time at which key expires (or 0) */
union {
time_t expiry; /* time at which key expires (or 0) */
time_t revoked_at; /* time at which key was revoked */
};
uid_t uid;
gid_t gid;
key_perm_t perm; /* access permissions */
Expand Down
1 change: 1 addition & 0 deletions trunk/security/keys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

obj-y := \
gc.o \
key.o \
keyring.o \
keyctl.o \
Expand Down
193 changes: 193 additions & 0 deletions trunk/security/keys/gc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/* Key garbage collector
*
* Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/

#include <linux/module.h>
#include <keys/keyring-type.h>
#include "internal.h"

/*
* Delay between key revocation/expiry in seconds
*/
unsigned key_gc_delay = 5 * 60;

/*
* Reaper
*/
static void key_gc_timer_func(unsigned long);
static void key_garbage_collector(struct work_struct *);
static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
static DECLARE_WORK(key_gc_work, key_garbage_collector);
static key_serial_t key_gc_cursor; /* the last key the gc considered */
static unsigned long key_gc_executing;
static time_t key_gc_next_run = LONG_MAX;

/*
* Schedule a garbage collection run
* - precision isn't particularly important
*/
void key_schedule_gc(time_t gc_at)
{
unsigned long expires;
time_t now = current_kernel_time().tv_sec;

kenter("%ld", gc_at - now);

gc_at += key_gc_delay;

if (now >= gc_at) {
schedule_work(&key_gc_work);
} else if (gc_at < key_gc_next_run) {
expires = jiffies + (gc_at - now) * HZ;
mod_timer(&key_gc_timer, expires);
}
}

/*
* The garbage collector timer kicked off
*/
static void key_gc_timer_func(unsigned long data)
{
kenter("");
key_gc_next_run = LONG_MAX;
schedule_work(&key_gc_work);
}

/*
* Garbage collect pointers from a keyring
* - return true if we altered the keyring
*/
static bool key_gc_keyring(struct key *keyring, time_t limit)
{
struct keyring_list *klist;
struct key *key;
int loop;

kenter("%x", key_serial(keyring));

if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
goto dont_gc;

/* scan the keyring looking for dead keys */
klist = rcu_dereference(keyring->payload.subscriptions);
if (!klist)
goto dont_gc;

for (loop = klist->nkeys - 1; loop >= 0; loop--) {
key = klist->keys[loop];
if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
(key->expiry > 0 && key->expiry <= limit))
goto do_gc;
}

dont_gc:
kleave(" = false");
return false;

do_gc:
key_gc_cursor = keyring->serial;
key_get(keyring);
spin_unlock(&key_serial_lock);
keyring_gc(keyring, limit);
key_put(keyring);
kleave(" = true");
return true;
}

/*
* Garbage collector for keys
* - this involves scanning the keyrings for dead, expired and revoked keys
* that have overstayed their welcome
*/
static void key_garbage_collector(struct work_struct *work)
{
struct rb_node *rb;
key_serial_t cursor;
struct key *key, *xkey;
time_t new_timer = LONG_MAX, limit;

kenter("");

if (test_and_set_bit(0, &key_gc_executing)) {
key_schedule_gc(current_kernel_time().tv_sec);
return;
}

limit = current_kernel_time().tv_sec;
if (limit > key_gc_delay)
limit -= key_gc_delay;
else
limit = key_gc_delay;

spin_lock(&key_serial_lock);

if (RB_EMPTY_ROOT(&key_serial_tree))
goto reached_the_end;

cursor = key_gc_cursor;
if (cursor < 0)
cursor = 0;

/* find the first key above the cursor */
key = NULL;
rb = key_serial_tree.rb_node;
while (rb) {
xkey = rb_entry(rb, struct key, serial_node);
if (cursor < xkey->serial) {
key = xkey;
rb = rb->rb_left;
} else if (cursor > xkey->serial) {
rb = rb->rb_right;
} else {
rb = rb_next(rb);
if (!rb)
goto reached_the_end;
key = rb_entry(rb, struct key, serial_node);
break;
}
}

if (!key)
goto reached_the_end;

/* trawl through the keys looking for keyrings */
for (;;) {
if (key->expiry > 0 && key->expiry < new_timer)
new_timer = key->expiry;

if (key->type == &key_type_keyring &&
key_gc_keyring(key, limit)) {
/* the gc ate our lock */
schedule_work(&key_gc_work);
goto no_unlock;
}

rb = rb_next(&key->serial_node);
if (!rb) {
key_gc_cursor = 0;
break;
}
key = rb_entry(rb, struct key, serial_node);
}

out:
spin_unlock(&key_serial_lock);
no_unlock:
clear_bit(0, &key_gc_executing);
if (new_timer < LONG_MAX)
key_schedule_gc(new_timer);

kleave("");
return;

reached_the_end:
key_gc_cursor = 0;
goto out;
}
4 changes: 4 additions & 0 deletions trunk/security/keys/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,

extern long join_session_keyring(const char *name);

extern unsigned key_gc_delay;
extern void keyring_gc(struct key *keyring, time_t limit);
extern void key_schedule_gc(time_t expiry_at);

/*
* check to see whether permission is granted to use a key in the desired way
*/
Expand Down
14 changes: 14 additions & 0 deletions trunk/security/keys/key.c
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ int key_negate_and_link(struct key *key,
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
now = current_kernel_time();
key->expiry = now.tv_sec + timeout;
key_schedule_gc(key->expiry);

if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
awaken = 1;
Expand Down Expand Up @@ -888,6 +889,9 @@ EXPORT_SYMBOL(key_update);
*/
void key_revoke(struct key *key)
{
struct timespec now;
time_t time;

key_check(key);

/* make sure no one's trying to change or use the key when we mark it
Expand All @@ -900,6 +904,14 @@ void key_revoke(struct key *key)
key->type->revoke)
key->type->revoke(key);

/* set the death time to no more than the expiry time */
now = current_kernel_time();
time = now.tv_sec;
if (key->revoked_at == 0 || key->revoked_at > time) {
key->revoked_at = time;
key_schedule_gc(key->revoked_at);
}

up_write(&key->sem);

} /* end key_revoke() */
Expand Down Expand Up @@ -984,6 +996,8 @@ void unregister_key_type(struct key_type *ktype)
spin_unlock(&key_serial_lock);
up_write(&key_types_sem);

key_schedule_gc(0);

} /* end unregister_key_type() */

EXPORT_SYMBOL(unregister_key_type);
Expand Down
1 change: 1 addition & 0 deletions trunk/security/keys/keyctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
}

key->expiry = expiry;
key_schedule_gc(key->expiry);

up_write(&key->sem);
key_put(key);
Expand Down
Loading

0 comments on commit 3ae06e9

Please sign in to comment.