/* Keyring handling
*
* Copyright (C) 2004-2005, 2008, 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 License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/export.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <keys/keyring-type.h>
#include <keys/user-type.h>
#include <linux/assoc_array_priv.h>
#include <linux/uaccess.h>
#include "internal.h"
/*
* When plumbing the depths of the key tree, this sets a hard limit
* set on how deep we're willing to go.
*/
#define KEYRING_SEARCH_MAX_DEPTH 6
/*
* We keep all named keyrings in a hash to speed looking them up.
*/
#define KEYRING_NAME_HASH_SIZE (1 << 5)
/*
* We mark pointers we pass to the associative array with bit 1 set if
* they're keyrings and clear otherwise.
*/
#define KEYRING_PTR_SUBTYPE 0x2UL
static inline bool keyring_ptr_is_keyring(const struct assoc_array_ptr *x)
{
return (unsigned long)x & KEYRING_PTR_SUBTYPE;
}
static inline struct key *keyring_ptr_to_key(const struct assoc_array_ptr *x)
{
void *object = assoc_array_ptr_to_leaf(x);
return (struct key *)((unsigned long)object & ~KEYRING_PTR_SUBTYPE);
}
static inline void *keyring_key_to_ptr(struct key *key)
{
if (key->type == &key_type_keyring)
return (void *)((unsigned long)key | KEYRING_PTR_SUBTYPE);
return key;
}
static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE];
static DEFINE_RWLOCK(keyring_name_lock);
static inline unsigned keyring_hash(const char *desc)
{
unsigned bucket = 0;
for (; *desc; desc++)
bucket += (unsigned char)*desc;
return bucket & (KEYRING_NAME_HASH_SIZE - 1);
}
/*
* The keyring key type definition. Keyrings are simply keys of this type and
* can be treated as ordinary keys in addition to having their own special
* operations.
*/
static int keyring_preparse(struct key_preparsed_payload *prep);
static void keyring_free_preparse(struct key_preparsed_payload *prep);
static int keyring_instantiate(struct key *keyring,
struct key_preparsed_payload *prep);
static void keyring_revoke(struct key *keyring);
static void keyring_destroy(struct key *keyring);
static void keyring_describe(const struct key *keyring, struct seq_file *m);
static long keyring_read(const struct key *keyring,
char __user *buffer, size_t buflen);
struct key_type key_type_keyring = {
.name = "keyring",
.def_datalen = 0,
.preparse = keyring_preparse,
.free_preparse = keyring_free_preparse,
.instantiate = keyring_instantiate,
.revoke = keyring_revoke,
.destroy = keyring_destroy,
.describe = keyring_describe,
.read = keyring_read,
};
EXPORT_SYMBOL(key_type_keyring);
/*
* Semaphore to serialise link/link calls to prevent two link calls in parallel
* introducing a cycle.
*/
static DECLARE_RWSEM(keyring_serialise_link_sem);
/*
* Publish the name of a keyring so that it can be found by name (if it has
* one).
*/
static void keyring_publish_name(struct key *keyring)
{
int bucket;
if (keyring->description) {
bucket = keyring_hash(keyring->description);
write_lock(&keyring_name_lock);
if (!keyring_name_hash[bucket].next)
INIT_LIST_HEAD(&keyring_name_hash[bucket]);
list_add_tail(&keyring->name_link,
&keyring_name_hash[bucket]);
write_unlock(&keyring_name_lock);
}
}
/*
* Preparse a keyring payload
*/
static int keyring_preparse(struct key_preparsed_payload *prep)
{
return prep->datalen != 0 ? -EINVAL : 0;
}
/*
* Free a preparse of a user defined key payload
*/
static void keyring_free_preparse(struct key_preparsed_payload *prep)
{
}
/*
* Initialise a keyring.
*
* Returns 0 on success, -EINVAL if given any data.
*/
static int keyring_instantiate(struct key *keyring,
struct key_preparsed_payload *prep)
{
assoc_array_init(&keyring->keys);
/* make the keyring available by name if it has one */
keyring_publish_name(keyring);
return 0;
}
/*
* Multiply 64-bits by 32-bits to 96-bits and fold back to 64-bit. Ideally we'd
* fold the carry back too, but that requires inline asm.
*/
static u64 mult_64x32_and_fold(u64 x, u32 y)
{
u64 hi = (u64)(u32)(x >> 32) * y;
u64 lo = (u64)(u32)(x) * y;
return lo + ((u64)(u32)hi << 32) + (u32)(hi >> 32);
}
/*
* Hash a key type and description.
*/
static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)
{
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK;
const char *description = index_key->description;
unsigned long hash, type;
u32 piece;
u64 acc;
int n, desc_len = index_key->desc_len;
type = (unsigned long)index_key->type;
acc = mult_64x32_and_fold(type, desc_len + 13);
acc = mult_64x32_and_fold(acc, 9207);
for (;;) {
n = desc_len;
if (n <= 0)
break;
if (n > 4)
n = 4;
piece = 0;
memcpy(&piece, description, n);
description += n;
desc_len -= n;
acc = mult_64x32_and_fold(acc, piece);
acc = mult_64x32_and_fold(acc, 9207);
}
/* Fold the hash down to 32 bits if need be. */
hash = acc;
if (ASSOC_ARRAY_KEY_CHUNK_SIZE == 32)
hash ^= acc >> 32;
/* Squidge all the keyring
|