Merge tag 'rtc-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
[cascardo/linux.git] / fs / crypto / keyinfo.c
index 06f5aa4..82f0285 100644 (file)
@@ -8,11 +8,8 @@
  * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
  */
 
-#include <keys/encrypted-type.h>
 #include <keys/user-type.h>
-#include <linux/random.h>
 #include <linux/scatterlist.h>
-#include <uapi/linux/keyctl.h>
 #include <linux/fscrypto.h>
 
 static void derive_crypt_complete(struct crypto_async_request *req, int rc)
@@ -78,6 +75,99 @@ out:
        return res;
 }
 
+static int validate_user_key(struct fscrypt_info *crypt_info,
+                       struct fscrypt_context *ctx, u8 *raw_key,
+                       u8 *prefix, int prefix_size)
+{
+       u8 *full_key_descriptor;
+       struct key *keyring_key;
+       struct fscrypt_key *master_key;
+       const struct user_key_payload *ukp;
+       int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1;
+       int res;
+
+       full_key_descriptor = kmalloc(full_key_len, GFP_NOFS);
+       if (!full_key_descriptor)
+               return -ENOMEM;
+
+       memcpy(full_key_descriptor, prefix, prefix_size);
+       sprintf(full_key_descriptor + prefix_size,
+                       "%*phN", FS_KEY_DESCRIPTOR_SIZE,
+                       ctx->master_key_descriptor);
+       full_key_descriptor[full_key_len - 1] = '\0';
+       keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
+       kfree(full_key_descriptor);
+       if (IS_ERR(keyring_key))
+               return PTR_ERR(keyring_key);
+
+       if (keyring_key->type != &key_type_logon) {
+               printk_once(KERN_WARNING
+                               "%s: key type must be logon\n", __func__);
+               res = -ENOKEY;
+               goto out;
+       }
+       down_read(&keyring_key->sem);
+       ukp = user_key_payload(keyring_key);
+       if (ukp->datalen != sizeof(struct fscrypt_key)) {
+               res = -EINVAL;
+               up_read(&keyring_key->sem);
+               goto out;
+       }
+       master_key = (struct fscrypt_key *)ukp->data;
+       BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);
+
+       if (master_key->size != FS_AES_256_XTS_KEY_SIZE) {
+               printk_once(KERN_WARNING
+                               "%s: key size incorrect: %d\n",
+                               __func__, master_key->size);
+               res = -ENOKEY;
+               up_read(&keyring_key->sem);
+               goto out;
+       }
+       res = derive_key_aes(ctx->nonce, master_key->raw, raw_key);
+       up_read(&keyring_key->sem);
+       if (res)
+               goto out;
+
+       crypt_info->ci_keyring_key = keyring_key;
+       return 0;
+out:
+       key_put(keyring_key);
+       return res;
+}
+
+static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode,
+                                const char **cipher_str_ret, int *keysize_ret)
+{
+       if (S_ISREG(inode->i_mode)) {
+               if (ci->ci_data_mode == FS_ENCRYPTION_MODE_AES_256_XTS) {
+                       *cipher_str_ret = "xts(aes)";
+                       *keysize_ret = FS_AES_256_XTS_KEY_SIZE;
+                       return 0;
+               }
+               pr_warn_once("fscrypto: unsupported contents encryption mode "
+                            "%d for inode %lu\n",
+                            ci->ci_data_mode, inode->i_ino);
+               return -ENOKEY;
+       }
+
+       if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+               if (ci->ci_filename_mode == FS_ENCRYPTION_MODE_AES_256_CTS) {
+                       *cipher_str_ret = "cts(cbc(aes))";
+                       *keysize_ret = FS_AES_256_CTS_KEY_SIZE;
+                       return 0;
+               }
+               pr_warn_once("fscrypto: unsupported filenames encryption mode "
+                            "%d for inode %lu\n",
+                            ci->ci_filename_mode, inode->i_ino);
+               return -ENOKEY;
+       }
+
+       pr_warn_once("fscrypto: unsupported file type %d for inode %lu\n",
+                    (inode->i_mode & S_IFMT), inode->i_ino);
+       return -ENOKEY;
+}
+
 static void put_crypt_info(struct fscrypt_info *ci)
 {
        if (!ci)
@@ -91,16 +181,11 @@ static void put_crypt_info(struct fscrypt_info *ci)
 int get_crypt_info(struct inode *inode)
 {
        struct fscrypt_info *crypt_info;
-       u8 full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE +
-                               (FS_KEY_DESCRIPTOR_SIZE * 2) + 1];
-       struct key *keyring_key = NULL;
-       struct fscrypt_key *master_key;
        struct fscrypt_context ctx;
-       const struct user_key_payload *ukp;
        struct crypto_skcipher *ctfm;
        const char *cipher_str;
+       int keysize;
        u8 raw_key[FS_MAX_KEY_SIZE];
-       u8 mode;
        int res;
 
        res = fscrypt_initialize();
@@ -123,13 +208,19 @@ retry:
        if (res < 0) {
                if (!fscrypt_dummy_context_enabled(inode))
                        return res;
+               ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
                ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
                ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
                ctx.flags = 0;
        } else if (res != sizeof(ctx)) {
                return -EINVAL;
        }
-       res = 0;
+
+       if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1)
+               return -EINVAL;
+
+       if (ctx.flags & ~FS_POLICY_FLAGS_VALID)
+               return -EINVAL;
 
        crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS);
        if (!crypt_info)
@@ -142,73 +233,33 @@ retry:
        crypt_info->ci_keyring_key = NULL;
        memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
                                sizeof(crypt_info->ci_master_key));
-       if (S_ISREG(inode->i_mode))
-               mode = crypt_info->ci_data_mode;
-       else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-               mode = crypt_info->ci_filename_mode;
-       else
-               BUG();
-
-       switch (mode) {
-       case FS_ENCRYPTION_MODE_AES_256_XTS:
-               cipher_str = "xts(aes)";
-               break;
-       case FS_ENCRYPTION_MODE_AES_256_CTS:
-               cipher_str = "cts(cbc(aes))";
-               break;
-       default:
-               printk_once(KERN_WARNING
-                           "%s: unsupported key mode %d (ino %u)\n",
-                           __func__, mode, (unsigned) inode->i_ino);
-               res = -ENOKEY;
+
+       res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
+       if (res)
                goto out;
-       }
+
        if (fscrypt_dummy_context_enabled(inode)) {
                memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
                goto got_key;
        }
-       memcpy(full_key_descriptor, FS_KEY_DESC_PREFIX,
-                                       FS_KEY_DESC_PREFIX_SIZE);
-       sprintf(full_key_descriptor + FS_KEY_DESC_PREFIX_SIZE,
-                                       "%*phN", FS_KEY_DESCRIPTOR_SIZE,
-                                       ctx.master_key_descriptor);
-       full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE +
-                                       (2 * FS_KEY_DESCRIPTOR_SIZE)] = '\0';
-       keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
-       if (IS_ERR(keyring_key)) {
-               res = PTR_ERR(keyring_key);
-               keyring_key = NULL;
-               goto out;
-       }
-       crypt_info->ci_keyring_key = keyring_key;
-       if (keyring_key->type != &key_type_logon) {
-               printk_once(KERN_WARNING
-                               "%s: key type must be logon\n", __func__);
-               res = -ENOKEY;
-               goto out;
-       }
-       down_read(&keyring_key->sem);
-       ukp = user_key_payload(keyring_key);
-       if (ukp->datalen != sizeof(struct fscrypt_key)) {
-               res = -EINVAL;
-               up_read(&keyring_key->sem);
-               goto out;
-       }
-       master_key = (struct fscrypt_key *)ukp->data;
-       BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);
 
-       if (master_key->size != FS_AES_256_XTS_KEY_SIZE) {
-               printk_once(KERN_WARNING
-                               "%s: key size incorrect: %d\n",
-                               __func__, master_key->size);
-               res = -ENOKEY;
-               up_read(&keyring_key->sem);
+       res = validate_user_key(crypt_info, &ctx, raw_key,
+                       FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
+       if (res && inode->i_sb->s_cop->key_prefix) {
+               u8 *prefix = NULL;
+               int prefix_size, res2;
+
+               prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix);
+               res2 = validate_user_key(crypt_info, &ctx, raw_key,
+                                                       prefix, prefix_size);
+               if (res2) {
+                       if (res2 == -ENOKEY)
+                               res = -ENOKEY;
+                       goto out;
+               }
+       } else if (res) {
                goto out;
        }
-       res = derive_key_aes(ctx.nonce, master_key->raw, raw_key);
-       up_read(&keyring_key->sem);
-       if (res)
-               goto out;
 got_key:
        ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
        if (!ctfm || IS_ERR(ctfm)) {
@@ -221,7 +272,7 @@ got_key:
        crypt_info->ci_ctfm = ctfm;
        crypto_skcipher_clear_flags(ctfm, ~0);
        crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
-       res = crypto_skcipher_setkey(ctfm, raw_key, fscrypt_key_size(mode));
+       res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
        if (res)
                goto out;