kernel_optimize_test/security/keys/persistent.c
David Howells f36f8c75ae KEYS: Add per-user_namespace registers for persistent per-UID kerberos caches
Add support for per-user_namespace registers of persistent per-UID kerberos
caches held within the kernel.

This allows the kerberos cache to be retained beyond the life of all a user's
processes so that the user's cron jobs can work.

The kerberos cache is envisioned as a keyring/key tree looking something like:

	struct user_namespace
	  \___ .krb_cache keyring		- The register
		\___ _krb.0 keyring		- Root's Kerberos cache
		\___ _krb.5000 keyring		- User 5000's Kerberos cache
		\___ _krb.5001 keyring		- User 5001's Kerberos cache
			\___ tkt785 big_key	- A ccache blob
			\___ tkt12345 big_key	- Another ccache blob

Or possibly:

	struct user_namespace
	  \___ .krb_cache keyring		- The register
		\___ _krb.0 keyring		- Root's Kerberos cache
		\___ _krb.5000 keyring		- User 5000's Kerberos cache
		\___ _krb.5001 keyring		- User 5001's Kerberos cache
			\___ tkt785 keyring	- A ccache
				\___ krbtgt/REDHAT.COM@REDHAT.COM big_key
				\___ http/REDHAT.COM@REDHAT.COM user
				\___ afs/REDHAT.COM@REDHAT.COM user
				\___ nfs/REDHAT.COM@REDHAT.COM user
				\___ krbtgt/KERNEL.ORG@KERNEL.ORG big_key
				\___ http/KERNEL.ORG@KERNEL.ORG big_key

What goes into a particular Kerberos cache is entirely up to userspace.  Kernel
support is limited to giving you the Kerberos cache keyring that you want.

The user asks for their Kerberos cache by:

	krb_cache = keyctl_get_krbcache(uid, dest_keyring);

The uid is -1 or the user's own UID for the user's own cache or the uid of some
other user's cache (requires CAP_SETUID).  This permits rpc.gssd or whatever to
mess with the cache.

The cache returned is a keyring named "_krb.<uid>" that the possessor can read,
search, clear, invalidate, unlink from and add links to.  Active LSMs get a
chance to rule on whether the caller is permitted to make a link.

Each uid's cache keyring is created when it first accessed and is given a
timeout that is extended each time this function is called so that the keyring
goes away after a while.  The timeout is configurable by sysctl but defaults to
three days.

Each user_namespace struct gets a lazily-created keyring that serves as the
register.  The cache keyrings are added to it.  This means that standard key
search and garbage collection facilities are available.

The user_namespace struct's register goes away when it does and anything left
in it is then automatically gc'd.

Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Simo Sorce <simo@redhat.com>
cc: Serge E. Hallyn <serge.hallyn@ubuntu.com>
cc: Eric W. Biederman <ebiederm@xmission.com>
2013-09-24 10:35:19 +01:00

170 lines
4.6 KiB
C

/* General persistent per-UID keyrings register
*
* Copyright (C) 2013 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/user_namespace.h>
#include "internal.h"
unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
/*
* Create the persistent keyring register for the current user namespace.
*
* Called with the namespace's sem locked for writing.
*/
static int key_create_persistent_register(struct user_namespace *ns)
{
struct key *reg = keyring_alloc(".persistent_register",
KUIDT_INIT(0), KGIDT_INIT(0),
current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(reg))
return PTR_ERR(reg);
ns->persistent_keyring_register = reg;
return 0;
}
/*
* Create the persistent keyring for the specified user.
*
* Called with the namespace's sem locked for writing.
*/
static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
struct keyring_index_key *index_key)
{
struct key *persistent;
key_ref_t reg_ref, persistent_ref;
if (!ns->persistent_keyring_register) {
long err = key_create_persistent_register(ns);
if (err < 0)
return ERR_PTR(err);
} else {
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
persistent_ref = find_key_to_update(reg_ref, index_key);
if (persistent_ref)
return persistent_ref;
}
persistent = keyring_alloc(index_key->description,
uid, INVALID_GID, current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA,
ns->persistent_keyring_register);
if (IS_ERR(persistent))
return ERR_CAST(persistent);
return make_key_ref(persistent, true);
}
/*
* Get the persistent keyring for a specific UID and link it to the nominated
* keyring.
*/
static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
key_ref_t dest_ref)
{
struct keyring_index_key index_key;
struct key *persistent;
key_ref_t reg_ref, persistent_ref;
char buf[32];
long ret;
/* Look in the register if it exists */
index_key.type = &key_type_keyring;
index_key.description = buf;
index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
if (ns->persistent_keyring_register) {
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
down_read(&ns->persistent_keyring_register_sem);
persistent_ref = find_key_to_update(reg_ref, &index_key);
up_read(&ns->persistent_keyring_register_sem);
if (persistent_ref)
goto found;
}
/* It wasn't in the register, so we'll need to create it. We might
* also need to create the register.
*/
down_write(&ns->persistent_keyring_register_sem);
persistent_ref = key_create_persistent(ns, uid, &index_key);
up_write(&ns->persistent_keyring_register_sem);
if (!IS_ERR(persistent_ref))
goto found;
return PTR_ERR(persistent_ref);
found:
ret = key_task_permission(persistent_ref, current_cred(), KEY_LINK);
if (ret == 0) {
persistent = key_ref_to_ptr(persistent_ref);
ret = key_link(key_ref_to_ptr(dest_ref), persistent);
if (ret == 0) {
key_set_timeout(persistent, persistent_keyring_expiry);
ret = persistent->serial;
}
}
key_ref_put(persistent_ref);
return ret;
}
/*
* Get the persistent keyring for a specific UID and link it to the nominated
* keyring.
*/
long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
{
struct user_namespace *ns = current_user_ns();
key_ref_t dest_ref;
kuid_t uid;
long ret;
/* -1 indicates the current user */
if (_uid == (uid_t)-1) {
uid = current_uid();
} else {
uid = make_kuid(ns, _uid);
if (!uid_valid(uid))
return -EINVAL;
/* You can only see your own persistent cache if you're not
* sufficiently privileged.
*/
if (uid_eq(uid, current_uid()) &&
uid_eq(uid, current_suid()) &&
uid_eq(uid, current_euid()) &&
uid_eq(uid, current_fsuid()) &&
!ns_capable(ns, CAP_SETUID))
return -EPERM;
}
/* There must be a destination keyring */
dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_WRITE);
if (IS_ERR(dest_ref))
return PTR_ERR(dest_ref);
if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
ret = -ENOTDIR;
goto out_put_dest;
}
ret = key_get_persistent(ns, uid, dest_ref);
out_put_dest:
key_ref_put(dest_ref);
return ret;
}