lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 22 Mar 2016 01:46:17 +0100
From:	Kirill Marinushkin <k.marinushkin@...il.com>
To:	dhowells@...hat.com
Cc:	linux-kernel@...r.kernel.org, keyrings@...r.kernel.org,
	linux-security-module@...r.kernel.org, k.marinushkin@...il.com
Subject: [PATCH] Security: Keys: Added derived keytype

For details see
Documentation/security/keys-derived.txt

Signed-off-by: Kirill Marinushkin <k.marinushkin@...il.com>
---
 Documentation/security/keys-derived.txt |  82 +++++
 include/keys/derived-type.h             |  33 ++
 security/keys/Kconfig                   |  11 +
 security/keys/Makefile                  |   1 +
 security/keys/derived.c                 | 577 ++++++++++++++++++++++++++++++++
 5 files changed, 704 insertions(+)
 create mode 100644 Documentation/security/keys-derived.txt
 create mode 100644 include/keys/derived-type.h
 create mode 100644 security/keys/derived.c

diff --git a/Documentation/security/keys-derived.txt b/Documentation/security/keys-derived.txt
new file mode 100644
index 0000000..3c1d65c
--- /dev/null
+++ b/Documentation/security/keys-derived.txt
@@ -0,0 +1,82 @@
+			Derived Keys
+
+Derived is a keytype of the kernel keyring facility.
+The key secret is derived from the secret value given by user.
+Optionally user may specify:
+	- hash function used for derivation;
+	- salt value;
+	- number of iterations.
+Both secret value and salt value may be given in one of the formats:
+	- plain data;
+	- hex string;
+	- size of data to generate randomly.
+If no optional parameters are specified, the key is derived from
+the plain secret value with sha256, no salt, 1 iteration.
+Derived keys store as a payload:
+	- derived key;
+	- salt;
+	- number of iterations;
+	- name of derivation algorithm;
+	- name of RNG algorithm.
+From userspace only the derived key value is returned on read.
+
+Usage:
+	keyctl add derived name "key [options]" ring
+
+	mandatory parameter:
+		key					- key secret value
+
+	options:
+		kf=, keyformat=		- key secret value format, see dataformat below
+		s=,  salt=			- salt value,
+								default is empty (no salt)
+		sf=, saltformat=	- salt value format, see dataformat below
+		i=,  iterations=	- number of itaretions,
+								default is 1, maximum is 0x000FFFFF
+		a=,  algorithm=		- name of crypto API hash derivation algorithm,
+								default is sha256
+		r=,  rng=			- name of crypto API RNG algorithm,
+								default is stdrng
+
+	dataformat:
+		plain				- data is a plain value, used by default
+		hex					- data is a hex string
+		rand				- data is a size of random value to be generated
+
+Examples:
+
+Create a simple derived key
+
+	$ keyctl add derived key0 secret0 @u
+	925448848
+
+	$ keyctl read 925448848
+	32 bytes of data in key:
+	97699b7c c0a0ed83 b78b2002 f0e57046 ee561be6 942bec25 6fe201ab ba552a9e
+
+Create a derived key from plain secret, hex salt
+
+	$ keyctl add derived key0 "secret0 s=65a4fe09 sf=hex" @u
+	847728695
+
+	$ keyctl read 847728695
+	32 bytes of data in key:
+	1c64cbb9 cc4dffff a94f8efe dce813d0 5def4a28 97c02336 6c95737b f2b152be
+
+Create a derived key from hex secret value, 32-byte random salt, 65536 iterations
+
+	$keyctl add derived key0 "09afde6781ff kf=hex s=32 sf=rand i=65536" @u
+	604146072
+
+	$ keyctl read 604146072
+	32 bytes of data in key:
+	a5b494b3 b6e3e26c bb9511b1 b16ce60e 99edf63e d8fbc3c2 ba38b195 229e3f43
+
+Create a derived key with sha1 algorithm
+
+	$ keyctl add derived key0 "secret0 a=sha1" @u
+	56670858
+
+	$ keyctl read 56670858
+	20 bytes of data in key:
+	d16cd26f bc3d44a6 16b8d0b2 ce8b5ddc c93e964d
diff --git a/include/keys/derived-type.h b/include/keys/derived-type.h
new file mode 100644
index 0000000..24772a1
--- /dev/null
+++ b/include/keys/derived-type.h
@@ -0,0 +1,33 @@
+/*
+ * Derived key type
+ *
+ * For details see
+ * Documentation/security/keys-derived.txt
+ *
+ * Copyright (C) 2016
+ * Written by Kirill Marinushkin (kmarinushkin@...il.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.
+ *
+ */
+
+#ifndef INCLUDE_KEYS_DERIVED_TYPE_H_
+#define INCLUDE_KEYS_DERIVED_TYPE_H_
+
+#include <linux/key.h>
+
+extern struct key_type key_type_derived;
+
+extern int derived_instantiate(struct key *key,
+		struct key_preparsed_payload *prep);
+extern int derived_update(struct key *key,
+		struct key_preparsed_payload *prep);
+extern long derived_read(const struct key *key,
+		char __user *buffer, size_t buflen);
+extern void derived_revoke(struct key *key);
+extern void derived_destroy(struct key *key);
+
+#endif /* INCLUDE_KEYS_DERIVED_TYPE_H_ */
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index fe4d74e..261994f 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -81,3 +81,14 @@ config ENCRYPTED_KEYS
 	  Userspace only ever sees/stores encrypted blobs.
 
 	  If you are unsure as to whether this is required, answer N.
+
+config DERIVED_KEYS
+	tristate "Derived keys"
+	depends on KEYS
+	select CRYPTO
+	select CRYPTO_SHA256
+	select CRYPTO_RNG
+	help
+	  This option provides support for derived key type.
+
+	  If you are unsure as to whether this is required, answer N.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index dfb3a7b..fbe954d 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
 obj-$(CONFIG_BIG_KEYS) += big_key.o
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
+obj-$(CONFIG_DERIVED_KEYS) += derived.o
diff --git a/security/keys/derived.c b/security/keys/derived.c
new file mode 100644
index 0000000..18085ce
--- /dev/null
+++ b/security/keys/derived.c
@@ -0,0 +1,577 @@
+/*
+ * Derived key type
+ *
+ * For details see
+ * Documentation/security/keys-derived.txt
+ *
+ * Copyright (C) 2016
+ * Written by Kirill Marinushkin (kmarinushkin@...il.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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <linux/parser.h>
+#include <linux/key.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include <keys/derived-type.h>
+#include <crypto/hash.h>
+#include <crypto/rng.h>
+#include "internal.h"
+
+/* KERN_ERR prefix */
+#define PREFIX	"derived: "
+
+/* Limits */
+#define ITER_MAX_VAL		0x000FFFFF
+#define SALT_MAX_SIZE		1024
+#define RAND_MAX_SIZE		1024
+
+/* Default values */
+#define ITER_DEFAULT		1
+#define ALG_NAME_DEFAULT	"sha256"
+#define RNG_NAME_DEFAULT	"stdrng"
+
+/* Options */
+enum {
+	OPT_SHORT_SALT,
+	OPT_LONG_SALT,
+	OPT_SHORT_ITER,
+	OPT_LONG_ITER,
+	OPT_SHORT_ALG,
+	OPT_LONG_ALG,
+	OPT_SHORT_RNG,
+	OPT_LONG_RNG,
+	OPT_SHORT_KEY_F,
+	OPT_LONG_KEY_F,
+	OPT_SHORT_SALT_F,
+	OPT_LONG_SALT_F
+};
+
+/* Options data formats */
+enum derived_opt_format {
+	OPT_FORMAT_ERR = -1,
+	OPT_FORMAT_PLAIN,
+	OPT_FORMAT_HEX,
+	OPT_FORMAT_RAND
+};
+
+/* Options data index */
+enum {
+	OPT_IND_KEY = 0,
+	OPT_IND_SALT,
+	OPT_IND_NUM /* number of indexes */
+};
+
+struct derived_blob {
+	u8 *data;
+	size_t *lenp;
+};
+
+struct derived_f_blob {
+	enum derived_opt_format format;
+	struct derived_blob *b;
+};
+
+struct derived_key_payload {
+	struct rcu_head rcu;	/* RCU destructor */
+	char *alg_name;			/* null-terminated digest algorithm name */
+	char *rng_name;			/* null-terminated random generator algorithm name */
+	u64 iter;				/* number of iterations */
+	unsigned int saltlen;	/* length of salt */
+	unsigned char *salt;	/* salt */
+	unsigned int datalen;	/* length of derived data */
+	unsigned char *data;	/* derived data */
+};
+
+/* Get option data format specified by user */
+static enum derived_opt_format get_opt_format(const char *arg)
+{
+	if (!strcmp(arg, "plain"))
+		return OPT_FORMAT_PLAIN;
+	if (!strcmp(arg, "hex"))
+		return OPT_FORMAT_HEX;
+	if (!strcmp(arg, "rand"))
+		return OPT_FORMAT_RAND;
+	return OPT_FORMAT_ERR;
+}
+
+/* Generate random data */
+static int gen_random(const char *rnd_name, u8 *buf, unsigned int len)
+{
+	int ret = -EINVAL;
+	struct crypto_rng *rng = NULL;
+
+	rng = crypto_alloc_rng(rnd_name, 0, 0);
+	if (IS_ERR(rng)) {
+		pr_err(PREFIX "RNG alloc failed");
+		return -EINVAL;
+	}
+
+	ret = crypto_rng_get_bytes(rng, buf, len);
+	if (ret < 0) {
+		pr_err(PREFIX "RNG get bytes failed");
+		ret = -EFAULT;
+	}
+
+	if (rng)
+		crypto_free_rng(rng);
+	return ret;
+}
+
+/* Parse options specified by user */
+static int parse_options(char **args_str,
+		struct derived_key_payload *payload, struct derived_blob *ukey)
+{
+	int ret = -EINVAL;
+	substring_t args[MAX_OPT_ARGS];
+	char *p = *args_str;
+	int token;
+	int i;
+	unsigned short templen;
+	unsigned int tempu;
+	u64 tempul;
+	struct derived_blob usalt = {NULL};
+	struct derived_f_blob v[OPT_IND_NUM] = {
+			{OPT_FORMAT_PLAIN, NULL}
+	};
+	const match_table_t key_tokens = {
+			{OPT_SHORT_SALT, "s=%s"},
+			{OPT_LONG_SALT, "salt=%s"},
+			{OPT_SHORT_ITER, "i=%u"},
+			{OPT_LONG_ITER, "iterations=%u"},
+			{OPT_SHORT_ALG, "a=%s"},
+			{OPT_LONG_ALG, "algorithm=%s"},
+			{OPT_SHORT_RNG, "r=%s"},
+			{OPT_LONG_RNG, "rng=%s"},
+			{OPT_SHORT_KEY_F, "kf=%s"},
+			{OPT_LONG_KEY_F, "keyformat=%s"},
+			{OPT_SHORT_SALT_F, "sf=%s"},
+			{OPT_LONG_SALT_F, "saltformat=%s"}
+	};
+
+	/* set defaults */
+	payload->iter = ITER_DEFAULT;
+	payload->alg_name = kstrdup(ALG_NAME_DEFAULT, GFP_KERNEL);
+	if (!payload->alg_name) {
+		pr_err(PREFIX "default algorithm name alloc failed");
+		return -ENOMEM;
+	}
+	payload->rng_name = kstrdup(RNG_NAME_DEFAULT, GFP_KERNEL);
+	if (!payload->rng_name) {
+		pr_err(PREFIX "default RNG name alloc failed");
+		return -ENOMEM;
+	}
+
+	/* parse key */
+	ukey->data = strsep(args_str, " \t");
+	if (!ukey->data) {
+		pr_err(PREFIX "input string separation failed");
+		return -EINVAL;
+	}
+	ukey->lenp = kmalloc(sizeof(*ukey->lenp), GFP_KERNEL);
+	if (!ukey->lenp) {
+		pr_err(PREFIX "input key secret alloc failed");
+		return -ENOMEM;
+	}
+	*ukey->lenp = strlen(ukey->data);
+
+	/* prepare format blob array */
+	v[OPT_IND_KEY].b = ukey;
+	v[OPT_IND_SALT].b = &usalt;
+
+	/* parse options */
+	while ((p = strsep(args_str, " \t"))) {
+		if (*p == '\0' || *p == ' ' || *p == '\t')
+			continue;
+
+		token = match_token(p, key_tokens, args);
+
+		switch (token) {
+
+		case OPT_SHORT_SALT: /* salt */
+		case OPT_LONG_SALT:
+			templen = args[0].to - args[0].from;
+			if (templen < 0 || templen > SALT_MAX_SIZE) {
+				pr_err(PREFIX "invalid salt length");
+				return -EINVAL;
+			}
+			payload->salt = kstrndup(args[0].from, templen, GFP_KERNEL);
+			if (!payload->salt) {
+				pr_err(PREFIX "salt alloc failed");
+				return -ENOMEM;
+			}
+			payload->saltlen = templen;
+			usalt.data = payload->salt;
+			usalt.lenp = &payload->saltlen;
+			break;
+
+		case OPT_SHORT_ITER: /* iterations */
+		case OPT_LONG_ITER:
+			if (kstrtou64(args[0].from, 0, &tempul)
+					|| tempul == 0
+					|| tempul > ITER_MAX_VAL) {
+				pr_err(PREFIX "invalid iterations number");
+				return -EINVAL;
+			}
+			payload->iter = tempul;
+			break;
+
+		case OPT_SHORT_ALG: /* alg name */
+		case OPT_LONG_ALG:
+			payload->alg_name = kstrdup(args[0].from, GFP_KERNEL);
+			if (!payload->alg_name) {
+				pr_err(PREFIX "algorithm name alloc failed");
+				return -ENOMEM;
+			}
+			break;
+
+		case OPT_SHORT_RNG: /* rng name */
+		case OPT_LONG_RNG:
+			payload->rng_name = kstrdup(args[0].from, GFP_KERNEL);
+			if (!payload->rng_name) {
+				pr_err(PREFIX "RNG name alloc failed");
+				return -ENOMEM;
+			}
+			break;
+
+		case OPT_SHORT_KEY_F: /* key format */
+		case OPT_LONG_KEY_F:
+			v[OPT_IND_KEY].format = get_opt_format(args[0].from);
+			if (v[OPT_IND_KEY].format == OPT_FORMAT_ERR) {
+				pr_err(PREFIX "invalid key format");
+				return -EINVAL;
+			}
+			break;
+
+		case OPT_SHORT_SALT_F: /* salt format */
+		case OPT_LONG_SALT_F:
+			v[OPT_IND_SALT].format = get_opt_format(args[0].from);
+			if (v[OPT_IND_SALT].format == OPT_FORMAT_ERR) {
+				pr_err(PREFIX "invalid salt format");
+				return -EINVAL;
+			}
+			break;
+
+		default:
+			pr_err(PREFIX "unsupported option");
+			return -EINVAL;
+		}
+	}
+
+	/* modify options according to format */
+	for (i = 0; i < OPT_IND_NUM; i++) {
+		if (!v[i].b || !v[i].b->data)
+			continue;
+
+		switch (v[i].format) {
+
+		case OPT_FORMAT_HEX:
+			if (*v[i].b->lenp % 2) {
+				pr_err(PREFIX "invalid hex string");
+				return -EINVAL;
+			}
+			*v[i].b->lenp /= 2;
+			ret = hex2bin(v[i].b->data, v[i].b->data, *v[i].b->lenp);
+			if (ret) {
+				pr_err(PREFIX "invalid hex string");
+				return -EINVAL;
+			}
+			break;
+
+		case OPT_FORMAT_RAND:
+			if (kstrtouint(v[i].b->data, 0, &tempu)
+					|| tempu == 0
+					|| tempu > RAND_MAX_SIZE) {
+				pr_err(PREFIX "invalid random size");
+				return -EINVAL;
+			}
+			v[i].b->data = kmalloc(tempu, GFP_KERNEL);
+			if (!v[i].b->data) {
+				pr_err(PREFIX "random data alloc failed");
+				return -ENOMEM;
+			}
+			*v[i].b->lenp = tempu;
+			ret = gen_random(payload->rng_name, v[i].b->data, *v[i].b->lenp);
+			if (ret)
+				return ret;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/* Free and zero payload fields */
+static void free_payload_content(struct derived_key_payload *payload)
+{
+	if (payload->alg_name)
+		kzfree(payload->alg_name);
+	if (payload->rng_name)
+		kzfree(payload->rng_name);
+	if (payload->data)
+		kzfree(payload->data);
+	if (payload->salt)
+		kzfree(payload->salt);
+}
+
+/* Fill derived key payload with data specified by user */
+static int fill_payload(struct derived_key_payload *payload,
+		struct key_preparsed_payload *prep)
+{
+	int ret = -EINVAL;
+	char *args_str = NULL;
+	struct derived_blob ukey = {NULL};
+	struct crypto_shash *sh = NULL;
+	struct shash_desc *sdesc = NULL;
+	unsigned int i;
+
+	if (!payload || prep->datalen <= 0 || prep->datalen > 32767 || !prep->data) {
+		pr_err(PREFIX "invalid data for payload");
+		return -EINVAL;
+	}
+
+	args_str = kstrndup(prep->data, prep->datalen, GFP_KERNEL);
+	if (!args_str) {
+		pr_err(PREFIX "input arguments alloc failed");
+		return -EINVAL;
+	}
+
+	ret = parse_options(&args_str, payload, &ukey);
+	if (ret)
+		return ret;
+	if (!ukey.data || !ukey.lenp) {
+		pr_err(PREFIX "invalid key input parsed");
+		return -EINVAL;
+	}
+
+	/* start derivation */
+	sh = crypto_alloc_shash(payload->alg_name, 0, 0);
+	if (IS_ERR(sh)) {
+		pr_err(PREFIX "shash alloc failed");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	sdesc = kzalloc(sizeof(struct shash_desc) + crypto_shash_descsize(sh), GFP_KERNEL);
+	if (!sdesc) {
+		pr_err(PREFIX "sdesc alloc failed");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	sdesc->tfm = sh;
+	sdesc->flags = 0;
+
+	payload->datalen = crypto_shash_digestsize(sh);
+	if (payload->data)
+		kzfree(payload->data);
+	payload->data = kmalloc(payload->datalen, GFP_KERNEL);
+	if (!payload->data) {
+		pr_err(PREFIX "payload data alloc failed");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < payload->iter; i++) {
+		ret = crypto_shash_init(sdesc);
+		if (ret) {
+			pr_err(PREFIX "shash init failed");
+			goto out;
+		}
+
+		if (i == 0) {
+			/* first iteration */
+			ret = crypto_shash_update(sdesc, ukey.data, *ukey.lenp);
+			if (ret) {
+				pr_err(PREFIX "shash update failed");
+				goto out;
+			}
+
+			ret = crypto_shash_update(sdesc, payload->salt, payload->saltlen);
+			if (ret) {
+				pr_err(PREFIX "shash update failed");
+				goto out;
+			}
+		} else {
+			/* next iterations */
+			ret = crypto_shash_update(sdesc, payload->data, payload->datalen);
+			if (ret) {
+				pr_err(PREFIX "shash update failed");
+				goto out;
+			}
+		}
+
+		ret = crypto_shash_final(sdesc, payload->data);
+		if (ret) {
+			pr_err(PREFIX "shash final failed");
+			goto out;
+		}
+
+	}
+
+out:
+	if (sdesc)
+		kzfree(sdesc);
+	if (!IS_ERR(sh))
+		crypto_free_shash(sh);
+	if (args_str)
+		kzfree(args_str);
+	return ret;
+}
+
+/* Reserve payload for derived key */
+static int reserve_derived_payload(struct key *key,
+		struct derived_key_payload *payload)
+{
+	return key_payload_reserve(key, sizeof(*payload)
+			+ payload->datalen + payload->saltlen
+			+ strlen(payload->alg_name) + strlen(payload->rng_name) + 2);
+}
+
+/* Derived key instantiate */
+int derived_instantiate(struct key *key, struct key_preparsed_payload *prep)
+{
+	int ret = -EINVAL;
+	struct derived_key_payload *payload = NULL;
+
+	if (prep->datalen <= 0 || prep->datalen > 32767 || !prep->data) {
+		pr_err(PREFIX "invalid input data");
+		return -EINVAL;
+	}
+
+	payload = kzalloc(sizeof(*payload), GFP_KERNEL);
+	if (!payload) {
+		pr_err(PREFIX "payload alloc failed");
+		return -ENOMEM;
+	}
+
+	/* fill payload */
+	ret = fill_payload(payload, prep);
+	if (!ret)
+		ret = reserve_derived_payload(key, payload);
+
+	/* assign key if succeed */
+	if (!ret)
+		rcu_assign_keypointer(key, payload);
+	else
+		kzfree(key->payload.data);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(derived_instantiate);
+
+/* Derived key update */
+int derived_update(struct key *key, struct key_preparsed_payload *prep)
+{
+	int ret  = -EINVAL;
+	struct derived_key_payload *payload =
+			(struct derived_key_payload *)key->payload.data;
+
+	/* free current payload */
+	free_payload_content(payload);
+	memset(payload, 0x00, sizeof(*payload));
+
+	ret = fill_payload(payload, prep);
+	if (!ret)
+		ret = reserve_derived_payload(key, payload);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(derived_update);
+
+/* Derived key read */
+long derived_read(const struct key *key, char __user *buffer, size_t buflen)
+{
+	long len = -1;
+	struct derived_key_payload *payload = rcu_dereference_key(key);
+
+	if (!payload) {
+		pr_err(PREFIX "invalid key payload");
+		return -EINVAL;
+	}
+
+	len = payload->datalen;
+	if (buffer && buflen > 0) {
+		/* copy to buffer */
+		if (buflen < payload->datalen
+				|| copy_to_user(buffer, payload->data, payload->datalen)) {
+			pr_err(PREFIX "read key data failed");
+			return -EFAULT;
+		}
+	} /* else return without copy */
+
+	return len;
+}
+EXPORT_SYMBOL_GPL(derived_read);
+
+/* Derived key revoke */
+void derived_revoke(struct key *key)
+{
+	struct derived_key_payload *payload =
+			(struct derived_key_payload *)key->payload.data;
+
+	/* clear the quota */
+	key_payload_reserve(key, 0);
+
+	if (payload) {
+		rcu_assign_keypointer(key, NULL);
+		kfree_rcu(payload, rcu);
+	}
+}
+EXPORT_SYMBOL(derived_revoke);
+
+/* Derived key destroy */
+void derived_destroy(struct key *key)
+{
+	struct derived_key_payload *payload =
+			(struct derived_key_payload *)key->payload.data;
+
+	if (!payload)
+		return;
+
+	free_payload_content(payload);
+
+	kzfree(payload);
+}
+EXPORT_SYMBOL_GPL(derived_destroy);
+
+struct key_type key_type_derived = {
+	.name			= "derived",
+	.instantiate	= derived_instantiate,
+	.update			= derived_update,
+	.destroy		= derived_destroy,
+	.revoke			= derived_revoke,
+	.describe		= user_describe,
+	.read			= derived_read,
+};
+EXPORT_SYMBOL_GPL(key_type_derived);
+
+static int __init init_derived(void)
+{
+	return register_key_type(&key_type_derived);
+}
+
+static void __exit cleanup_derived(void)
+{
+	unregister_key_type(&key_type_derived);
+}
+
+late_initcall(init_derived);
+module_exit(cleanup_derived);
+
+MODULE_LICENSE("GPL");
-- 
1.9.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ