Ubifs upgrade to support fscrypt

The Linux kernel configuration item CONFIG_UBIFS_FS_ENCRYPTION:

ubifs fscrypt advantages:

Enable encryption of UBIFS files and directories. This feature is similar to ecryptfs, but it is more memory efficient since it avoids caching the encrypted and decrypted pages in the page cache.

Originally kernel version is 4.9.68, ubifs does not support fscrypt, will now be upgraded to kernel 4.10.10 to support ubifs fscrypt

Need to modify the place: Some changes to UBIFS and fscrypt were required

struct ubifs_info {   //新增加两个bit field,sb占用的字节大小不变,所以可以向后兼容
    struct super_block *vfs_sb;

    ...

    unsigned int double_hash:1;
    unsigned int encrypted:1;

    ...

}

Analytical increase in these two bit field function reads the superblock:

int ubifs_read_superblock(struct ubifs_info *c)

{

    ...

    c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
    c->encrypted = !!(sup_flags & UBIFS_FLG_ENCRYPTION);

}
int ubifs_enable_encryption(struct ubifs_info *c)
{
	int err;
	struct ubifs_sb_node *sup;

	if (c->encrypted)
		return 0;

	if (c->ro_mount || c->ro_media)
		return -EROFS;

	if (c->fmt_version < 5) {
		ubifs_err(c, "on-flash format version 5 is needed for encryption");
		return -EINVAL;
	}

	sup = ubifs_read_sb_node(c);
	if (IS_ERR(sup))
		return PTR_ERR(sup);

	sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);

	err = ubifs_write_sb_node(c, sup);
	if (!err)
		c->encrypted = 1;
	kfree(sup);

	return err;
}
//ioctl中新增命令FS_IOC_SET_ENCRYPTION_POLICY和FS_IOC_GET_ENCRYPTION_POLICY,使能ubifs目录加密

long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    ...
    case FS_IOC_SET_ENCRYPTION_POLICY: {
#ifdef CONFIG_UBIFS_FS_ENCRYPTION
		struct ubifs_info *c = inode->i_sb->s_fs_info;

		err = ubifs_enable_encryption(c);
		if (err)
			return err;

		return fscrypt_ioctl_set_policy(file, (const void __user *)arg);
#else
		return -EOPNOTSUPP;
#endif
	}
	case FS_IOC_GET_ENCRYPTION_POLICY: {
#ifdef CONFIG_UBIFS_FS_ENCRYPTION
		return fscrypt_ioctl_get_policy(file, (void __user *)arg);
#else
		return -EOPNOTSUPP;
#endif
}
//fs/crypto/policy.c
int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) //设置目录加密策略
{
      struct inode *inode = file_inode(filp);
    ...
      if (!inode_has_encryption_context(inode)) {  //目录的xattr中是否加密相关的属性,存在返回TRUE
          if (!S_ISDIR(inode->i_mode))  //不是目录,返回错误
              ret = -EINVAL;
          else if (!inode->i_sb->s_cop->empty_dir) //目录不为空,返回错误
              ret = -EOPNOTSUPP;
          else if (!inode->i_sb->s_cop->empty_dir(inode)) //设置加密的目录不为空,则返回错误
              ret = -ENOTEMPTY;
          else
              ret = create_encryption_context_from_policy(inode,
                                      &policy); //将加密策略保存在目录对应的xattr中
      } else if (!is_encryption_context_consistent_with_policy(inode,
                                   &policy)) {//如果目录已经设置了加密策略,则检查是否一致
          printk(KERN_WARNING
                 "%s: Policy inconsistent with encryption context\n",
                 __func__);
          ret = -EINVAL;
      }
    ...
}
  static int inode_has_encryption_context(struct inode *inode)                                                                                                           
  {
    if (!inode->i_sb->s_cop->get_context)
          return 0;
    return (inode->i_sb->s_cop->get_context(inode, NULL, 0L) > 0); //大于0说明属性存在
  }
//cop是crypt operations的缩写,sb中s_cop是本来就有的字段,对于ubifs新增如下代码
static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
{
    ...
    sb->s_cop = &ubifs_crypt_operations;
    ...
}
//fs/ubifs/crypto.c,这个文件为新增
  struct fscrypt_operations ubifs_crypt_operations = { 
      .flags          = FS_CFLG_OWN_PAGES,
      .get_context        = ubifs_crypt_get_context,
      .set_context        = ubifs_crypt_set_context,
      .is_encrypted       = __ubifs_crypt_is_encrypted,
      .empty_dir      = ubifs_crypt_empty_dir,
      .max_namelen        = ubifs_crypt_max_namelen,
      .key_prefix     = ubifs_key_prefix,
}; 

static inline bool __ubifs_crypt_is_encrypted(struct inode *inode)                                                                                                     
{
    struct ubifs_inode *ui = ubifs_inode(inode);
  
    return ui->flags & UBIFS_CRYPT_FL;  //inode中flags指定是否加密
}
static int ubifs_crypt_get_context(struct inode *inode, void *ctx, size_t len)
{
	return ubifs_xattr_get(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
			       ctx, len);
}
#define UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
//从目录的xattr中取名字为"c"的属性值
static bool ubifs_crypt_empty_dir(struct inode *inode)
{
	return ubifs_check_dir_empty(inode) == 0; //如果目录为空,则返回0
}
  static int create_encryption_context_from_policy(struct inode *inode,
                  const struct fscrypt_policy *policy)
  {
      struct fscrypt_context ctx;
      int res;
      ...
      ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
      memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
                      FS_KEY_DESCRIPTOR_SIZE);
  
      if (!fscrypt_valid_contents_enc_mode( //检查文件内容加密模式
                  policy->contents_encryption_mode)) {
          printk(KERN_WARNING
                 "%s: Invalid contents encryption mode %d\n", __func__,                                                                                                  
              policy->contents_encryption_mode);
          return -EINVAL;
      }
      if (!fscrypt_valid_filenames_enc_mode( //检查文件名字加密模式
                  policy->filenames_encryption_mode)) {
          printk(KERN_WARNING
              "%s: Invalid filenames encryption mode %d\n", __func__,
              policy->filenames_encryption_mode);
          return -EINVAL;
      }
  
      if (policy->flags & ~FS_POLICY_FLAGS_VALID) //检查flags的合法性
          return -EINVAL;
  
      ctx.contents_encryption_mode = policy->contents_encryption_mode;
      ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
      ctx.flags = policy->flags;
      BUILD_BUG_ON(sizeof(ctx.nonce) != FS_KEY_DERIVATION_NONCE_SIZE);
      get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); //nonce填充随机值,FS_KEY_DERIVATION_NONCE_SIZE = 16 byte
  
      return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL);
  }
//目前文件内容加密模式仅仅支持FS_ENCRYPTION_MODE_AES_256_XTS
static inline bool fscrypt_valid_contents_enc_mode(u32 mode)                                                                                                           
  {
    return (mode == FS_ENCRYPTION_MODE_AES_256_XTS);
  }
//目前文件名字加密模式仅仅支持FS_ENCRYPTION_MODE_AES_256_CTS
static inline bool fscrypt_valid_filenames_enc_mode(u32 mode)
  {
    return (mode == FS_ENCRYPTION_MODE_AES_256_CTS);
  }

  static int ubifs_crypt_set_context(struct inode *inode, const void *ctx,
                   size_t len, void *fs_data)
  {
      return ubifs_xattr_set(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
                     ctx, len, 0); 
  }

static int ubifs_file_open(struct inode *inode, struct file *filp)
{
	int ret;
	struct dentry *dir;
	struct ubifs_info *c = inode->i_sb->s_fs_info;

	if (ubifs_crypt_is_encrypted(inode)) {//inode中flags指定是否加密
		ret = fscrypt_get_encryption_info(inode); //得到加密信息
		if (ret)
			return -EACCES;
		if (!fscrypt_has_encryption_key(inode))
			return -ENOKEY;
	}

	dir = dget_parent(file_dentry(filp));
	if (ubifs_crypt_is_encrypted(d_inode(dir)) &&
			!fscrypt_has_permitted_context(d_inode(dir), inode)) {
		ubifs_err(c, "Inconsistent encryption contexts: %lu/%lu",
			  (unsigned long) d_inode(dir)->i_ino,
			  (unsigned long) inode->i_ino);
		dput(dir);
		ubifs_ro_mode(c, -EPERM);
		return -EPERM;
	}
	dput(dir);

	return 0;
}

int fscrypt_get_encryption_info(struct inode *inode)
{
	struct fscrypt_info *crypt_info;
	struct fscrypt_context ctx;
	struct crypto_skcipher *ctfm;
	const char *cipher_str;
	int keysize;
	u8 *raw_key = NULL;
	int res;

	if (inode->i_crypt_info)
		return 0;

	res = fscrypt_initialize(inode->i_sb->s_cop->flags);
	if (res)
		return res;

	res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));//从xattr得到加密策略
    ...
	crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS);
	if (!crypt_info)
		return -ENOMEM;

	crypt_info->ci_flags = ctx.flags;
	crypt_info->ci_data_mode = ctx.contents_encryption_mode;
	crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
	crypt_info->ci_ctfm = NULL;
	memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
				sizeof(crypt_info->ci_master_key)); //注意这里的master_key_descriptor
    //根据加密mode,得到对应的加密器和key的大小
	res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
    ...
	raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS);
    ...
	res = validate_user_key(crypt_info, &ctx, raw_key, //根据前缀检查key的合法性,并将key放入raw_key中
			FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
//#define FS_KEY_DESC_PREFIX             "fscrypt:"
	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);
        ...
	} else if (res) {
		goto out;
	}
got_key:
	ctfm = crypto_alloc_skcipher(cipher_str, 0, 0); //根据名字分配加密器
	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, keysize); //设置key
	if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL) //保存到i_crypt_info中
		crypt_info = NULL;
out:
	if (res == -ENOKEY)
		res = 0;
	put_crypt_info(crypt_info);
	kzfree(raw_key);
	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_descriptor[full_key_len - 1] = '\0';
	keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);//根据full_key_descriptor 得到key
	kfree(full_key_descriptor);
	if (IS_ERR(keyring_key))
		return PTR_ERR(keyring_key);
	down_read(&keyring_key->sem);

	if (keyring_key->type != &key_type_logon) {
		printk_once(KERN_WARNING
				"%s: key type must be logon\n", __func__);
		res = -ENOKEY;
		goto out;
	}
	ukp = user_key_payload(keyring_key);
	if (ukp->datalen != sizeof(struct fscrypt_key)) {
		res = -EINVAL;
		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;
		goto out;
	}
	res = derive_key_aes(ctx->nonce, master_key->raw, raw_key);//nonce作为数据,master_key->raw作为key,进行AES-128-ECB运算加密,加密后的数据存入raw_key
out:
	up_read(&keyring_key->sem);
	key_put(keyring_key);
	return res;
}

/*
 * user defined keys take an arbitrary string as the description and an
 * arbitrary blob of data as the payload
 */
struct key_type key_type_user = {
	.name			= "user",
	.preparse		= user_preparse,
	.free_preparse		= user_free_preparse,
	.instantiate		= generic_key_instantiate,
	.update			= user_update,
	.revoke			= user_revoke,
	.destroy		= user_destroy,
	.describe		= user_describe,
	.read			= user_read,
};

EXPORT_SYMBOL_GPL(key_type_user);

/*
 * This key type is essentially the same as key_type_user, but it does
 * not define a .read op. This is suitable for storing username and
 * password pairs in the keyring that you do not want to be readable
 * from userspace.
 */
struct key_type key_type_logon = {
	.name			= "logon",
	.preparse		= user_preparse,
	.free_preparse		= user_free_preparse,
	.instantiate		= generic_key_instantiate,
	.update			= user_update,
	.revoke			= user_revoke,
	.destroy		= user_destroy,
	.describe		= user_describe,
	.vet_description	= logon_vet_description,
};
EXPORT_SYMBOL_GPL(key_type_logon);

SYSCALL_DEFINE5(add_key, const char __user *, _type,
          const char __user *, _description,
          const void __user *, _payload,
          size_t, plen,
          key_serial_t, ringid)
//通过系统调用add_key,增加一个key,每个key包括type,_description和_payload,key_type_logon 和key_type_user 相似,只是从用户空间读取不出来key,request_key()根据_description得到对应的_payload。

static int read_block(struct inode *inode, void *addr, unsigned int block,
		      struct ubifs_data_node *dn)
{
    ...
	if (ubifs_crypt_is_encrypted(inode)) {
		err = ubifs_decrypt(inode, dn, &dlen, block);//dn中存放着读到的数据,进行解密
		if (err)
			goto dump;
	}
    ...
}

int ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn,
		  unsigned int *out_len, int block)
{
    ...
	err = fscrypt_decrypt_page(inode, virt_to_page(&dn->data), dlen,
			offset_in_page(&dn->data), block);
    ...
	*out_len = clen;

	return 0;
}
  int fscrypt_decrypt_page(const struct inode *inode, struct page *page,                                                                                                 
              unsigned int len, unsigned int offs, u64 lblk_num)
  {
      return do_page_crypto(inode, FS_DECRYPT, lblk_num, page, page, len,
              offs, GFP_NOFS); //解密后的数据保存在同一个page中
  }
static int do_page_crypto(const struct inode *inode,
			fscrypt_direction_t rw, u64 lblk_num,
			struct page *src_page, struct page *dest_page,
			unsigned int len, unsigned int offs,
			gfp_t gfp_flags)
{
	struct {
		__le64 index;
		u8 padding[FS_XTS_TWEAK_SIZE - sizeof(__le64)];
	} xts_tweak;
	struct skcipher_request *req = NULL;
	DECLARE_FS_COMPLETION_RESULT(ecr);
	struct scatterlist dst, src;
	struct fscrypt_info *ci = inode->i_crypt_info;  //从inode从得到fscrypt_info
	struct crypto_skcipher *tfm = ci->ci_ctfm;
	int res = 0;

	BUG_ON(len == 0);

	req = skcipher_request_alloc(tfm, gfp_flags);
	if (!req) {
		printk_ratelimited(KERN_ERR
				"%s: crypto_request_alloc() failed\n",
				__func__);
		return -ENOMEM;
	}

	skcipher_request_set_callback(
		req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
		page_crypt_complete, &ecr);

	BUILD_BUG_ON(sizeof(xts_tweak) != FS_XTS_TWEAK_SIZE);
	xts_tweak.index = cpu_to_le64(lblk_num);
	memset(xts_tweak.padding, 0, sizeof(xts_tweak.padding));

	sg_init_table(&dst, 1);
	sg_set_page(&dst, dest_page, len, offs);
	sg_init_table(&src, 1);
	sg_set_page(&src, src_page, len, offs);
	skcipher_request_set_crypt(req, &src, &dst, len, &xts_tweak);
	if (rw == FS_DECRYPT)
		res = crypto_skcipher_decrypt(req);
	else
		res = crypto_skcipher_encrypt(req);
	if (res == -EINPROGRESS || res == -EBUSY) {
		BUG_ON(req->base.data != &ecr);
		wait_for_completion(&ecr.completion);
		res = ecr.res;
	}
	skcipher_request_free(req);
	if (res) {
		printk_ratelimited(KERN_ERR
			"%s: crypto_skcipher_encrypt() returned %d\n",
			__func__, res);
		return res;
	}
	return 0;
}

What will happen document has been created in a device encryption policy it empty directory

/**
 * ubifs_new_inode - allocate new UBIFS inode object.
 * @c: UBIFS file-system description object
 * @dir: parent directory inode
 * @mode: inode mode flags
 *
 * This function finds an unused inode number, allocates new inode and
 * initializes it. Returns new inode in case of success and an error code in
 * case of failure.
 */
struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
			      umode_t mode)
{
	bool encrypted = false;

	if (ubifs_crypt_is_encrypted(dir)) { //如果父目录是加密的
		err = fscrypt_get_encryption_info(dir); //并且设备了加密策略
		if (err) {
			ubifs_err(c, "fscrypt_get_encryption_info failed: %i", err);
			return ERR_PTR(err);
		}

		if (!fscrypt_has_encryption_key(dir))
			return ERR_PTR(-EPERM);

		encrypted = true;
	}
	inode = new_inode(c->vfs_sb);
	ui = ubifs_inode(inode);
    ...
	if (encrypted) {
		err = fscrypt_inherit_context(dir, inode, &encrypted, true);//继承父目录的加密策略,存放在新创建文件的xattr中
		if (err) {
			ubifs_err(c, "fscrypt_inherit_context failed: %i", err);
			make_bad_inode(inode);
			iput(inode);
			return ERR_PTR(err);
		}
	}

	return inode;
}
static inline int fscrypt_has_encryption_key(const struct inode *inode)                                                                                                
{
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
    return (inode->i_crypt_info != NULL);
#else
    return 0;
#endif
}
/**
 * fscrypt_inherit_context() - Sets a child context from its parent
 * @parent: Parent inode from which the context is inherited.
 * @child:  Child inode that inherits the context from @parent.
 * @fs_data:  private data given by FS.
 * @preload:  preload child i_crypt_info
 *
 * Return: Zero on success, non-zero otherwise
 */
int fscrypt_inherit_context(struct inode *parent, struct inode *child,
						void *fs_data, bool preload)
{
	struct fscrypt_context ctx;
	struct fscrypt_info *ci;
	int res;

	if (!parent->i_sb->s_cop->set_context)
		return -EOPNOTSUPP;

	res = fscrypt_get_encryption_info(parent);
	if (res < 0)
		return res;

	ci = parent->i_crypt_info; //得到父目录的加密策略
	if (ci == NULL)
		return -ENOKEY;

	ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
	if (fscrypt_dummy_context_enabled(parent)) {
		ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
		ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
		ctx.flags = 0;
		memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE);
		res = 0;
	} else {
		ctx.contents_encryption_mode = ci->ci_data_mode;
		ctx.filenames_encryption_mode = ci->ci_filename_mode;
		ctx.flags = ci->ci_flags;
		memcpy(ctx.master_key_descriptor, ci->ci_master_key,
				FS_KEY_DESCRIPTOR_SIZE);
	}
	get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);//不同的nonce
	res = parent->i_sb->s_cop->set_context(child, &ctx,
						sizeof(ctx), fs_data); //保存到新创建文件的xattr中
	if (res)
		return res;
	return preload ? fscrypt_get_encryption_info(child): 0;
}

The whole process took as shown below:

For security reasons, Master keys blob is encrypted, is loaded into the kernel User key in decrypted by hardware call add_key () through the system, while the Master keys descriptors stored in xattr ubifs the corresponding directory, when reading a file, if it is encrypted, then the descriptor is achieved by xattr Master keys Master keys, and then generate xattr file key stored in the Nonce Master keys by encrypting the file key can be used to decode the encrypted file name and the file content.

Note Master keys must be careful care, are generally encrypted storage, or else ubifs fscrypt not make any sense, Master keys after loaded into the kernel can not read from the user space, can only get used inside the kernel, so it is still safe of.

Through the above code analysis, it should be very clear, to see how to use the following:

./fscryptctl insert_key --ext4 <key.data this action by the system is actually increasing call key key_type_logon add_key (), which descriptor is cd8c77009a9a3e6d

keyctl show for the display of key kernel added

./fscryptctl set_policy cd8c77009a9a3e6d / mnt / disks / encrypted / test FS_IOC_SET_ENCRYPTION_POLICY by ubifs_ioctl () is, cd8c77009a9a3e6d is key descriptor, stored in the policy-> master_key_descriptor, and finally through the corresponding directory stored in set_context in xattr.

References:

https://elinux.org/images/f/f6/Slides_24-ubifs.pdf

https://elinux.org/images/9/97/Protecting_your_system.pdf

https://github.com/google/fscryptctl/

Published 85 original articles · won praise 26 · views 120 000 +

Guess you like

Origin blog.csdn.net/whuzm08/article/details/86526546