summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2013-09-28 14:12:58 -0400
committerGreg Hudson <ghudson@mit.edu>2013-10-02 10:41:34 -0400
commit7c69a0372db5b7ed670ef3099a97942ede7a4739 (patch)
treeb6561c3c5e5675fad5617e5795b9654a3e3f5cba
parentc1e8d03a6254e3ce86a71eed31e4c127e3324f9b (diff)
downloadkrb5-7c69a0372db5b7ed670ef3099a97942ede7a4739.tar.gz
krb5-7c69a0372db5b7ed670ef3099a97942ede7a4739.tar.xz
krb5-7c69a0372db5b7ed670ef3099a97942ede7a4739.zip
Support new KEYRING anchor names and big_key keys
Add support for the new anchor names persistent, user, and session. The persistent anchor attempts to use a persistent keyring for a specified uid, and falls back to the user keyring if it cannot; the collection is stored at a fixed name within the persistent or user keyring. The session anchor uses the session keyring without legacy semantics. For all keyring types except legacy, attempt to use the "big_key" key type on systems which have keyctl_get_persistent. (They are essentially unrelated features, but were added at the same time.) This key type is stored in a kernel tmpfs and can store larger tickets. Since kernel commit 96b5c8fea6c0861621051290d705ec2e971963f1, new keys created by add_key() only have VIEW permission for the user, and the rest of the permissions require "possession," which means there is a path from the thread, process, or session keyring to the key. For the user and persistent anchor types, we link the collection into the process keyring to ensure that we have a possession rights on the collection. Adapted from a patch by simo@redhat.com. ticket: 7711
-rw-r--r--src/aclocal.m410
-rw-r--r--src/lib/krb5/ccache/cc_keyring.c164
-rw-r--r--src/lib/krb5/error_tables/k5e1_err.et1
3 files changed, 148 insertions, 27 deletions
diff --git a/src/aclocal.m4 b/src/aclocal.m4
index 210c473eb..7be77c28d 100644
--- a/src/aclocal.m4
+++ b/src/aclocal.m4
@@ -89,6 +89,7 @@ KRB5_AC_INITFINI
KRB5_AC_ENABLE_THREADS
KRB5_AC_FIND_DLOPEN
KRB5_AC_KEYRING_CCACHE
+KRB5_AC_PERSISTENT_KEYRING
])dnl
dnl Maintainer mode, akin to what automake provides, 'cept we don't
@@ -1658,3 +1659,12 @@ AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[
]))
])dnl
dnl
+dnl If libkeyutils supports persistent keyrings, use them
+AC_DEFUN(KRB5_AC_PERSISTENT_KEYRING,[
+ AC_CHECK_HEADERS([keyutils.h],
+ AC_CHECK_LIB(keyutils, keyctl_get_persistent,
+ [AC_DEFINE(HAVE_PERSISTENT_KEYRING, 1,
+ [Define if persistent keyrings are supported])
+ ]))
+])dnl
+dnl
diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c
index f30f87ee9..795ccd63c 100644
--- a/src/lib/krb5/ccache/cc_keyring.c
+++ b/src/lib/krb5/ccache/cc_keyring.c
@@ -126,7 +126,20 @@ debug_print(char *fmt, ...)
#endif
/*
- * We always use "user" key type
+ * We try to use the big_key key type for credentials except in legacy caches.
+ * We fall back to the user key type if the kernel does not support big_key.
+ * If the library doesn't support keyctl_get_persistent(), we don't even try
+ * big_key since the two features were added at the same time.
+ */
+#ifdef HAVE_PERSISTENT_KEYRING
+#define KRCC_CRED_KEY_TYPE "big_key"
+#else
+#define KRCC_CRED_KEY_TYPE "user"
+#endif
+
+/*
+ * We use the "user" key type for collection primary names, for cache principal
+ * names, and for credentials in legacy caches.
*/
#define KRCC_KEY_TYPE_USER "user"
@@ -160,7 +173,7 @@ debug_print(char *fmt, ...)
* If the library context does not specify a keyring collection, unique ccaches
* will be created within this collection.
*/
-#define KRCC_DEFAULT_UNIQUE_COLLECTION "__krb5_unique__"
+#define KRCC_DEFAULT_UNIQUE_COLLECTION "session:__krb5_unique__"
/*
* Collection keyring names begin with this prefix. We use a prefix so that a
@@ -170,6 +183,12 @@ debug_print(char *fmt, ...)
#define KRCC_CCCOL_PREFIX "_krb_"
/*
+ * For the "persistent" anchor type, we look up or create this fixed keyring
+ * name within the per-UID persistent keyring.
+ */
+#define KRCC_PERSISTENT_KEYRING_NAME "_krb"
+
+/*
* Keyring name prefix and length of random name part
*/
#define KRCC_NAME_PREFIX "krb_ccache_"
@@ -177,8 +196,11 @@ debug_print(char *fmt, ...)
#define KRCC_COLLECTION_VERSION 1
+#define KRCC_PERSISTENT_ANCHOR "persistent"
#define KRCC_PROCESS_ANCHOR "process"
#define KRCC_THREAD_ANCHOR "thread"
+#define KRCC_SESSION_ANCHOR "session"
+#define KRCC_USER_ANCHOR "user"
#define KRCC_LEGACY_ANCHOR "legacy"
#define KRB5_OK 0
@@ -212,6 +234,7 @@ typedef struct _krb5_krcc_data
key_serial_t cache_id; /* keyring representing ccache */
key_serial_t princ_id; /* key holding principal info */
krb5_timestamp changetime;
+ krb5_boolean is_legacy_type;
} krb5_krcc_data;
/* Passed internally to assure we don't go past the bounds of our buffer */
@@ -391,19 +414,58 @@ krb5_krcc_unparse_index(krb5_context context, krb5_int32 version,
/* Note the following is a stub function for Linux */
extern krb5_error_code krb5_change_cache(void);
-/* Find or create a keyring within parent with the given name. */
+/*
+ * GET_PERSISTENT(uid) acquires the persistent keyring for uid, or falls back
+ * to the user keyring if uid matches the current effective uid.
+ */
+
+static key_serial_t
+get_persistent_fallback(uid_t uid)
+{
+ return (uid == geteuid()) ? KEY_SPEC_USER_KEYRING : -1;
+}
+
+#ifdef HAVE_PERSISTENT_KEYRING
+#define GET_PERSISTENT get_persistent_real
+static key_serial_t
+get_persistent_real(uid_t uid)
+{
+ key_serial_t key;
+
+ key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
+ return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) :
+ key;
+}
+#else
+#define GET_PERSISTENT get_persistent_fallback
+#endif
+
+/*
+ * Find or create a keyring within parent with the given name. If possess is
+ * nonzero, also make sure the key is linked from possess. This is necessary
+ * to ensure that we have possession rights on the key when the parent is the
+ * user or persistent keyring.
+ */
static krb5_error_code
-find_or_create_keyring(key_serial_t parent, const char *name,
- key_serial_t *key_out)
+find_or_create_keyring(key_serial_t parent, key_serial_t possess,
+ const char *name, key_serial_t *key_out)
{
key_serial_t key;
*key_out = -1;
- key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, 0);
+ key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
if (key == -1) {
- key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
- if (key == -1)
- return errno;
+ if (possess != 0) {
+ key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
+ if (key == -1)
+ return errno;
+ if (keyctl_link(key, parent) == -1)
+ return errno;
+ } else {
+ key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
+ if (key == -1)
+ return errno;
+ }
}
*key_out = key;
return 0;
@@ -530,24 +592,57 @@ get_collection(const char *anchor_name, const char *collection_name,
key_serial_t *collection_id_out)
{
krb5_error_code ret;
- key_serial_t anchor_id;
+ key_serial_t persistent_id, anchor_id, possess_id = 0;
char *ckname;
+ long uidnum;
*collection_id_out = 0;
- if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0)
+ if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
+ /*
+ * The collection name is a uid (or empty for the current effective
+ * uid), and we look up a fixed keyring name within the persistent
+ * keyring for that uid. We link it to the process keyring to ensure
+ * that we have possession rights on the collection key.
+ */
+ if (*collection_name != '\0') {
+ errno = 0;
+ uidnum = strtol(collection_name, NULL, 10);
+ if (errno)
+ return KRB5_KCC_INVALID_UID;
+ } else {
+ uidnum = geteuid();
+ }
+ persistent_id = GET_PERSISTENT(uidnum);
+ if (persistent_id == -1)
+ return KRB5_KCC_INVALID_UID;
+ return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
+ KRCC_PERSISTENT_KEYRING_NAME,
+ collection_id_out);
+ }
+
+ if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
anchor_id = KEY_SPEC_PROCESS_KEYRING;
- else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0)
+ } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
anchor_id = KEY_SPEC_THREAD_KEYRING;
- else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0)
+ } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
+ anchor_id = KEY_SPEC_SESSION_KEYRING;
+ } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
+ /* The user keyring does not confer possession, so we need to link the
+ * collection to the process keyring to maintain possession rights. */
+ anchor_id = KEY_SPEC_USER_KEYRING;
+ possess_id = KEY_SPEC_PROCESS_KEYRING;
+ } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
anchor_id = KEY_SPEC_SESSION_KEYRING;
- else
+ } else {
return KRB5_KCC_INVALID_ANCHOR;
+ }
/* Look up the collection keyring name within the anchor keyring. */
if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
return ENOMEM;
- ret = find_or_create_keyring(anchor_id, ckname, collection_id_out);
+ ret = find_or_create_keyring(anchor_id, possess_id, ckname,
+ collection_id_out);
free(ckname);
return ret;
}
@@ -703,6 +798,25 @@ cleanup:
return ret;
}
+static krb5_error_code
+add_cred_key(const char *name, const void *payload, size_t plen,
+ key_serial_t cache_id, krb5_boolean legacy_type)
+{
+ key_serial_t key;
+
+ if (!legacy_type) {
+ /* Try the preferred cred key type; fall back if no kernel support. */
+ key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
+ if (key != -1)
+ return 0;
+ else if (errno != EINVAL && errno != ENODEV)
+ return errno;
+ }
+ /* Use the user key type. */
+ key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
+ return (key == -1) ? errno : 0;
+}
+
/*
* Modifies:
* id
@@ -737,7 +851,7 @@ krb5_krcc_initialize(krb5_context context, krb5_ccache id,
* key if it still isn't there. */
p = strrchr(data->name, ':');
cache_name = (p != NULL) ? p + 1 : data->name;
- kret = find_or_create_keyring(data->collection_id, cache_name,
+ kret = find_or_create_keyring(data->collection_id, 0, cache_name,
&data->cache_id);
if (kret)
goto out;
@@ -1146,6 +1260,7 @@ krb5_krcc_new_data(const char *anchor_name, const char *collection_name,
d->cache_id = cache_id;
d->collection_id = collection_id;
d->changetime = 0;
+ d->is_legacy_type = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
krb5_krcc_update_change_time(d);
*datapp = d;
@@ -1335,7 +1450,6 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds)
krb5_krcc_data *d = (krb5_krcc_data *) id->data;
char *payload = NULL;
unsigned int payloadlen;
- key_serial_t newkey;
char *keyname = NULL;
DEBUG_PRINT(("krb5_krcc_store: entered\n"));
@@ -1362,16 +1476,12 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds)
/* Add new key (credentials) into keyring */
DEBUG_PRINT(("krb5_krcc_store: adding new key '%s' to keyring %d\n",
keyname, d->cache_id));
- newkey = add_key(KRCC_KEY_TYPE_USER, keyname, payload,
- payloadlen, d->cache_id);
- if (newkey < 0) {
- kret = errno;
- DEBUG_PRINT(("Error adding user key '%s': %s\n",
- keyname, strerror(kret)));
- } else {
- kret = KRB5_OK;
- krb5_krcc_update_change_time(d);
- }
+ kret = add_cred_key(keyname, payload, payloadlen, d->cache_id,
+ d->is_legacy_type);
+ if (kret)
+ goto errout;
+
+ krb5_krcc_update_change_time(d);
errout:
if (keyname)
diff --git a/src/lib/krb5/error_tables/k5e1_err.et b/src/lib/krb5/error_tables/k5e1_err.et
index 9a7b4c445..071b7f218 100644
--- a/src/lib/krb5/error_tables/k5e1_err.et
+++ b/src/lib/krb5/error_tables/k5e1_err.et
@@ -37,4 +37,5 @@ error_code KRB5KDC_ERR_DISCARD, "The KDC should discard this request"
error_code KRB5_DCC_CANNOT_CREATE, "Can't create new subsidiary cache"
error_code KRB5_KCC_INVALID_ANCHOR, "Invalid keyring anchor name"
error_code KRB5_KCC_UNKNOWN_VERSION, "Unknown keyring collection version"
+error_code KRB5_KCC_INVALID_UID, "Invalid UID in persistent keyring name"
end