aboutsummaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig31
-rw-r--r--security/keys/Makefile2
-rw-r--r--security/keys/encrypted_defined.c903
-rw-r--r--security/keys/encrypted_defined.h54
-rw-r--r--security/keys/trusted_defined.c1175
-rw-r--r--security/keys/trusted_defined.h134
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/smack/smack.h45
-rw-r--r--security/smack/smack_access.c58
-rw-r--r--security/smack/smack_lsm.c354
-rw-r--r--security/smack/smackfs.c41
11 files changed, 2668 insertions, 131 deletions
diff --git a/security/Kconfig b/security/Kconfig
index e80da955e68..95accd442d5 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -21,6 +21,37 @@ config KEYS
If you are unsure as to whether this is required, answer N.
+config TRUSTED_KEYS
+ tristate "TRUSTED KEYS"
+ depends on KEYS && TCG_TPM
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_SHA1
+ help
+ This option provides support for creating, sealing, and unsealing
+ keys in the kernel. Trusted keys are random number symmetric keys,
+ generated and RSA-sealed by the TPM. The TPM only unseals the keys,
+ if the boot PCRs and other criteria match. Userspace will only ever
+ see encrypted blobs.
+
+ If you are unsure as to whether this is required, answer N.
+
+config ENCRYPTED_KEYS
+ tristate "ENCRYPTED KEYS"
+ depends on KEYS && TRUSTED_KEYS
+ select CRYPTO_AES
+ select CRYPTO_CBC
+ select CRYPTO_SHA256
+ select CRYPTO_RNG
+ help
+ This option provides support for create/encrypting/decrypting keys
+ in the kernel. Encrypted keys are kernel generated random numbers,
+ which are encrypted/decrypted with a 'master' symmetric key. The
+ 'master' key can be either a trusted-key or user-key type.
+ Userspace only ever sees/stores encrypted blobs.
+
+ If you are unsure as to whether this is required, answer N.
+
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 74d5447d7df..6c941050f57 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -13,6 +13,8 @@ obj-y := \
request_key_auth.o \
user_defined.o
+obj-$(CONFIG_TRUSTED_KEYS) += trusted_defined.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted_defined.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
new file mode 100644
index 00000000000..32d27c85838
--- /dev/null
+++ b/security/keys/encrypted_defined.c
@@ -0,0 +1,903 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.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, version 2 of the License.
+ *
+ * See Documentation/keys-trusted-encrypted.txt
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/parser.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <keys/user-type.h>
+#include <keys/trusted-type.h>
+#include <keys/encrypted-type.h>
+#include <linux/key-type.h>
+#include <linux/random.h>
+#include <linux/rcupdate.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <crypto/aes.h>
+
+#include "encrypted_defined.h"
+
+static const char KEY_TRUSTED_PREFIX[] = "trusted:";
+static const char KEY_USER_PREFIX[] = "user:";
+static const char hash_alg[] = "sha256";
+static const char hmac_alg[] = "hmac(sha256)";
+static const char blkcipher_alg[] = "cbc(aes)";
+static unsigned int ivsize;
+static int blksize;
+
+#define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
+#define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
+#define HASH_SIZE SHA256_DIGEST_SIZE
+#define MAX_DATA_SIZE 4096
+#define MIN_DATA_SIZE 20
+
+struct sdesc {
+ struct shash_desc shash;
+ char ctx[];
+};
+
+static struct crypto_shash *hashalg;
+static struct crypto_shash *hmacalg;
+
+enum {
+ Opt_err = -1, Opt_new, Opt_load, Opt_update
+};
+
+static const match_table_t key_tokens = {
+ {Opt_new, "new"},
+ {Opt_load, "load"},
+ {Opt_update, "update"},
+ {Opt_err, NULL}
+};
+
+static int aes_get_sizes(void)
+{
+ struct crypto_blkcipher *tfm;
+
+ tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ pr_err("encrypted_key: failed to alloc_cipher (%ld)\n",
+ PTR_ERR(tfm));
+ return PTR_ERR(tfm);
+ }
+ ivsize = crypto_blkcipher_ivsize(tfm);
+ blksize = crypto_blkcipher_blocksize(tfm);
+ crypto_free_blkcipher(tfm);
+ return 0;
+}
+
+/*
+ * valid_master_desc - verify the 'key-type:desc' of a new/updated master-key
+ *
+ * key-type:= "trusted:" | "encrypted:"
+ * desc:= master-key description
+ *
+ * Verify that 'key-type' is valid and that 'desc' exists. On key update,
+ * only the master key description is permitted to change, not the key-type.
+ * The key-type remains constant.
+ *
+ * On success returns 0, otherwise -EINVAL.
+ */
+static int valid_master_desc(const char *new_desc, const char *orig_desc)
+{
+ if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) {
+ if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN)
+ goto out;
+ if (orig_desc)
+ if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN))
+ goto out;
+ } else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) {
+ if (strlen(new_desc) == KEY_USER_PREFIX_LEN)
+ goto out;
+ if (orig_desc)
+ if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN))
+ goto out;
+ } else
+ goto out;
+ return 0;
+out:
+ return -EINVAL;
+}
+
+/*
+ * datablob_parse - parse the keyctl data
+ *
+ * datablob format:
+ * new <master-key name> <decrypted data length>
+ * load <master-key name> <decrypted data length> <encrypted iv + data>
+ * update <new-master-key name>
+ *
+ * Tokenizes a copy of the keyctl data, returning a pointer to each token,
+ * which is null terminated.
+ *
+ * On success returns 0, otherwise -EINVAL.
+ */
+static int datablob_parse(char *datablob, char **master_desc,
+ char **decrypted_datalen, char **hex_encoded_iv)
+{
+ substring_t args[MAX_OPT_ARGS];
+ int ret = -EINVAL;
+ int key_cmd;
+ char *p;
+
+ p = strsep(&datablob, " \t");
+ if (!p)
+ return ret;
+ key_cmd = match_token(p, key_tokens, args);
+
+ *master_desc = strsep(&datablob, " \t");
+ if (!*master_desc)
+ goto out;
+
+ if (valid_master_desc(*master_desc, NULL) < 0)
+ goto out;
+
+ if (decrypted_datalen) {
+ *decrypted_datalen = strsep(&datablob, " \t");
+ if (!*decrypted_datalen)
+ goto out;
+ }
+
+ switch (key_cmd) {
+ case Opt_new:
+ if (!decrypted_datalen)
+ break;
+ ret = 0;
+ break;
+ case Opt_load:
+ if (!decrypted_datalen)
+ break;
+ *hex_encoded_iv = strsep(&datablob, " \t");
+ if (!*hex_encoded_iv)
+ break;
+ ret = 0;
+ break;
+ case Opt_update:
+ if (decrypted_datalen)
+ break;
+ ret = 0;
+ break;
+ case Opt_err:
+ break;
+ }
+out:
+ return ret;
+}
+
+/*
+ * datablob_format - format as an ascii string, before copying to userspace
+ */
+static char *datablob_format(struct encrypted_key_payload *epayload,
+ size_t asciiblob_len)
+{
+ char *ascii_buf, *bufp;
+ u8 *iv = epayload->iv;
+ int len;
+ int i;
+
+ ascii_buf = kmalloc(asciiblob_len + 1, GFP_KERNEL);
+ if (!ascii_buf)
+ goto out;
+
+ ascii_buf[asciiblob_len] = '\0';
+
+ /* copy datablob master_desc and datalen strings */
+ len = sprintf(ascii_buf, "%s %s ", epayload->master_desc,
+ epayload->datalen);
+
+ /* convert the hex encoded iv, encrypted-data and HMAC to ascii */
+ bufp = &ascii_buf[len];
+ for (i = 0; i < (asciiblob_len - len) / 2; i++)
+ bufp = pack_hex_byte(bufp, iv[i]);
+out:
+ return ascii_buf;
+}
+
+/*
+ * request_trusted_key - request the trusted key
+ *
+ * Trusted keys are sealed to PCRs and other metadata. Although userspace
+ * manages both trusted/encrypted key-types, like the encrypted key type
+ * data, trusted key type data is not visible decrypted from userspace.
+ */
+static struct key *request_trusted_key(const char *trusted_desc,
+ u8 **master_key, size_t *master_keylen)
+{
+ struct trusted_key_payload *tpayload;
+ struct key *tkey;
+
+ tkey = request_key(&key_type_trusted, trusted_desc, NULL);
+ if (IS_ERR(tkey))
+ goto error;
+
+ down_read(&tkey->sem);
+ tpayload = rcu_dereference(tkey->payload.data);
+ *master_key = tpayload->key;
+ *master_keylen = tpayload->key_len;
+error:
+ return tkey;
+}
+
+/*
+ * request_user_key - request the user key
+ *
+ * Use a user provided key to encrypt/decrypt an encrypted-key.
+ */
+static struct key *request_user_key(const char *master_desc, u8 **master_key,
+ size_t *master_keylen)
+{
+ struct user_key_payload *upayload;
+ struct key *ukey;
+
+ ukey = request_key(&key_type_user, master_desc, NULL);
+ if (IS_ERR(ukey))
+ goto error;
+
+ down_read(&ukey->sem);
+ upayload = rcu_dereference(ukey->payload.data);
+ *master_key = upayload->data;
+ *master_keylen = upayload->datalen;
+error:
+ return ukey;
+}
+
+static struct sdesc *alloc_sdesc(struct crypto_shash *alg)
+{
+ struct sdesc *sdesc;
+ int size;
+
+ size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
+ sdesc = kmalloc(size, GFP_KERNEL);
+ if (!sdesc)
+ return ERR_PTR(-ENOMEM);
+ sdesc->shash.tfm = alg;
+ sdesc->shash.flags = 0x0;
+ return sdesc;
+}
+
+static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen,
+ const u8 *buf, unsigned int buflen)
+{
+ struct sdesc *sdesc;
+ int ret;
+
+ sdesc = alloc_sdesc(hmacalg);
+ if (IS_ERR(sdesc)) {
+ pr_info("encrypted_key: can't alloc %s\n", hmac_alg);
+ return PTR_ERR(sdesc);
+ }
+
+ ret = crypto_shash_setkey(hmacalg, key, keylen);
+ if (!ret)
+ ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
+ kfree(sdesc);
+ return ret;
+}
+
+static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen)
+{
+ struct sdesc *sdesc;
+ int ret;
+
+ sdesc = alloc_sdesc(hashalg);
+ if (IS_ERR(sdesc)) {
+ pr_info("encrypted_key: can't alloc %s\n", hash_alg);
+ return PTR_ERR(sdesc);
+ }
+
+ ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
+ kfree(sdesc);
+ return ret;
+}
+
+enum derived_key_type { ENC_KEY, AUTH_KEY };
+
+/* Derive authentication/encryption key from trusted key */
+static int get_derived_key(u8 *derived_key, enum derived_key_type key_type,
+ const u8 *master_key, size_t master_keylen)
+{
+ u8 *derived_buf;
+ unsigned int derived_buf_len;
+ int ret;
+
+ derived_buf_len = strlen("AUTH_KEY") + 1 + master_keylen;
+ if (derived_buf_len < HASH_SIZE)
+ derived_buf_len = HASH_SIZE;
+
+ derived_buf = kzalloc(derived_buf_len, GFP_KERNEL);
+ if (!derived_buf) {
+ pr_err("encrypted_key: out of memory\n");
+ return -ENOMEM;
+ }
+ if (key_type)
+ strcpy(derived_buf, "AUTH_KEY");
+ else
+ strcpy(derived_buf, "ENC_KEY");
+
+ memcpy(derived_buf + strlen(derived_buf) + 1, master_key,
+ master_keylen);
+ ret = calc_hash(derived_key, derived_buf, derived_buf_len);
+ kfree(derived_buf);
+ return ret;
+}
+
+static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key,
+ unsigned int key_len, const u8 *iv,
+ unsigned int ivsize)
+{
+ int ret;
+
+ desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc->tfm)) {
+ pr_err("encrypted_key: failed to load %s transform (%ld)\n",
+ blkcipher_alg, PTR_ERR(desc->tfm));
+ return PTR_ERR(desc->tfm);
+ }
+ desc->flags = 0;
+
+ ret = crypto_blkcipher_setkey(desc->tfm, key, key_len);
+ if (ret < 0) {
+ pr_err("encrypted_key: failed to setkey (%d)\n", ret);
+ crypto_free_blkcipher(desc->tfm);
+ return ret;
+ }
+ crypto_blkcipher_set_iv(desc->tfm, iv, ivsize);
+ return 0;
+}
+
+static struct key *request_master_key(struct encrypted_key_payload *epayload,
+ u8 **master_key, size_t *master_keylen)
+{
+ struct key *mkey = NULL;
+
+ if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX,
+ KEY_TRUSTED_PREFIX_LEN)) {
+ mkey = request_trusted_key(epayload->master_desc +
+ KEY_TRUSTED_PREFIX_LEN,
+ master_key, master_keylen);
+ } else if (!strncmp(epayload->master_desc, KEY_USER_PREFIX,
+ KEY_USER_PREFIX_LEN)) {
+ mkey = request_user_key(epayload->master_desc +
+ KEY_USER_PREFIX_LEN,
+ master_key, master_keylen);
+ } else
+ goto out;
+
+ if (IS_ERR(mkey))
+ pr_info("encrypted_key: key %s not found",
+ epayload->master_desc);
+ if (mkey)
+ dump_master_key(*master_key, *master_keylen);
+out:
+ return mkey;
+}
+
+/* Before returning data to userspace, encrypt decrypted data. */
+static int derived_key_encrypt(struct encrypted_key_payload *epayload,
+ const u8 *derived_key,
+ unsigned int derived_keylen)
+{
+ struct scatterlist sg_in[2];
+ struct scatterlist sg_out[1];
+ struct blkcipher_desc desc;
+ unsigned int encrypted_datalen;
+ unsigned int padlen;
+ char pad[16];
+ int ret;
+
+ encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
+ padlen = encrypted_datalen - epayload->decrypted_datalen;
+
+ ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
+ epayload->iv, ivsize);
+ if (ret < 0)
+ goto out;
+ dump_decrypted_data(epayload);
+
+ memset(pad, 0, sizeof pad);
+ sg_init_table(sg_in, 2);
+ sg_set_buf(&sg_in[0], epayload->decrypted_data,
+ epayload->decrypted_datalen);
+ sg_set_buf(&sg_in[1], pad, padlen);
+
+ sg_init_table(sg_out, 1);
+ sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen);
+
+ ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen);
+ crypto_free_blkcipher(desc.tfm);
+ if (ret < 0)
+ pr_err("encrypted_key: failed to encrypt (%d)\n", ret);
+ else
+ dump_encrypted_data(epayload, encrypted_datalen);
+out:
+ return ret;
+}
+
+static int datablob_hmac_append(struct encrypted_key_payload *epayload,
+ const u8 *master_key, size_t master_keylen)
+{
+ u8 derived_key[HASH_SIZE];
+ u8 *digest;
+ int ret;
+
+ ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
+ if (ret < 0)
+ goto out;
+
+ digest = epayload->master_desc + epayload->datablob_len;
+ ret = calc_hmac(digest, derived_key, sizeof derived_key,
+ epayload->master_desc, epayload->datablob_len);
+ if (!ret)
+ dump_hmac(NULL, digest, HASH_SIZE);
+out:
+ return ret;
+}
+
+/* verify HMAC before decrypting encrypted key */
+static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
+ const u8 *master_key, size_t master_keylen)
+{
+ u8 derived_key[HASH_SIZE];
+ u8 digest[HASH_SIZE];
+ int ret;
+
+ ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
+ if (ret < 0)
+ goto out;
+
+ ret = calc_hmac(digest, derived_key, sizeof derived_key,
+ epayload->master_desc, epayload->datablob_len);
+ if (ret < 0)
+ goto out;
+ ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
+ sizeof digest);
+ if (ret) {
+ ret = -EINVAL;
+ dump_hmac("datablob",
+ epayload->master_desc + epayload->datablob_len,
+ HASH_SIZE);
+ dump_hmac("calc", digest, HASH_SIZE);
+ }
+out:
+ return ret;
+}
+
+static int derived_key_decrypt(struct encrypted_key_payload *epayload,
+ const u8 *derived_key,
+ unsigned int derived_keylen)
+{
+ struct scatterlist sg_in[1];
+ struct scatterlist sg_out[2];
+ struct blkcipher_desc desc;
+ unsigned int encrypted_datalen;
+ char pad[16];
+ int ret;
+
+ encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
+ ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
+ epayload->iv, ivsize);
+ if (ret < 0)
+ goto out;
+ dump_encrypted_data(epayload, encrypted_datalen);
+
+ memset(pad, 0, sizeof pad);
+ sg_init_table(sg_in, 1);
+ sg_init_table(sg_out, 2);
+ sg_set_buf(sg_in, epayload->encrypted_data, encrypted_datalen);
+ sg_set_buf(&sg_out[0], epayload->decrypted_data,
+ epayload->decrypted_datalen);
+ sg_set_buf(&sg_out[1], pad, sizeof pad);
+
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen);
+ crypto_free_blkcipher(desc.tfm);
+ if (ret < 0)
+ goto out;
+ dump_decrypted_data(epayload);
+out:
+ return ret;
+}
+
+/* Allocate memory for decrypted key and datablob. */
+static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
+ const char *master_desc,
+ const char *datalen)
+{
+ struct encrypted_key_payload *epayload = NULL;
+ unsigned short datablob_len;
+ unsigned short decrypted_datalen;
+ unsigned int encrypted_datalen;
+ long dlen;
+ int ret;
+
+ ret = strict_strtol(datalen, 10, &dlen);
+ if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
+ return ERR_PTR(-EINVAL);
+
+ decrypted_datalen = dlen;
+ encrypted_datalen = roundup(decrypted_datalen, blksize);
+
+ datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
+ + ivsize + 1 + encrypted_datalen;
+
+ ret = key_payload_reserve(key, decrypted_datalen + datablob_len
+ + HASH_SIZE + 1);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
+ datablob_len + HASH_SIZE + 1, GFP_KERNEL);
+ if (!epayload)
+ return ERR_PTR(-ENOMEM);
+
+ epayload->decrypted_datalen = decrypted_datalen;
+ epayload->datablob_len = datablob_len;
+ return epayload;
+}
+
+static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
+ const char *hex_encoded_iv)
+{
+ struct key *mkey;
+ u8 derived_key[HASH_SIZE];
+ u8 *master_key;
+ u8 *hmac;
+ const char *hex_encoded_data;
+ unsigned int encrypted_datalen;
+ size_t master_keylen;
+ size_t asciilen;
+ int ret;
+
+ encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
+ asciilen = (ivsize + 1 + encrypted_datalen + HASH_SIZE) * 2;
+ if (strlen(hex_encoded_iv) != asciilen)
+ return -EINVAL;
+
+ hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2;
+ hex2bin(epayload->iv, hex_encoded_iv, ivsize);
+ hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
+
+ hmac = epayload->master_desc + epayload->datablob_len;
+ hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
+
+ mkey = request_master_key(epayload, &master_key, &master_keylen);
+ if (IS_ERR(mkey))
+ return PTR_ERR(mkey);
+
+ ret = datablob_hmac_verify(epayload, master_key, master_keylen);
+ if (ret < 0) {
+ pr_err("encrypted_key: bad hmac (%d)\n", ret);
+ goto out;
+ }
+
+ ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
+ if (ret < 0)
+ goto out;
+
+ ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
+ if (ret < 0)
+ pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
+out:
+ up_read(&mkey->sem);
+ key_put(mkey);
+ return ret;
+}
+
+static void __ekey_init(struct encrypted_key_payload *epayload,
+ const char *master_desc, const char *datalen)
+{
+ epayload->master_desc = epayload->decrypted_data
+ + epayload->decrypted_datalen;
+ epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
+ epayload->iv = epayload->datalen + strlen(datalen) + 1;
+ epayload->encrypted_data = epayload->iv + ivsize + 1;
+
+ memcpy(epayload->master_desc, master_desc, strlen(master_desc));
+ memcpy(epayload->datalen, datalen, strlen(datalen));
+}
+
+/*
+ * encrypted_init - initialize an encrypted key
+ *
+ * For a new key, use a random number for both the iv and data
+ * itself. For an old key, decrypt the hex encoded data.
+ */
+static int encrypted_init(struct encrypted_key_payload *epayload,
+ const char *master_desc, const char *datalen,
+ const char *hex_encoded_iv)
+{
+ int ret = 0;
+
+ __ekey_init(epayload, master_desc, datalen);
+ if (!hex_encoded_iv) {
+ get_random_bytes(epayload->iv, ivsize);
+
+ get_random_bytes(epayload->decrypted_data,
+ epayload->decrypted_datalen);
+ } else
+ ret = encrypted_key_decrypt(epayload, hex_encoded_iv);
+ return ret;
+}
+
+/*
+ * encrypted_instantiate - instantiate an encrypted key
+ *
+ * Decrypt an existing encrypted datablob or create a new encrypted key
+ * based on a kernel random number.
+ *
+ * On success, return 0. Otherwise return errno.
+ */
+static int encrypted_instantiate(struct key *key, const void *data,
+ size_t datalen)
+{
+ struct encrypted_key_payload *epayload = NULL;
+ char *datablob = NULL;
+ char *master_desc = NULL;
+ char *decrypted_datalen = NULL;
+ char *hex_encoded_iv = NULL;
+ int ret;
+
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ datablob = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!datablob)
+ return -ENOMEM;
+ datablob[datalen] = 0;
+ memcpy(datablob, data, datalen);
+ ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
+ &hex_encoded_iv);
+ if (ret < 0)
+ goto out;
+
+ epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
+ if (IS_ERR(epayload)) {
+ ret = PTR_ERR(epayload);
+ goto out;
+ }
+ ret = encrypted_init(epayload, master_desc, decrypted_datalen,
+ hex_encoded_iv);
+ if (ret < 0) {
+ kfree(epayload);
+ goto out;
+ }
+
+ rcu_assign_pointer(key->payload.data, epayload);
+out:
+ kfree(datablob);
+ return ret;
+}
+
+static void encrypted_rcu_free(struct rcu_head *rcu)
+{
+ struct encrypted_key_payload *epayload;
+
+ epayload = container_of(rcu, struct encrypted_key_payload, rcu);
+ memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
+ kfree(epayload);
+}
+
+/*
+ * encrypted_update - update the master key description
+ *
+ * Change the master key description for an existing encrypted key.
+ * The next read will return an encrypted datablob using the new
+ * master key description.
+ *
+ * On success, return 0. Otherwise return errno.
+ */
+static int encrypted_update(struct key *key, const void *data, size_t datalen)
+{
+ struct encrypted_key_payload *epayload = key->payload.data;
+ struct encrypted_key_payload *new_epayload;
+ char *buf;
+ char *new_master_desc = NULL;
+ int ret = 0;
+
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ buf = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[datalen] = 0;
+ memcpy(buf, data, datalen);
+ ret = datablob_parse(buf, &new_master_desc, NULL, NULL);
+ if (ret < 0)
+ goto out;
+
+ ret = valid_master_desc(new_master_desc, epayload->master_desc);
+ if (ret < 0)
+ goto out;
+
+ new_epayload = encrypted_key_alloc(key, new_master_desc,
+ epayload->datalen);
+ if (IS_ERR(new_epayload)) {
+ ret = PTR_ERR(new_epayload);
+ goto out;
+ }
+
+ __ekey_init(new_epayload, new_master_desc, epayload->datalen);
+
+ memcpy(new_epayload->iv, epayload->iv, ivsize);
+ memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
+ epayload->decrypted_datalen);
+
+ rcu_assign_pointer(key->payload.data, new_epayload);
+ call_rcu(&epayload->rcu, encrypted_rcu_free);
+out:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * encrypted_read - format and copy the encrypted data to userspace
+ *
+ * The resulting datablob format is:
+ * <master-key name> <decrypted data length> <encrypted iv> <encrypted data>
+ *
+ * On success, return to userspace the encrypted key datablob size.
+ */
+static long encrypted_read(const struct key *key, char __user *buffer,
+ size_t buflen)
+{
+ struct encrypted_key_payload *epayload;
+ struct key *mkey;
+ u8 *master_key;
+ size_t master_keylen;
+ char derived_key[HASH_SIZE];
+ char *ascii_buf;
+ size_t asciiblob_len;
+ int ret;
+
+ epayload = rcu_dereference_protected(key->payload.data,
+ rwsem_is_locked(&((struct key *)key)->sem));
+
+ /* returns the hex encoded iv, encrypted-data, and hmac as ascii */
+ asciiblob_len = epayload->datablob_len + ivsize + 1
+ + roundup(epayload->decrypted_datalen, blksize)
+ + (HASH_SIZE * 2);
+
+ if (!buffer || buflen < asciiblob_len)
+ return asciiblob_len;
+
+ mkey = request_master_key(epayload, &master_key, &master_keylen);
+ if (IS_ERR(mkey))
+ return PTR_ERR(mkey);
+
+ ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
+ if (ret < 0)
+ goto out;
+
+ ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
+ if (ret < 0)
+ goto out;
+
+ ret = datablob_hmac_append(epayload, master_key, master_keylen);
+ if (ret < 0)
+ goto out;
+
+ ascii_buf = datablob_format(epayload, asciiblob_len);
+ if (!ascii_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ up_read(&mkey->sem);
+ key_put(mkey);
+
+ if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
+ ret = -EFAULT;
+ kfree(ascii_buf);
+
+ return asciiblob_len;
+out:
+ up_read(&mkey->sem);
+ key_put(mkey);
+ return ret;
+}
+
+/*
+ * encrypted_destroy - before freeing the key, clear the decrypted data
+ *
+ * Before freeing the key, clear the memory containing the decrypted
+ * key data.
+ */
+static void encrypted_destroy(struct key *key)
+{
+ struct encrypted_key_payload *epayload = key->payload.data;
+
+ if (!epayload)
+ return;
+
+ memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
+ kfree(key->payload.data);
+}
+
+struct key_type key_type_encrypted = {
+ .name = "encrypted",
+ .instantiate = encrypted_instantiate,
+ .update = encrypted_update,
+ .match = user_match,
+ .destroy = encrypted_destroy,
+ .describe = user_describe,
+ .read = encrypted_read,
+};
+EXPORT_SYMBOL_GPL(key_type_encrypted);
+
+static void encrypted_shash_release(void)
+{
+ if (hashalg)
+ crypto_free_shash(hashalg);
+ if (hmacalg)
+ crypto_free_shash(hmacalg);
+}
+
+static int __init encrypted_shash_alloc(void)
+{
+ int ret;
+
+ hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(hmacalg)) {
+ pr_info("encrypted_key: could not allocate crypto %s\n",
+ hmac_alg);
+ return PTR_ERR(hmacalg);
+ }
+
+ hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(hashalg)) {
+ pr_info("encrypted_key: could not allocate crypto %s\n",
+ hash_alg);
+ ret = PTR_ERR(hashalg);
+ goto hashalg_fail;
+ }
+
+ return 0;
+
+hashalg_fail:
+ crypto_free_shash(hmacalg);
+ return ret;
+}
+
+static int __init init_encrypted(void)
+{
+ int ret;
+
+ ret = encrypted_shash_alloc();
+ if (ret < 0)
+ return ret;
+ ret = register_key_type(&key_type_encrypted);
+ if (ret < 0)
+ goto out;
+ return aes_get_sizes();
+out:
+ encrypted_shash_release();
+ return ret;
+
+}
+
+static void __exit cleanup_encrypted(void)
+{
+ encrypted_shash_release();
+ unregister_key_type(&key_type_encrypted);
+}
+
+late_initcall(init_encrypted);
+module_exit(cleanup_encrypted);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/encrypted_defined.h b/security/keys/encrypted_defined.h
new file mode 100644
index 00000000000..cef5e2f2b7d
--- /dev/null
+++ b/security/keys/encrypted_defined.h
@@ -0,0 +1,54 @@
+#ifndef __ENCRYPTED_KEY_H
+#define __ENCRYPTED_KEY_H
+
+#define ENCRYPTED_DEBUG 0
+
+#if ENCRYPTED_DEBUG
+static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
+{
+ print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
+ master_key, master_keylen, 0);
+}
+
+static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
+{
+ print_hex_dump(KERN_ERR, "decrypted data: ", DUMP_PREFIX_NONE, 32, 1,
+ epayload->decrypted_data,
+ epayload->decrypted_datalen, 0);
+}
+
+static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
+ unsigned int encrypted_datalen)
+{
+ print_hex_dump(KERN_ERR, "encrypted data: ", DUMP_PREFIX_NONE, 32, 1,
+ epayload->encrypted_data, encrypted_datalen, 0);
+}
+
+static inline void dump_hmac(const char *str, const u8 *digest,
+ unsigned int hmac_size)
+{
+ if (str)
+ pr_info("encrypted_key: %s", str);
+ print_hex_dump(KERN_ERR, "hmac: ", DUMP_PREFIX_NONE, 32, 1, digest,
+ hmac_size, 0);
+}
+#else
+static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
+{
+}
+
+static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
+{
+}
+
+static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
+ unsigned int encrypted_datalen)
+{
+}
+
+static inline void dump_hmac(const char *str, const u8 *digest,
+ unsigned int hmac_size)
+{
+}
+#endif
+#endif
diff --git a/security/keys/trusted_defined.c b/security/keys/trusted_defined.c
new file mode 100644
index 00000000000..975e9f29a52
--- /dev/null
+++ b/security/keys/trusted_defined.c
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Author:
+ * David Safford <safford@us.ibm.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, version 2 of the License.
+ *
+ * See Documentation/keys-trusted-encrypted.txt
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/parser.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <keys/user-type.h>
+#include <keys/trusted-type.h>
+#include <linux/key-type.h>
+#include <linux/rcupdate.h>
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <linux/capability.h>
+#include <linux/tpm.h>
+#include <linux/tpm_command.h>
+
+#include "trusted_defined.h"
+
+static const char hmac_alg[] = "hmac(sha1)";
+static const char hash_alg[] = "sha1";
+
+struct sdesc {
+ struct shash_desc shash;
+ char ctx[];
+};
+
+static struct crypto_shash *hashalg;
+static struct crypto_shash *hmacalg;
+
+static struct sdesc *init_sdesc(struct crypto_shash *alg)
+{
+ struct sdesc *sdesc;
+ int size;
+
+ size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
+ sdesc = kmalloc(size, GFP_KERNEL);
+ if (!sdesc)
+ return ERR_PTR(-ENOMEM);
+ sdesc->shash.tfm = alg;
+ sdesc->shash.flags = 0x0;
+ return sdesc;
+}
+
+static int TSS_sha1(const unsigned char *data, unsigned int datalen,
+ unsigned char *digest)
+{
+ struct sdesc *sdesc;
+ int ret;
+
+ sdesc = init_sdesc(hashalg);
+ if (IS_ERR(sdesc)) {
+ pr_info("trusted_key: can't alloc %s\n", hash_alg);
+ return PTR_ERR(sdesc);
+ }
+
+ ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
+ kfree(sdesc);
+ return ret;
+}
+
+static int TSS_rawhmac(unsigned char *digest, const unsigned char *key,
+ unsigned int keylen, ...)
+{
+ struct sdesc *sdesc;
+ va_list argp;
+ unsigned int dlen;
+ unsigned char *data;
+ int ret;
+
+ sdesc = init_sdesc(hmacalg);
+ if (IS_ERR(sdesc)) {
+ pr_info("trusted_key: can't alloc %s\n", hmac_alg);
+ return PTR_ERR(sdesc);
+ }
+
+ ret = crypto_shash_setkey(hmacalg, key, keylen);
+ if (ret < 0)
+ goto out;
+ ret = crypto_shash_init(&sdesc->shash);
+ if (ret < 0)
+ goto out;
+
+ va_start(argp, keylen);
+ for (;;) {
+ dlen = va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ data = va_arg(argp, unsigned char *);
+ if (data == NULL)
+ return -EINVAL;
+ ret = crypto_shash_update(&sdesc->shash, data, dlen);
+ if (ret < 0)
+ goto out;
+ }
+ va_end(argp);
+ if (!ret)
+ ret = crypto_shash_final(&sdesc->shash, digest);
+out:
+ kfree(sdesc);
+ return ret;
+}
+
+/*
+ * calculate authorization info fields to send to TPM
+ */
+static int TSS_authhmac(unsigned char *digest, const unsigned char *key,
+ unsigned int keylen, unsigned char *h1,
+ unsigned char *h2, unsigned char h3, ...)
+{
+ unsigned char paramdigest[SHA1_DIGEST_SIZE];
+ struct sdesc *sdesc;
+ unsigned int dlen;
+ unsigned char *data;
+ unsigned char c;
+ int ret;
+ va_list argp;
+
+ sdesc = init_sdesc(hashalg);
+ if (IS_ERR(sdesc)) {
+ pr_info("trusted_key: can't alloc %s\n", hash_alg);
+ return PTR_ERR(sdesc);
+ }
+
+ c = h3;
+ ret = crypto_shash_init(&sdesc->shash);
+ if (ret < 0)
+ goto out;
+ va_start(argp, h3);
+ for (;;) {
+ dlen = va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ data = va_arg(argp, unsigned char *);
+ ret = crypto_shash_update(&sdesc->shash, data, dlen);
+ if (ret < 0) {
+ va_end(argp);
+ goto out;
+ }
+ }
+ va_end(argp);
+ ret = crypto_shash_final(&sdesc->shash, paramdigest);
+ if (!ret)
+ ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE,
+ paramdigest, TPM_NONCE_SIZE, h1,
+ TPM_NONCE_SIZE, h2, 1, &c, 0, 0);
+out:
+ kfree(sdesc);
+ return ret;
+}
+
+/*
+ * verify the AUTH1_COMMAND (Seal) result from TPM
+ */
+static int TSS_checkhmac1(unsigned char *buffer,
+ const uint32_t command,
+ const unsigned char *ononce,
+ const unsigned char *key,
+ unsigned int keylen, ...)
+{
+ uint32_t bufsize;
+ uint16_t tag;
+ uint32_t ordinal;
+ uint32_t result;
+ unsigned char *enonce;
+ unsigned char *continueflag;
+ unsigned char *authdata;
+ unsigned char testhmac[SHA1_DIGEST_SIZE];
+ unsigned char paramdigest[SHA1_DIGEST_SIZE];
+ struct sdesc *sdesc;
+ unsigned int dlen;
+ unsigned int dpos;
+ va_list argp;
+ int ret;
+
+ bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
+ tag = LOAD16(buffer, 0);
+ ordinal = command;
+ result = LOAD32N(buffer, TPM_RETURN_OFFSET);
+ if (tag == TPM_TAG_RSP_COMMAND)
+ return 0;
+ if (tag != TPM_TAG_RSP_AUTH1_COMMAND)
+ return -EINVAL;
+ authdata = buffer + bufsize - SHA1_DIGEST_SIZE;
+ continueflag = authdata - 1;
+ enonce = continueflag - TPM_NONCE_SIZE;
+
+ sdesc = init_sdesc(hashalg);
+ if (IS_ERR(sdesc)) {
+ pr_info("trusted_key: can't alloc %s\n", hash_alg);
+ return PTR_ERR(sdesc);
+ }
+ ret = crypto_shash_init(&sdesc->shash);
+ if (ret < 0)
+ goto out;
+ ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,
+ sizeof result);
+ if (ret < 0)
+ goto out;
+ ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,
+ sizeof ordinal);
+ if (ret < 0)
+ goto out;
+ va_start(argp, keylen);
+ for (;;) {
+ dlen = va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ dpos = va_arg(argp, unsigned int);
+ ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);
+ if (ret < 0) {
+ va_end(argp);
+ goto out;
+ }
+ }
+ va_end(argp);
+ ret = crypto_shash_final(&sdesc->shash, paramdigest);
+ if (ret < 0)
+ goto out;
+
+ ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest,
+ TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce,
+ 1, continueflag, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE))
+ ret = -EINVAL;
+out:
+ kfree(sdesc);
+ return ret;
+}
+
+/*
+ * verify the AUTH2_COMMAND (unseal) result from TPM
+ */
+static int TSS_checkhmac2(unsigned char *buffer,
+ const uint32_t command,
+ const unsigned char *ononce,
+ const unsigned char *key1,
+ unsigned int keylen1,
+ const unsigned char *key2,
+ unsigned int keylen2, ...)
+{
+ uint32_t bufsize;
+ uint16_t tag;
+ uint32_t ordinal;
+ uint32_t result;
+ unsigned char *enonce1;
+ unsigned char *continueflag1;
+ unsigned char *authdata1;
+ unsigned char *enonce2;
+ unsigned char *continueflag2;
+ unsigned char *authdata2;
+ unsigned char testhmac1[SHA1_DIGEST_SIZE];
+ unsigned char testhmac2[SHA1_DIGEST_SIZE];
+ unsigned char paramdigest[SHA1_DIGEST_SIZE];
+ struct sdesc *sdesc;
+ unsigned int dlen;
+ unsigned int dpos;
+ va_list argp;
+ int ret;
+
+ bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
+ tag = LOAD16(buffer, 0);
+ ordinal = command;
+ result = LOAD32N(buffer, TPM_RETURN_OFFSET);
+
+ if (tag == TPM_TAG_RSP_COMMAND)
+ return 0;
+ if (tag != TPM_TAG_RSP_AUTH2_COMMAND)
+ return -EINVAL;
+ authdata1 = buffer + bufsize - (SHA1_DIGEST_SIZE + 1
+ + SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE);
+ authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE);
+ continueflag1 = authdata1 - 1;
+ continueflag2 = authdata2 - 1;
+ enonce1 = continueflag1 - TPM_NONCE_SIZE;
+ enonce2 = continueflag2 - TPM_NONCE_SIZE;
+
+ sdesc = init_sdesc(hashalg);
+ if (IS_ERR(sdesc)) {
+ pr_info("trusted_key: can't alloc %s\n", hash_alg);
+ return PTR_ERR(sdesc);
+ }
+ ret = crypto_shash_init(&sdesc->shash);
+ if (ret < 0)
+ goto out;
+ ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,
+ sizeof result);
+ if (ret < 0)
+ goto out;
+ ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,
+ sizeof ordinal);
+ if (ret < 0)
+ goto out;
+
+ va_start(argp, keylen2);
+ for (;;) {
+ dlen = va_arg(argp, unsigned int);
+ if (dlen == 0)
+ break;
+ dpos = va_arg(argp, unsigned int);
+ ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);
+ if (ret < 0) {
+ va_end(argp);
+ goto out;
+ }
+ }
+ va_end(argp);
+ ret = crypto_shash_final(&sdesc->shash, paramdigest);
+ if (ret < 0)
+ goto out;
+
+ ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE,
+ paramdigest, TPM_NONCE_SIZE, enonce1,
+ TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0);
+ if (ret < 0)
+ goto out;
+ if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE,
+ paramdigest, TPM_NONCE_SIZE, enonce2,
+ TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0);
+ if (ret < 0)
+ goto out;
+ if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE))
+ ret = -EINVAL;
+out:
+ kfree(sdesc);
+ return ret;
+}
+
+/*
+ * For key specific tpm requests, we will generate and send our
+ * own TPM command packets using the drivers send function.
+ */
+static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
+ size_t buflen)
+{
+ int rc;
+
+ dump_tpm_buf(cmd);
+ rc = tpm_send(chip_num, cmd, buflen);
+ dump_tpm_buf(cmd);
+ if (rc > 0)
+ /* Can't return positive return codes values to keyctl */
+ rc = -EPERM;
+ return rc;
+}
+
+/*
+ * get a random value from TPM
+ */
+static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
+{
+ int ret;
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_GETRANDOM_SIZE);
+ store32(tb, TPM_ORD_GETRANDOM);
+ store32(tb, len);
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
+ if (!ret)
+ memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);
+ return ret;
+}
+
+static int my_get_random(unsigned char *buf, int len)
+{
+ struct tpm_buf *tb;
+ int ret;
+
+ tb = kmalloc(sizeof *tb, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+ ret = tpm_get_random(tb, buf, len);
+
+ kfree(tb);
+ return ret;
+}
+
+/*
+ * Lock a trusted key, by extending a selected PCR.
+ *
+ * Prevents a trusted key that is sealed to PCRs from being accessed.
+ * This uses the tpm driver's extend function.
+ */
+static int pcrlock(const int pcrnum)
+{
+ unsigned char hash[SHA1_DIGEST_SIZE];
+ int ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ ret = my_get_random(hash, SHA1_DIGEST_SIZE);
+ if (ret < 0)
+ return ret;
+ return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
+}
+
+/*
+ * Create an object specific authorisation protocol (OSAP) session
+ */
+static int osap(struct tpm_buf *tb, struct osapsess *s,
+ const unsigned char *key, uint16_t type, uint32_t handle)
+{
+ unsigned char enonce[TPM_NONCE_SIZE];
+ unsigned char ononce[TPM_NONCE_SIZE];
+ int ret;
+
+ ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
+ if (ret < 0)
+ return ret;
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_OSAP_SIZE);
+ store32(tb, TPM_ORD_OSAP);
+ store16(tb, type);
+ store32(tb, handle);
+ storebytes(tb, ononce, TPM_NONCE_SIZE);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ s->handle = LOAD32(tb->data, TPM_DATA_OFFSET);
+ memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]),
+ TPM_NONCE_SIZE);
+ memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) +
+ TPM_NONCE_SIZE]), TPM_NONCE_SIZE);
+ return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE,
+ enonce, TPM_NONCE_SIZE, ononce, 0, 0);
+}
+
+/*
+ * Create an object independent authorisation protocol (oiap) session
+ */
+static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
+{
+ int ret;
+
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_OIAP_SIZE);
+ store32(tb, TPM_ORD_OIAP);
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ *handle = LOAD32(tb->data, TPM_DATA_OFFSET);
+ memcpy(nonce, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)],
+ TPM_NONCE_SIZE);
+ return 0;
+}
+
+struct tpm_digests {
+ unsigned char encauth[SHA1_DIGEST_SIZE];
+ unsigned char pubauth[SHA1_DIGEST_SIZE];
+ unsigned char xorwork[SHA1_DIGEST_SIZE * 2];
+ unsigned char xorhash[SHA1_DIGEST_SIZE];
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+};
+
+/*
+ * Have the TPM seal(encrypt) the trusted key, possibly based on
+ * Platform Configuration Registers (PCRs). AUTH1 for sealing key.
+ */
+static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
+ uint32_t keyhandle, const unsigned char *keyauth,
+ const unsigned char *data, uint32_t datalen,
+ unsigned char *blob, uint32_t *bloblen,
+ const unsigned char *blobauth,
+ const unsigned char *pcrinfo, uint32_t pcrinfosize)
+{
+ struct osapsess sess;
+ struct tpm_digests *td;
+ unsigned char cont;
+ uint32_t ordinal;
+ uint32_t pcrsize;
+ uint32_t datsize;
+ int sealinfosize;
+ int encdatasize;
+ int storedsize;
+ int ret;
+ int i;
+
+ /* alloc some work space for all the hashes */
+ td = kmalloc(sizeof *td, GFP_KERNEL);
+ if (!td)
+ return -ENOMEM;
+
+ /* get session for sealing key */
+ ret = osap(tb, &sess, keyauth, keytype, keyhandle);
+ if (ret < 0)
+ return ret;
+ dump_sess(&sess);
+
+ /* calculate encrypted authorization value */
+ memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE);
+ memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE);
+ ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash);
+ if (ret < 0)
+ return ret;
+
+ ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0)
+ return ret;
+ ordinal = htonl(TPM_ORD_SEAL);
+ datsize = htonl(datalen);
+ pcrsize = htonl(pcrinfosize);
+ cont = 0;
+
+ /* encrypt data authorization key */
+ for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
+ td->encauth[i] = td->xorhash[i] ^ blobauth[i];
+
+ /* calculate authorization HMAC value */
+ if (pcrinfosize == 0) {
+ /* no pcr info specified */
+ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE,
+ sess.enonce, td->nonceodd, cont,
+ sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE,
+ td->encauth, sizeof(uint32_t), &pcrsize,
+ sizeof(uint32_t), &datsize, datalen, data, 0,
+ 0);
+ } else {
+ /* pcr info specified */
+ ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE,
+ sess.enonce, td->nonceodd, cont,
+ sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE,
+ td->encauth, sizeof(uint32_t), &pcrsize,
+ pcrinfosize, pcrinfo, sizeof(uint32_t),
+ &datsize, datalen, data, 0, 0);
+ }
+ if (ret < 0)
+ return ret;
+
+ /* build and send the TPM request packet */
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
+ store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen);
+ store32(tb, TPM_ORD_SEAL);
+ store32(tb, keyhandle);
+ storebytes(tb, td->encauth, SHA1_DIGEST_SIZE);
+ store32(tb, pcrinfosize);
+ storebytes(tb, pcrinfo, pcrinfosize);
+ store32(tb, datalen);
+ storebytes(tb, data, datalen);
+ store32(tb, sess.handle);
+ storebytes(tb, td->nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ /* calculate the size of the returned Blob */
+ sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t));
+ encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) +
+ sizeof(uint32_t) + sealinfosize);
+ storedsize = sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize +
+ sizeof(uint32_t) + encdatasize;
+
+ /* check the HMAC in the response */
+ ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, sess.secret,
+ SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0,
+ 0);
+
+ /* copy the returned blob to caller */
+ if (!ret) {
+ memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize);
+ *bloblen = storedsize;
+ }
+ return ret;
+}
+
+/*
+ * use the AUTH2_COMMAND form of unseal, to authorize both key and blob
+ */
+static int tpm_unseal(struct tpm_buf *tb,
+ uint32_t keyhandle, const unsigned char *keyauth,
+ const unsigned char *blob, int bloblen,
+ const unsigned char *blobauth,
+ unsigned char *data, unsigned int *datalen)
+{
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+ unsigned char enonce1[TPM_NONCE_SIZE];
+ unsigned char enonce2[TPM_NONCE_SIZE];
+ unsigned char authdata1[SHA1_DIGEST_SIZE];
+ unsigned char authdata2[SHA1_DIGEST_SIZE];
+ uint32_t authhandle1 = 0;
+ uint32_t authhandle2 = 0;
+ unsigned char cont = 0;
+ uint32_t ordinal;
+ uint32_t keyhndl;
+ int ret;
+
+ /* sessions for unsealing key and data */
+ ret = oiap(tb, &authhandle1, enonce1);
+ if (ret < 0) {
+ pr_info("trusted_key: oiap failed (%d)\n", ret);
+ return ret;
+ }
+ ret = oiap(tb, &authhandle2, enonce2);
+ if (ret < 0) {
+ pr_info("trusted_key: oiap failed (%d)\n", ret);
+ return ret;
+ }
+
+ ordinal = htonl(TPM_ORD_UNSEAL);
+ keyhndl = htonl(SRKHANDLE);
+ ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0) {
+ pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
+ return ret;
+ }
+ ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE,
+ enonce1, nonceodd, cont, sizeof(uint32_t),
+ &ordinal, bloblen, blob, 0, 0);
+ if (ret < 0)
+ return ret;
+ ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE,
+ enonce2, nonceodd, cont, sizeof(uint32_t),
+ &ordinal, bloblen, blob, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* build and send TPM request packet */
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH2_COMMAND);
+ store32(tb, TPM_UNSEAL_SIZE + bloblen);
+ store32(tb, TPM_ORD_UNSEAL);
+ store32(tb, keyhandle);
+ storebytes(tb, blob, bloblen);
+ store32(tb, authhandle1);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata1, SHA1_DIGEST_SIZE);
+ store32(tb, authhandle2);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata2, SHA1_DIGEST_SIZE);
+
+ ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ if (ret < 0) {
+ pr_info("trusted_key: authhmac failed (%d)\n", ret);
+ return ret;
+ }
+
+ *datalen = LOAD32(tb->data, TPM_DATA_OFFSET);
+ ret = TSS_checkhmac2(tb->data, ordinal, nonceodd,
+ keyauth, SHA1_DIGEST_SIZE,
+ blobauth, SHA1_DIGEST_SIZE,
+ sizeof(uint32_t), TPM_DATA_OFFSET,
+ *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0,
+ 0);
+ if (ret < 0) {
+ pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret);
+ return ret;
+ }
+ memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen);
+ return 0;
+}
+
+/*
+ * Have the TPM seal(encrypt) the symmetric key
+ */
+static int key_seal(struct trusted_key_payload *p,
+ struct trusted_key_options *o)
+{
+ struct tpm_buf *tb;
+ int ret;
+
+ tb = kzalloc(sizeof *tb, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ /* include migratable flag at end of sealed key */
+ p->key[p->key_len] = p->migratable;
+
+ ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
+ p->key, p->key_len + 1, p->blob, &p->blob_len,
+ o->blobauth, o->pcrinfo, o->pcrinfo_len);
+ if (ret < 0)
+ pr_info("trusted_key: srkseal failed (%d)\n", ret);
+
+ kfree(tb);
+ return ret;
+}
+
+/*
+ * Have the TPM unseal(decrypt) the symmetric key
+ */
+static int key_unseal(struct trusted_key_payload *p,
+ struct trusted_key_options *o)
+{
+ struct tpm_buf *tb;
+ int ret;
+
+ tb = kzalloc(sizeof *tb, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
+ o->blobauth, p->key, &p->key_len);
+ if (ret < 0)
+ pr_info("trusted_key: srkunseal failed (%d)\n", ret);
+ else
+ /* pull migratable flag out of sealed key */
+ p->migratable = p->key[--p->key_len];
+
+ kfree(tb);
+ return ret;
+}
+
+enum {
+ Opt_err = -1,
+ Opt_new, Opt_load, Opt_update,
+ Opt_keyhandle, Opt_keyauth, Opt_blobauth,
+ Opt_pcrinfo, Opt_pcrlock, Opt_migratable
+};
+
+static const match_table_t key_tokens = {
+ {Opt_new, "new"},
+ {Opt_load, "load"},
+ {Opt_update, "update"},
+ {Opt_keyhandle, "keyhandle=%s"},
+ {Opt_keyauth, "keyauth=%s"},
+ {Opt_blobauth, "blobauth=%s"},
+ {Opt_pcrinfo, "pcrinfo=%s"},
+ {Opt_pcrlock, "pcrlock=%s"},
+ {Opt_migratable, "migratable=%s"},
+ {Opt_err, NULL}
+};
+
+/* can have zero or more token= options */
+static int getoptions(char *c, struct trusted_key_payload *pay,
+ struct trusted_key_options *opt)
+{
+ substring_t args[MAX_OPT_ARGS];
+ char *p = c;
+ int token;
+ int res;
+ unsigned long handle;
+ unsigned long lock;
+
+ while ((p = strsep(&c, " \t"))) {
+ if (*p == '\0' || *p == ' ' || *p == '\t')
+ continue;
+ token = match_token(p, key_tokens, args);
+
+ switch (token) {
+ case Opt_pcrinfo:
+ opt->pcrinfo_len = strlen(args[0].from) / 2;
+ if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
+ return -EINVAL;
+ hex2bin(opt->pcrinfo, args[0].from, opt->pcrinfo_len);
+ break;
+ case Opt_keyhandle:
+ res = strict_strtoul(args[0].from, 16, &handle);
+ if (res < 0)
+ return -EINVAL;
+ opt->keytype = SEAL_keytype;
+ opt->keyhandle = handle;
+ break;
+ case Opt_keyauth:
+ if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
+ return -EINVAL;
+ hex2bin(opt->keyauth, args[0].from, SHA1_DIGEST_SIZE);
+ break;
+ case Opt_blobauth:
+ if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
+ return -EINVAL;
+ hex2bin(opt->blobauth, args[0].from, SHA1_DIGEST_SIZE);
+ break;
+ case Opt_migratable:
+ if (*args[0].from == '0')
+ pay->migratable = 0;
+ else
+ return -EINVAL;
+ break;
+ case Opt_pcrlock:
+ res = strict_strtoul(args[0].from, 10, &lock);
+ if (res < 0)
+ return -EINVAL;
+ opt->pcrlock = lock;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * datablob_parse - parse the keyctl data and fill in the
+ * payload and options structures
+ *
+ * On success returns 0, otherwise -EINVAL.
+ */
+static int datablob_parse(char *datablob, struct trusted_key_payload *p,
+ struct trusted_key_options *o)
+{
+ substring_t args[MAX_OPT_ARGS];
+ long keylen;
+ int ret = -EINVAL;
+ int key_cmd;
+ char *c;
+
+ /* main command */
+ c = strsep(&datablob, " \t");
+ if (!c)
+ return -EINVAL;
+ key_cmd = match_token(c, key_tokens, args);
+ switch (key_cmd) {
+ case Opt_new:
+ /* first argument is key size */
+ c = strsep(&datablob, " \t");
+ if (!c)
+ return -EINVAL;
+ ret = strict_strtol(c, 10, &keylen);
+ if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)
+ return -EINVAL;
+ p->key_len = keylen;
+ ret = getoptions(datablob, p, o);
+ if (ret < 0)
+ return ret;
+ ret = Opt_new;
+ break;
+ case Opt_load:
+ /* first argument is sealed blob */
+ c = strsep(&datablob, " \t");
+ if (!c)
+ return -EINVAL;
+ p->blob_len = strlen(c) / 2;
+ if (p->blob_len > MAX_BLOB_SIZE)
+ return -EINVAL;
+ hex2bin(p->blob, c, p->blob_len);
+ ret = getoptions(datablob, p, o);
+ if (ret < 0)
+ return ret;
+ ret = Opt_load;
+ break;
+ case Opt_update:
+ /* all arguments are options */
+ ret = getoptions(datablob, p, o);
+ if (ret < 0)
+ return ret;
+ ret = Opt_update;
+ break;
+ case Opt_err:
+ return -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static struct trusted_key_options *trusted_options_alloc(void)
+{
+ struct trusted_key_options *options;
+
+ options = kzalloc(sizeof *options, GFP_KERNEL);
+ if (options) {
+ /* set any non-zero defaults */
+ options->keytype = SRK_keytype;
+ options->keyhandle = SRKHANDLE;
+ }
+ return options;
+}
+
+static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
+{
+ struct trusted_key_payload *p = NULL;
+ int ret;
+
+ ret = key_payload_reserve(key, sizeof *p);
+ if (ret < 0)
+ return p;
+ p = kzalloc(sizeof *p, GFP_KERNEL);
+ if (p)
+ p->migratable = 1; /* migratable by default */
+ return p;
+}
+
+/*
+ * trusted_instantiate - create a new trusted key
+ *
+ * Unseal an existing trusted blob or, for a new key, get a
+ * random key, then seal and create a trusted key-type key,
+ * adding it to the specified keyring.
+ *
+ * On success, return 0. Otherwise return errno.
+ */
+static int trusted_instantiate(struct key *key, const void *data,
+ size_t datalen)
+{
+ struct trusted_key_payload *payload = NULL;
+ struct trusted_key_options *options = NULL;
+ char *datablob;
+ int ret = 0;
+ int key_cmd;
+
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ datablob = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!datablob)
+ return -ENOMEM;
+ memcpy(datablob, data, datalen);
+ datablob[datalen] = '\0';
+
+ options = trusted_options_alloc();
+ if (!options) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ payload = trusted_payload_alloc(key);
+ if (!payload) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ key_cmd = datablob_parse(datablob, payload, options);
+ if (key_cmd < 0) {
+ ret = key_cmd;
+ goto out;
+ }
+
+ dump_payload(payload);
+ dump_options(options);
+
+ switch (key_cmd) {
+ case Opt_load:
+ ret = key_unseal(payload, options);
+ dump_payload(payload);
+ dump_options(options);
+ if (ret < 0)
+ pr_info("trusted_key: key_unseal failed (%d)\n", ret);
+ break;
+ case Opt_new:
+ ret = my_get_random(payload->key, payload->key_len);
+ if (ret < 0) {
+ pr_info("trusted_key: key_create failed (%d)\n", ret);
+ goto out;
+ }
+ ret = key_seal(payload, options);
+ if (ret < 0)
+ pr_info("trusted_key: key_seal failed (%d)\n", ret);
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!ret && options->pcrlock)
+ ret = pcrlock(options->pcrlock);
+out:
+ kfree(datablob);
+ kfree(options);
+ if (!ret)
+ rcu_assign_pointer(key->payload.data, payload);
+ else
+ kfree(payload);
+ return ret;
+}
+
+static void trusted_rcu_free(struct rcu_head *rcu)
+{
+ struct trusted_key_payload *p;
+
+ p = container_of(rcu, struct trusted_key_payload, rcu);
+ memset(p->key, 0, p->key_len);
+ kfree(p);
+}
+
+/*
+ * trusted_update - reseal an existing key with new PCR values
+ */
+static int trusted_update(struct key *key, const void *data, size_t datalen)
+{
+ struct trusted_key_payload *p = key->payload.data;
+ struct trusted_key_payload *new_p;
+ struct trusted_key_options *new_o;
+ char *datablob;
+ int ret = 0;
+
+ if (!p->migratable)
+ return -EPERM;
+ if (datalen <= 0 || datalen > 32767 || !data)
+ return -EINVAL;
+
+ datablob = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!datablob)
+ return -ENOMEM;
+ new_o = trusted_options_alloc();
+ if (!new_o) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ new_p = trusted_payload_alloc(key);
+ if (!new_p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(datablob, data, datalen);
+ datablob[datalen] = '\0';
+ ret = datablob_parse(datablob, new_p, new_o);
+ if (ret != Opt_update) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /* copy old key values, and reseal with new pcrs */
+ new_p->migratable = p->migratable;
+ new_p->key_len = p->key_len;
+ memcpy(new_p->key, p->key, p->key_len);
+ dump_payload(p);
+ dump_payload(new_p);
+
+ ret = key_seal(new_p, new_o);
+ if (ret < 0) {
+ pr_info("trusted_key: key_seal failed (%d)\n", ret);
+ kfree(new_p);
+ goto out;
+ }
+ if (new_o->pcrlock) {
+ ret = pcrlock(new_o->pcrlock);
+ if (ret < 0) {
+ pr_info("trusted_key: pcrlock failed (%d)\n", ret);
+ kfree(new_p);
+ goto out;
+ }
+ }
+ rcu_assign_pointer(key->payload.data, new_p);
+ call_rcu(&p->rcu, trusted_rcu_free);
+out:
+ kfree(datablob);
+ kfree(new_o);
+ return ret;
+}
+
+/*
+ * trusted_read - copy the sealed blob data to userspace in hex.
+ * On success, return to userspace the trusted key datablob size.
+ */
+static long trusted_read(const struct key *key, char __user *buffer,
+ size_t buflen)
+{
+ struct trusted_key_payload *p;
+ char *ascii_buf;
+ char *bufp;
+ int i;
+
+ p = rcu_dereference_protected(key->payload.data,
+ rwsem_is_locked(&((struct key *)key)->sem));
+ if (!p)
+ return -EINVAL;
+ if (!buffer || buflen <= 0)
+ return 2 * p->blob_len;
+ ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL);
+ if (!ascii_buf)
+ return -ENOMEM;
+
+ bufp = ascii_buf;
+ for (i = 0; i < p->blob_len; i++)
+ bufp = pack_hex_byte(bufp, p->blob[i]);
+ if ((copy_to_user(buffer, ascii_buf, 2 * p->blob_len)) != 0) {
+ kfree(ascii_buf);
+ return -EFAULT;
+ }
+ kfree(ascii_buf);
+ return 2 * p->blob_len;
+}
+
+/*
+ * trusted_destroy - before freeing the key, clear the decrypted data
+ */
+static void trusted_destroy(struct key *key)
+{
+ struct trusted_key_payload *p = key->payload.data;
+
+ if (!p)
+ return;
+ memset(p->key, 0, p->key_len);
+ kfree(key->payload.data);
+}
+
+struct key_type key_type_trusted = {
+ .name = "trusted",
+ .instantiate = trusted_instantiate,
+ .update = trusted_update,
+ .match = user_match,
+ .destroy = trusted_destroy,
+ .describe = user_describe,
+ .read = trusted_read,
+};
+
+EXPORT_SYMBOL_GPL(key_type_trusted);
+
+static void trusted_shash_release(void)
+{
+ if (hashalg)
+ crypto_free_shash(hashalg);
+ if (hmacalg)
+ crypto_free_shash(hmacalg);
+}
+
+static int __init trusted_shash_alloc(void)
+{
+ int ret;
+
+ hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(hmacalg)) {
+ pr_info("trusted_key: could not allocate crypto %s\n",
+ hmac_alg);
+ return PTR_ERR(hmacalg);
+ }
+
+ hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(hashalg)) {
+ pr_info("trusted_key: could not allocate crypto %s\n",
+ hash_alg);
+ ret = PTR_ERR(hashalg);
+ goto hashalg_fail;
+ }
+
+ return 0;
+
+hashalg_fail:
+ crypto_free_shash(hmacalg);
+ return ret;
+}
+
+static int __init init_trusted(void)
+{
+ int ret;
+
+ ret = trusted_shash_alloc();
+ if (ret < 0)
+ return ret;
+ ret = register_key_type(&key_type_trusted);
+ if (ret < 0)
+ trusted_shash_release();
+ return ret;
+}
+
+static void __exit cleanup_trusted(void)
+{
+ trusted_shash_release();
+ unregister_key_type(&key_type_trusted);
+}
+
+late_initcall(init_trusted);
+module_exit(cleanup_trusted);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/trusted_defined.h b/security/keys/trusted_defined.h
new file mode 100644
index 00000000000..3249fbd2b65
--- /dev/null
+++ b/security/keys/trusted_defined.h
@@ -0,0 +1,134 @@
+#ifndef __TRUSTED_KEY_H
+#define __TRUSTED_KEY_H
+
+/* implementation specific TPM constants */
+#define MAX_PCRINFO_SIZE 64
+#define MAX_BUF_SIZE 512
+#define TPM_GETRANDOM_SIZE 14
+#define TPM_OSAP_SIZE 36
+#define TPM_OIAP_SIZE 10
+#define TPM_SEAL_SIZE 87
+#define TPM_UNSEAL_SIZE 104
+#define TPM_SIZE_OFFSET 2
+#define TPM_RETURN_OFFSET 6
+#define TPM_DATA_OFFSET 10
+
+#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset]))
+#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
+#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
+
+struct tpm_buf {
+ int len;
+ unsigned char data[MAX_BUF_SIZE];
+};
+
+#define INIT_BUF(tb) (tb->len = 0)
+
+struct osapsess {
+ uint32_t handle;
+ unsigned char secret[SHA1_DIGEST_SIZE];
+ unsigned char enonce[TPM_NONCE_SIZE];
+};
+
+/* discrete values, but have to store in uint16_t for TPM use */
+enum {
+ SEAL_keytype = 1,
+ SRK_keytype = 4
+};
+
+struct trusted_key_options {
+ uint16_t keytype;
+ uint32_t keyhandle;
+ unsigned char keyauth[SHA1_DIGEST_SIZE];
+ unsigned char blobauth[SHA1_DIGEST_SIZE];
+ uint32_t pcrinfo_len;
+ unsigned char pcrinfo[MAX_PCRINFO_SIZE];
+ int pcrlock;
+};
+
+#define TPM_DEBUG 0
+
+#if TPM_DEBUG
+static inline void dump_options(struct trusted_key_options *o)
+{
+ pr_info("trusted_key: sealing key type %d\n", o->keytype);
+ pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle);
+ pr_info("trusted_key: pcrlock %d\n", o->pcrlock);
+ pr_info("trusted_key: pcrinfo %d\n", o->pcrinfo_len);
+ print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
+ 16, 1, o->pcrinfo, o->pcrinfo_len, 0);
+}
+
+static inline void dump_payload(struct trusted_key_payload *p)
+{
+ pr_info("trusted_key: key_len %d\n", p->key_len);
+ print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
+ 16, 1, p->key, p->key_len, 0);
+ pr_info("trusted_key: bloblen %d\n", p->blob_len);
+ print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
+ 16, 1, p->blob, p->blob_len, 0);
+ pr_info("trusted_key: migratable %d\n", p->migratable);
+}
+
+static inline void dump_sess(struct osapsess *s)
+{
+ print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE,
+ 16, 1, &s->handle, 4, 0);
+ pr_info("trusted-key: secret:\n");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
+ pr_info("trusted-key: enonce:\n");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
+}
+
+static inline void dump_tpm_buf(unsigned char *buf)
+{
+ int len;
+
+ pr_info("\ntrusted-key: tpm buffer\n");
+ len = LOAD32(buf, TPM_SIZE_OFFSET);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
+}
+#else
+static inline void dump_options(struct trusted_key_options *o)
+{
+}
+
+static inline void dump_payload(struct trusted_key_payload *p)
+{
+}
+
+static inline void dump_sess(struct osapsess *s)
+{
+}
+
+static inline void dump_tpm_buf(unsigned char *buf)
+{
+}
+#endif
+
+static inline void store8(struct tpm_buf *buf, const unsigned char value)
+{
+ buf->data[buf->len++] = value;
+}
+
+static inline void store16(struct tpm_buf *buf, const uint16_t value)
+{
+ *(uint16_t *) & buf->data[buf->len] = htons(value);
+ buf->len += sizeof value;
+}
+
+static inline void store32(struct tpm_buf *buf, const uint32_t value)
+{
+ *(uint32_t *) & buf->data[buf->len] = htonl(value);
+ buf->len += sizeof value;
+}
+
+static inline void storebytes(struct tpm_buf *buf, const unsigned char *in,
+ const int len)
+{
+ memcpy(buf->data + buf->len, in, len);
+ buf->len += len;
+}
+#endif
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 8858d2b2d4b..7ed3663332e 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -142,7 +142,7 @@ struct security_class_mapping secclass_map[] = {
"node_bind", "name_connect", NULL } },
{ "memprotect", { "mmap_zero", NULL } },
{ "peer", { "recv", NULL } },
- { "capability2", { "mac_override", "mac_admin", NULL } },
+ { "capability2", { "mac_override", "mac_admin", "syslog", NULL } },
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
{ "tun_socket",
{ COMMON_SOCK_PERMS, NULL } },
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 43ae747a5aa..129c4eb8ffb 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -51,11 +51,18 @@ struct socket_smack {
*/
struct inode_smack {
char *smk_inode; /* label of the fso */
+ char *smk_task; /* label of the task */
struct mutex smk_lock; /* initialization lock */
int smk_flags; /* smack inode flags */
};
+struct task_smack {
+ char *smk_task; /* label used for access control */
+ char *smk_forked; /* label when forked */
+};
+
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
+#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
/*
* A label access rule.
@@ -161,6 +168,10 @@ struct smack_known {
#define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */
/*
+ * Flag for transmute access
+ */
+#define MAY_TRANSMUTE 64
+/*
* Just to make the common cases easier to deal with
*/
#define MAY_ANY (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
@@ -191,6 +202,7 @@ struct inode_smack *new_inode_smack(char *);
/*
* These functions are in smack_access.c
*/
+int smk_access_entry(char *, char *);
int smk_access(char *, char *, int, struct smk_audit_info *);
int smk_curacc(char *, u32, struct smk_audit_info *);
int smack_to_cipso(const char *, struct smack_cipso *);
@@ -234,6 +246,15 @@ static inline void smack_catset_bit(int cat, char *catsetp)
}
/*
+ * Is the directory transmuting?
+ */
+static inline int smk_inode_transmutable(const struct inode *isp)
+{
+ struct inode_smack *sip = isp->i_security;
+ return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
+}
+
+/*
* Present a pointer to the smack label in an inode blob.
*/
static inline char *smk_of_inode(const struct inode *isp)
@@ -243,6 +264,30 @@ static inline char *smk_of_inode(const struct inode *isp)
}
/*
+ * Present a pointer to the smack label in an task blob.
+ */
+static inline char *smk_of_task(const struct task_smack *tsp)
+{
+ return tsp->smk_task;
+}
+
+/*
+ * Present a pointer to the forked smack label in an task blob.
+ */
+static inline char *smk_of_forked(const struct task_smack *tsp)
+{
+ return tsp->smk_forked;
+}
+
+/*
+ * Present a pointer to the smack label in the current task blob.
+ */
+static inline char *smk_of_current(void)
+{
+ return smk_of_task(current_security());
+}
+
+/*
* logging functions
*/
#define SMACK_AUDIT_DENIED 0x1
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index f4fac64c4da..7ba8478f599 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -67,6 +67,46 @@ static u32 smack_next_secid = 10;
int log_policy = SMACK_AUDIT_DENIED;
/**
+ * smk_access_entry - look up matching access rule
+ * @subject_label: a pointer to the subject's Smack label
+ * @object_label: a pointer to the object's Smack label
+ *
+ * This function looks up the subject/object pair in the
+ * access rule list and returns pointer to the matching rule if found,
+ * NULL otherwise.
+ *
+ * NOTE:
+ * Even though Smack labels are usually shared on smack_list
+ * labels that come in off the network can't be imported
+ * and added to the list for locking reasons.
+ *
+ * Therefore, it is necessary to check the contents of the labels,
+ * not just the pointer values. Of course, in most cases the labels
+ * will be on the list, so checking the pointers may be a worthwhile
+ * optimization.
+ */
+int smk_access_entry(char *subject_label, char *object_label)
+{
+ u32 may = MAY_NOT;
+ struct smack_rule *srp;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(srp, &smack_rule_list, list) {
+ if (srp->smk_subject == subject_label ||
+ strcmp(srp->smk_subject, subject_label) == 0) {
+ if (srp->smk_object == object_label ||
+ strcmp(srp->smk_object, object_label) == 0) {
+ may = srp->smk_access;
+ break;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ return may;
+}
+
+/**
* smk_access - determine if a subject has a specific access to an object
* @subject_label: a pointer to the subject's Smack label
* @object_label: a pointer to the object's Smack label
@@ -90,7 +130,6 @@ int smk_access(char *subject_label, char *object_label, int request,
struct smk_audit_info *a)
{
u32 may = MAY_NOT;
- struct smack_rule *srp;
int rc = 0;
/*
@@ -144,18 +183,7 @@ int smk_access(char *subject_label, char *object_label, int request,
* access (e.g. read is included in readwrite) it's
* good.
*/
- rcu_read_lock();
- list_for_each_entry_rcu(srp, &smack_rule_list, list) {
- if (srp->smk_subject == subject_label ||
- strcmp(srp->smk_subject, subject_label) == 0) {
- if (srp->smk_object == object_label ||
- strcmp(srp->smk_object, object_label) == 0) {
- may = srp->smk_access;
- break;
- }
- }
- }
- rcu_read_unlock();
+ may = smk_access_entry(subject_label, object_label);
/*
* This is a bit map operation.
*/
@@ -185,7 +213,7 @@ out_audit:
int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
{
int rc;
- char *sp = current_security();
+ char *sp = smk_of_current();
rc = smk_access(sp, obj_label, mode, NULL);
if (rc == 0)
@@ -196,7 +224,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
* only one that gets privilege and current does not
* have that label.
*/
- if (smack_onlycap != NULL && smack_onlycap != current->cred->security)
+ if (smack_onlycap != NULL && smack_onlycap != sp)
goto out_audit;
if (capable(CAP_MAC_OVERRIDE))
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ccb71a044a1..533bf3255d7 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3,12 +3,14 @@
*
* This file contains the smack hook function implementations.
*
- * Author:
+ * Authors:
* Casey Schaufler <casey@schaufler-ca.com>
+ * Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
*
* Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
* Paul Moore <paul.moore@hp.com>
+ * Copyright (C) 2010 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -35,6 +37,9 @@
#define task_security(task) (task_cred_xxx((task), security))
+#define TRANS_TRUE "TRUE"
+#define TRANS_TRUE_SIZE 4
+
/**
* smk_fetch - Fetch the smack label from a file.
* @ip: a pointer to the inode
@@ -43,7 +48,7 @@
* Returns a pointer to the master list entry for the Smack label
* or NULL if there was no label to fetch.
*/
-static char *smk_fetch(struct inode *ip, struct dentry *dp)
+static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp)
{
int rc;
char in[SMK_LABELLEN];
@@ -51,7 +56,7 @@ static char *smk_fetch(struct inode *ip, struct dentry *dp)
if (ip->i_op->getxattr == NULL)
return NULL;
- rc = ip->i_op->getxattr(dp, XATTR_NAME_SMACK, in, SMK_LABELLEN);
+ rc = ip->i_op->getxattr(dp, name, in, SMK_LABELLEN);
if (rc < 0)
return NULL;
@@ -103,8 +108,8 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
if (rc != 0)
return rc;
- sp = current_security();
- tsp = task_security(ctp);
+ sp = smk_of_current();
+ tsp = smk_of_task(task_security(ctp));
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, ctp);
@@ -138,8 +143,8 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, ptp);
- sp = current_security();
- tsp = task_security(ptp);
+ sp = smk_of_current();
+ tsp = smk_of_task(task_security(ptp));
/* we won't log here, because rc can be overriden */
rc = smk_access(tsp, sp, MAY_READWRITE, NULL);
if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
@@ -160,7 +165,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
static int smack_syslog(int typefrom_file)
{
int rc = 0;
- char *sp = current_security();
+ char *sp = smk_of_current();
if (capable(CAP_MAC_OVERRIDE))
return 0;
@@ -391,6 +396,40 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
}
/*
+ * BPRM hooks
+ */
+
+static int smack_bprm_set_creds(struct linux_binprm *bprm)
+{
+ struct task_smack *tsp = bprm->cred->security;
+ struct inode_smack *isp;
+ struct dentry *dp;
+ int rc;
+
+ rc = cap_bprm_set_creds(bprm);
+ if (rc != 0)
+ return rc;
+
+ if (bprm->cred_prepared)
+ return 0;
+
+ if (bprm->file == NULL || bprm->file->f_dentry == NULL)
+ return 0;
+
+ dp = bprm->file->f_dentry;
+
+ if (dp->d_inode == NULL)
+ return 0;
+
+ isp = dp->d_inode->i_security;
+
+ if (isp->smk_task != NULL)
+ tsp->smk_task = isp->smk_task;
+
+ return 0;
+}
+
+/*
* Inode hooks
*/
@@ -402,7 +441,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
*/
static int smack_inode_alloc_security(struct inode *inode)
{
- inode->i_security = new_inode_smack(current_security());
+ inode->i_security = new_inode_smack(smk_of_current());
if (inode->i_security == NULL)
return -ENOMEM;
return 0;
@@ -434,6 +473,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len)
{
char *isp = smk_of_inode(inode);
+ char *dsp = smk_of_inode(dir);
+ u32 may;
if (name) {
*name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
@@ -442,6 +483,16 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
}
if (value) {
+ may = smk_access_entry(smk_of_current(), dsp);
+
+ /*
+ * If the access rule allows transmutation and
+ * the directory requests transmutation then
+ * by all means transmute.
+ */
+ if (((may & MAY_TRANSMUTE) != 0) && smk_inode_transmutable(dir))
+ isp = dsp;
+
*value = kstrdup(isp, GFP_KERNEL);
if (*value == NULL)
return -ENOMEM;
@@ -664,7 +715,8 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
+ strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
if (!capable(CAP_MAC_ADMIN))
rc = -EPERM;
/*
@@ -674,6 +726,12 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
if (size == 0 || size >= SMK_LABELLEN ||
smk_import(value, size) == NULL)
rc = -EINVAL;
+ } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
+ if (!capable(CAP_MAC_ADMIN))
+ rc = -EPERM;
+ if (size != TRANS_TRUE_SIZE ||
+ strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
+ rc = -EINVAL;
} else
rc = cap_inode_setxattr(dentry, name, value, size, flags);
@@ -700,26 +758,23 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- struct inode_smack *isp;
char *nsp;
+ struct inode_smack *isp = dentry->d_inode->i_security;
- /*
- * Not SMACK
- */
- if (strcmp(name, XATTR_NAME_SMACK))
- return;
-
- isp = dentry->d_inode->i_security;
-
- /*
- * No locking is done here. This is a pointer
- * assignment.
- */
- nsp = smk_import(value, size);
- if (nsp != NULL)
- isp->smk_inode = nsp;
- else
- isp->smk_inode = smack_known_invalid.smk_known;
+ if (strcmp(name, XATTR_NAME_SMACK) == 0) {
+ nsp = smk_import(value, size);
+ if (nsp != NULL)
+ isp->smk_inode = nsp;
+ else
+ isp->smk_inode = smack_known_invalid.smk_known;
+ } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
+ nsp = smk_import(value, size);
+ if (nsp != NULL)
+ isp->smk_task = nsp;
+ else
+ isp->smk_task = smack_known_invalid.smk_known;
+ } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
+ isp->smk_flags |= SMK_INODE_TRANSMUTE;
return;
}
@@ -752,12 +807,15 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
*/
static int smack_inode_removexattr(struct dentry *dentry, const char *name)
{
+ struct inode_smack *isp;
struct smk_audit_info ad;
int rc = 0;
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
+ strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
if (!capable(CAP_MAC_ADMIN))
rc = -EPERM;
} else
@@ -768,6 +826,11 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
if (rc == 0)
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
+ if (rc == 0) {
+ isp = dentry->d_inode->i_security;
+ isp->smk_task = NULL;
+ }
+
return rc;
}
@@ -895,7 +958,7 @@ static int smack_file_permission(struct file *file, int mask)
*/
static int smack_file_alloc_security(struct file *file)
{
- file->f_security = current_security();
+ file->f_security = smk_of_current();
return 0;
}
@@ -1005,7 +1068,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
*/
static int smack_file_set_fowner(struct file *file)
{
- file->f_security = current_security();
+ file->f_security = smk_of_current();
return 0;
}
@@ -1025,7 +1088,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
{
struct file *file;
int rc;
- char *tsp = tsk->cred->security;
+ char *tsp = smk_of_task(tsk->cred->security);
struct smk_audit_info ad;
/*
@@ -1082,7 +1145,9 @@ static int smack_file_receive(struct file *file)
*/
static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- cred->security = NULL;
+ cred->security = kzalloc(sizeof(struct task_smack), gfp);
+ if (cred->security == NULL)
+ return -ENOMEM;
return 0;
}
@@ -1097,7 +1162,7 @@ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
*/
static void smack_cred_free(struct cred *cred)
{
- cred->security = NULL;
+ kfree(cred->security);
}
/**
@@ -1111,7 +1176,16 @@ static void smack_cred_free(struct cred *cred)
static int smack_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- new->security = old->security;
+ struct task_smack *old_tsp = old->security;
+ struct task_smack *new_tsp;
+
+ new_tsp = kzalloc(sizeof(struct task_smack), gfp);
+ if (new_tsp == NULL)
+ return -ENOMEM;
+
+ new_tsp->smk_task = old_tsp->smk_task;
+ new_tsp->smk_forked = old_tsp->smk_task;
+ new->security = new_tsp;
return 0;
}
@@ -1124,7 +1198,11 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
*/
static void smack_cred_transfer(struct cred *new, const struct cred *old)
{
- new->security = old->security;
+ struct task_smack *old_tsp = old->security;
+ struct task_smack *new_tsp = new->security;
+
+ new_tsp->smk_task = old_tsp->smk_task;
+ new_tsp->smk_forked = old_tsp->smk_task;
}
/**
@@ -1136,12 +1214,13 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
*/
static int smack_kernel_act_as(struct cred *new, u32 secid)
{
+ struct task_smack *new_tsp = new->security;
char *smack = smack_from_secid(secid);
if (smack == NULL)
return -EINVAL;
- new->security = smack;
+ new_tsp->smk_task = smack;
return 0;
}
@@ -1157,8 +1236,10 @@ static int smack_kernel_create_files_as(struct cred *new,
struct inode *inode)
{
struct inode_smack *isp = inode->i_security;
+ struct task_smack *tsp = new->security;
- new->security = isp->smk_inode;
+ tsp->smk_forked = isp->smk_inode;
+ tsp->smk_task = isp->smk_inode;
return 0;
}
@@ -1175,7 +1256,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access)
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, p);
- return smk_curacc(task_security(p), access, &ad);
+ return smk_curacc(smk_of_task(task_security(p)), access, &ad);
}
/**
@@ -1221,7 +1302,7 @@ static int smack_task_getsid(struct task_struct *p)
*/
static void smack_task_getsecid(struct task_struct *p, u32 *secid)
{
- *secid = smack_to_secid(task_security(p));
+ *secid = smack_to_secid(smk_of_task(task_security(p)));
}
/**
@@ -1333,14 +1414,15 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
* can write the receiver.
*/
if (secid == 0)
- return smk_curacc(task_security(p), MAY_WRITE, &ad);
+ return smk_curacc(smk_of_task(task_security(p)), MAY_WRITE,
+ &ad);
/*
* If the secid isn't 0 we're dealing with some USB IO
* specific behavior. This is not clean. For one thing
* we can't take privilege into account.
*/
- return smk_access(smack_from_secid(secid), task_security(p),
- MAY_WRITE, &ad);
+ return smk_access(smack_from_secid(secid),
+ smk_of_task(task_security(p)), MAY_WRITE, &ad);
}
/**
@@ -1352,12 +1434,12 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
static int smack_task_wait(struct task_struct *p)
{
struct smk_audit_info ad;
- char *sp = current_security();
- char *tsp = task_security(p);
+ char *sp = smk_of_current();
+ char *tsp = smk_of_forked(task_security(p));
int rc;
/* we don't log here, we can be overriden */
- rc = smk_access(sp, tsp, MAY_WRITE, NULL);
+ rc = smk_access(tsp, sp, MAY_WRITE, NULL);
if (rc == 0)
goto out_log;
@@ -1378,7 +1460,7 @@ static int smack_task_wait(struct task_struct *p)
out_log:
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, p);
- smack_log(sp, tsp, MAY_WRITE, rc, &ad);
+ smack_log(tsp, sp, MAY_WRITE, rc, &ad);
return rc;
}
@@ -1392,7 +1474,7 @@ static int smack_task_wait(struct task_struct *p)
static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
{
struct inode_smack *isp = inode->i_security;
- isp->smk_inode = task_security(p);
+ isp->smk_inode = smk_of_task(task_security(p));
}
/*
@@ -1411,7 +1493,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
*/
static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
{
- char *csp = current_security();
+ char *csp = smk_of_current();
struct socket_smack *ssp;
ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
@@ -1667,10 +1749,13 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
ssp->smk_in = sp;
else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
ssp->smk_out = sp;
- rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
- if (rc != 0)
- printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
- __func__, -rc);
+ if (sock->sk->sk_family != PF_UNIX) {
+ rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
+ if (rc != 0)
+ printk(KERN_WARNING
+ "Smack: \"%s\" netlbl error %d.\n",
+ __func__, -rc);
+ }
} else
return -EOPNOTSUPP;
@@ -1749,7 +1834,7 @@ static int smack_flags_to_may(int flags)
*/
static int smack_msg_msg_alloc_security(struct msg_msg *msg)
{
- msg->security = current_security();
+ msg->security = smk_of_current();
return 0;
}
@@ -1785,7 +1870,7 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp)
{
struct kern_ipc_perm *isp = &shp->shm_perm;
- isp->security = current_security();
+ isp->security = smk_of_current();
return 0;
}
@@ -1908,7 +1993,7 @@ static int smack_sem_alloc_security(struct sem_array *sma)
{
struct kern_ipc_perm *isp = &sma->sem_perm;
- isp->security = current_security();
+ isp->security = smk_of_current();
return 0;
}
@@ -2026,7 +2111,7 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq)
{
struct kern_ipc_perm *kisp = &msq->q_perm;
- kisp->security = current_security();
+ kisp->security = smk_of_current();
return 0;
}
@@ -2198,9 +2283,11 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
struct super_block *sbp;
struct superblock_smack *sbsp;
struct inode_smack *isp;
- char *csp = current_security();
+ char *csp = smk_of_current();
char *fetched;
char *final;
+ char trattr[TRANS_TRUE_SIZE];
+ int transflag = 0;
struct dentry *dp;
if (inode == NULL)
@@ -2267,9 +2354,10 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
break;
case SOCKFS_MAGIC:
/*
- * Casey says sockets get the smack of the task.
+ * Socket access is controlled by the socket
+ * structures associated with the task involved.
*/
- final = csp;
+ final = smack_known_star.smk_known;
break;
case PROC_SUPER_MAGIC:
/*
@@ -2296,7 +2384,16 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
/*
* This isn't an understood special case.
* Get the value from the xattr.
- *
+ */
+
+ /*
+ * UNIX domain sockets use lower level socket data.
+ */
+ if (S_ISSOCK(inode->i_mode)) {
+ final = smack_known_star.smk_known;
+ break;
+ }
+ /*
* No xattr support means, alas, no SMACK label.
* Use the aforeapplied default.
* It would be curious if the label of the task
@@ -2308,9 +2405,21 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* Get the dentry for xattr.
*/
dp = dget(opt_dentry);
- fetched = smk_fetch(inode, dp);
- if (fetched != NULL)
+ fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp);
+ if (fetched != NULL) {
final = fetched;
+ if (S_ISDIR(inode->i_mode)) {
+ trattr[0] = '\0';
+ inode->i_op->getxattr(dp,
+ XATTR_NAME_SMACKTRANSMUTE,
+ trattr, TRANS_TRUE_SIZE);
+ if (strncmp(trattr, TRANS_TRUE,
+ TRANS_TRUE_SIZE) == 0)
+ transflag = SMK_INODE_TRANSMUTE;
+ }
+ }
+ isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
+
dput(dp);
break;
}
@@ -2320,7 +2429,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
else
isp->smk_inode = final;
- isp->smk_flags |= SMK_INODE_INSTANT;
+ isp->smk_flags |= (SMK_INODE_INSTANT | transflag);
unlockandout:
mutex_unlock(&isp->smk_lock);
@@ -2345,7 +2454,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
if (strcmp(name, "current") != 0)
return -EINVAL;
- cp = kstrdup(task_security(p), GFP_KERNEL);
+ cp = kstrdup(smk_of_task(task_security(p)), GFP_KERNEL);
if (cp == NULL)
return -ENOMEM;
@@ -2369,6 +2478,8 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
static int smack_setprocattr(struct task_struct *p, char *name,
void *value, size_t size)
{
+ struct task_smack *tsp;
+ struct task_smack *oldtsp;
struct cred *new;
char *newsmack;
@@ -2398,10 +2509,18 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (newsmack == smack_known_web.smk_known)
return -EPERM;
+ oldtsp = p->cred->security;
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
- new->security = newsmack;
+ tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL);
+ if (tsp == NULL) {
+ kfree(new);
+ return -ENOMEM;
+ }
+ tsp->smk_task = newsmack;
+ tsp->smk_forked = oldtsp->smk_forked;
+ new->security = tsp;
commit_creds(new);
return size;
}
@@ -2418,14 +2537,18 @@ static int smack_setprocattr(struct task_struct *p, char *name,
static int smack_unix_stream_connect(struct sock *sock,
struct sock *other, struct sock *newsk)
{
- struct inode *sp = SOCK_INODE(sock->sk_socket);
- struct inode *op = SOCK_INODE(other->sk_socket);
+ struct socket_smack *ssp = sock->sk_security;
+ struct socket_smack *osp = other->sk_security;
struct smk_audit_info ad;
+ int rc = 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
smk_ad_setfield_u_net_sk(&ad, other);
- return smk_access(smk_of_inode(sp), smk_of_inode(op),
- MAY_READWRITE, &ad);
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
+
+ return rc;
}
/**
@@ -2438,13 +2561,18 @@ static int smack_unix_stream_connect(struct sock *sock,
*/
static int smack_unix_may_send(struct socket *sock, struct socket *other)
{
- struct inode *sp = SOCK_INODE(sock);
- struct inode *op = SOCK_INODE(other);
+ struct socket_smack *ssp = sock->sk->sk_security;
+ struct socket_smack *osp = other->sk->sk_security;
struct smk_audit_info ad;
+ int rc = 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
smk_ad_setfield_u_net_sk(&ad, other->sk);
- return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE, &ad);
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
+
+ return rc;
}
/**
@@ -2629,7 +2757,7 @@ static int smack_socket_getpeersec_stream(struct socket *sock,
/**
* smack_socket_getpeersec_dgram - pull in packet label
- * @sock: the socket
+ * @sock: the peer socket
* @skb: packet data
* @secid: pointer to where to put the secid of the packet
*
@@ -2640,41 +2768,39 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
{
struct netlbl_lsm_secattr secattr;
- struct sock *sk;
+ struct socket_smack *sp;
char smack[SMK_LABELLEN];
- int family = PF_INET;
- u32 s;
+ int family = PF_UNSPEC;
+ u32 s = 0; /* 0 is the invalid secid */
int rc;
- /*
- * Only works for families with packets.
- */
- if (sock != NULL) {
- sk = sock->sk;
- if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
- return 0;
- family = sk->sk_family;
+ if (skb != NULL) {
+ if (skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+ else if (skb->protocol == htons(ETH_P_IPV6))
+ family = PF_INET6;
}
- /*
- * Translate what netlabel gave us.
- */
- netlbl_secattr_init(&secattr);
- rc = netlbl_skbuff_getattr(skb, family, &secattr);
- if (rc == 0)
- smack_from_secattr(&secattr, smack);
- netlbl_secattr_destroy(&secattr);
+ if (family == PF_UNSPEC && sock != NULL)
+ family = sock->sk->sk_family;
- /*
- * Give up if we couldn't get anything
- */
- if (rc != 0)
- return rc;
-
- s = smack_to_secid(smack);
+ if (family == PF_UNIX) {
+ sp = sock->sk->sk_security;
+ s = smack_to_secid(sp->smk_out);
+ } else if (family == PF_INET || family == PF_INET6) {
+ /*
+ * Translate what netlabel gave us.
+ */
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_skbuff_getattr(skb, family, &secattr);
+ if (rc == 0) {
+ smack_from_secattr(&secattr, smack);
+ s = smack_to_secid(smack);
+ }
+ netlbl_secattr_destroy(&secattr);
+ }
+ *secid = s;
if (s == 0)
return -EINVAL;
-
- *secid = s;
return 0;
}
@@ -2695,7 +2821,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
return;
ssp = sk->sk_security;
- ssp->smk_in = ssp->smk_out = current_security();
+ ssp->smk_in = ssp->smk_out = smk_of_current();
/* cssp->smk_packet is already set in smack_inet_csk_clone() */
}
@@ -2816,7 +2942,7 @@ static void smack_inet_csk_clone(struct sock *sk,
static int smack_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
- key->security = cred->security;
+ key->security = smk_of_task(cred->security);
return 0;
}
@@ -2845,6 +2971,7 @@ static int smack_key_permission(key_ref_t key_ref,
{
struct key *keyp;
struct smk_audit_info ad;
+ char *tsp = smk_of_task(cred->security);
keyp = key_ref_to_ptr(key_ref);
if (keyp == NULL)
@@ -2858,14 +2985,14 @@ static int smack_key_permission(key_ref_t key_ref,
/*
* This should not occur
*/
- if (cred->security == NULL)
+ if (tsp == NULL)
return -EACCES;
#ifdef CONFIG_AUDIT
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY);
ad.a.u.key_struct.key = keyp->serial;
ad.a.u.key_struct.key_desc = keyp->description;
#endif
- return smk_access(cred->security, keyp->security,
+ return smk_access(tsp, keyp->security,
MAY_READWRITE, &ad);
}
#endif /* CONFIG_KEYS */
@@ -3067,6 +3194,8 @@ struct security_operations smack_ops = {
.sb_mount = smack_sb_mount,
.sb_umount = smack_sb_umount,
+ .bprm_set_creds = smack_bprm_set_creds,
+
.inode_alloc_security = smack_inode_alloc_security,
.inode_free_security = smack_inode_free_security,
.inode_init_security = smack_inode_init_security,
@@ -3203,9 +3332,16 @@ static __init void init_smack_know_list(void)
static __init int smack_init(void)
{
struct cred *cred;
+ struct task_smack *tsp;
- if (!security_module_enable(&smack_ops))
+ tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL);
+ if (tsp == NULL)
+ return -ENOMEM;
+
+ if (!security_module_enable(&smack_ops)) {
+ kfree(tsp);
return 0;
+ }
printk(KERN_INFO "Smack: Initializing.\n");
@@ -3213,7 +3349,9 @@ static __init int smack_init(void)
* Set the security state for the initial task.
*/
cred = (struct cred *) current->cred;
- cred->security = &smack_known_floor.smk_known;
+ tsp->smk_forked = smack_known_floor.smk_known;
+ tsp->smk_task = smack_known_floor.smk_known;
+ cred->security = tsp;
/* initialize the smack_know_list */
init_smack_know_list();
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index dc1fd6239f2..362d5eda948 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -109,9 +109,12 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION;
* SMK_ACCESSLEN: Maximum length for a rule access field
* SMK_LOADLEN: Smack rule length
*/
-#define SMK_ACCESS "rwxa"
-#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
-#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
+#define SMK_OACCESS "rwxa"
+#define SMK_ACCESS "rwxat"
+#define SMK_OACCESSLEN (sizeof(SMK_OACCESS) - 1)
+#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
+#define SMK_OLOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN)
+#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
/**
* smk_netlabel_audit_set - fill a netlbl_audit struct
@@ -121,7 +124,7 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap)
{
nap->loginuid = audit_get_loginuid(current);
nap->sessionid = audit_get_sessionid(current);
- nap->secid = smack_to_secid(current_security());
+ nap->secid = smack_to_secid(smk_of_current());
}
/*
@@ -175,6 +178,8 @@ static int load_seq_show(struct seq_file *s, void *v)
seq_putc(s, 'x');
if (srp->smk_access & MAY_APPEND)
seq_putc(s, 'a');
+ if (srp->smk_access & MAY_TRANSMUTE)
+ seq_putc(s, 't');
if (srp->smk_access == 0)
seq_putc(s, '-');
@@ -273,10 +278,15 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
- if (*ppos != 0 || count != SMK_LOADLEN)
+ if (*ppos != 0)
+ return -EINVAL;
+ /*
+ * Minor hack for backward compatability
+ */
+ if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN)
return -EINVAL;
- data = kzalloc(count, GFP_KERNEL);
+ data = kzalloc(SMK_LOADLEN, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -285,6 +295,12 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
goto out;
}
+ /*
+ * More on the minor hack for backward compatability
+ */
+ if (count == (SMK_OLOADLEN))
+ data[SMK_OLOADLEN] = '-';
+
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (rule == NULL) {
rc = -ENOMEM;
@@ -345,6 +361,17 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
goto out_free_rule;
}
+ switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
+ case '-':
+ break;
+ case 't':
+ case 'T':
+ rule->smk_access |= MAY_TRANSMUTE;
+ break;
+ default:
+ goto out_free_rule;
+ }
+
rc = smk_set_access(rule);
if (!rc)
@@ -1160,7 +1187,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char in[SMK_LABELLEN];
- char *sp = current->cred->security;
+ char *sp = smk_of_task(current->cred->security);
if (!capable(CAP_MAC_ADMIN))
return -EPERM;