kernel_samsung_a53x/fs/crypto/fscrypt_ddar.c
2024-06-15 16:02:09 -03:00

381 lines
9 KiB
C
Executable file

/*
* fscrypt_ddar.c
*
* Created on: Oct 15, 2018
* Author: olic.moon
*/
#include <linux/bio.h>
#include "fscrypt_private.h"
extern int dd_submit_bio(struct dd_info *info, struct bio *bio);
int update_encryption_context_with_dd_policy(
struct inode *inode,
const struct dd_policy *policy)
{
union fscrypt_context ctx;
int ret;
dd_info("update encryption context with dd policy ino:%ld flag:%x\n", inode->i_ino, policy->flags);
/**
* i_rwsem needs to be acquired while setting policy so that new files
* cannot be created in the directory during or after the empty_dir() check
*/
inode_lock(inode);
ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
switch (ctx.version) {
case FSCRYPT_CONTEXT_V1: {
if (ret == offsetof(struct fscrypt_context_v1, knox_flags)) {
ctx.v1.knox_flags = 0;
ret = sizeof(ctx.v1);
}
break;
}
case FSCRYPT_CONTEXT_V2: {
if (ret == offsetof(struct fscrypt_context_v2, knox_flags)) {
ctx.v2.knox_flags = 0;
ret = sizeof(ctx.v2);
}
break;
}
}
if (ret == -ENODATA) {
dd_error("failed to set dd policy. empty fscrypto context\n");
ret = -EFAULT;
} else if (ret == fscrypt_context_size(&ctx)) {
struct fscrypt_info *ci = inode->i_crypt_info;
if (ci && ci->ci_policy.version == FSCRYPT_POLICY_V1) {
ctx.v1.knox_flags |= policy->flags << FSCRYPT_KNOX_FLG_DDAR_SHIFT & FSCRYPT_KNOX_FLG_DDAR_MASK;
dd_verbose("fscrypt_context.knox_flag:0x%08x\n", ctx.v1.knox_flags);
} else if (ci && ci->ci_policy.version == FSCRYPT_POLICY_V2) {
ctx.v2.knox_flags |= policy->flags << FSCRYPT_KNOX_FLG_DDAR_SHIFT & FSCRYPT_KNOX_FLG_DDAR_MASK;
dd_verbose("fscrypt_context.knox_flag:0x%08x\n", ctx.v2.knox_flags);
}
// ctx.knox_flags |= policy->flags << FSCRYPT_KNOX_FLG_DDAR_SHIFT & FSCRYPT_KNOX_FLG_DDAR_MASK;
// dd_verbose("fscrypt_context.knox_flag:0x%08x\n", ctx.knox_flags);
ret = ((int (*)(struct inode*, const char*, const void*, size_t, void *))(inode->i_sb->s_cop->android_oem_data1[1]))(inode, NULL, &ctx, fscrypt_context_size(&ctx), NULL);
dd_info("result of set knox context for ino(%ld) : %d\n", inode->i_ino, ret);
} else {
dd_error("failed to set dd policy. get_context rc:%d\n", ret);
ret = -EEXIST;
}
ret = dd_create_crypt_context(inode, policy, NULL);
inode_unlock(inode);
if (!ret) {
struct dd_crypt_context crypt_context;
struct fscrypt_info *ci = inode->i_crypt_info;
if (!ci) {
dd_error("failed to alloc dd_info: no fbe policy found\n");
ret = -EINVAL;
} else {
if (dd_read_crypt_context(inode, &crypt_context) != sizeof(struct dd_crypt_context)) {
dd_error("update_encryption_context_with_dd_policy: failed to read dd crypt context ino:%ld\n", inode->i_ino);
ret = -EINVAL;
} else {
struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci);
ext_ci->ci_dd_info = alloc_dd_info(inode, policy, &crypt_context);
if (IS_ERR(ext_ci->ci_dd_info)) {
dd_error("failed to alloc dd info:%ld\n", inode->i_ino);
ret = -ENOMEM;
ext_ci->ci_dd_info = NULL;
} else {
fscrypt_dd_inc_count();
}
}
}
}
return ret;
}
int dd_oem_page_crypto_inplace(struct dd_info *info, struct page *page, int dir)
{
return fscrypt_crypt_block(info->inode,
(dir == WRITE) ? FS_ENCRYPT:FS_DECRYPT, 0, page, page, PAGE_SIZE, 0, GFP_NOFS);
}
/* { KNOX_SUPPORT_DAR_DUAL_DO */
int fscrypt_dd_has_policy(const struct inode *inode)
{
struct fscrypt_info *ci = NULL;
if (!inode)
return 0;
ci = inode->i_crypt_info;
if (ci) {
struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci);
if (ext_ci->ci_dd_info) {
if (dd_policy_encrypted(ext_ci->ci_dd_info->policy.flags))
return 1;
}
}
return 0;
}
/* } KNOX_SUPPORT_DAR_DUAL_DO */
int fscrypt_dd_encrypted_inode(const struct inode *inode)
{
struct fscrypt_info *ci = NULL;
if (!inode)
return 0;
ci = inode->i_crypt_info;
if (!S_ISREG(inode->i_mode))
return 0;
if (ci) {
struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci);
if (ext_ci->ci_dd_info) {
if (dd_policy_encrypted(ext_ci->ci_dd_info->policy.flags))
return 1;
}
}
return 0;
}
EXPORT_SYMBOL(fscrypt_dd_encrypted_inode);
#ifdef CONFIG_SDP_KEY_DUMP
int fscrypt_dd_is_traced_inode(const struct inode *inode)
{
struct fscrypt_info *ci = NULL;
if (!inode)
return 0;
ci = inode->i_crypt_info;
if (!S_ISREG(inode->i_mode))
return 0;
if (ci) {
struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci);
if (ext_ci->ci_dd_info) {
if (dd_policy_trace_file(ext_ci->ci_dd_info->policy.flags))
return 1;
}
}
return 0;
}
EXPORT_SYMBOL(fscrypt_dd_is_traced_inode);
void fscrypt_dd_trace_inode(const struct inode *inode)
{
struct fscrypt_info *ci = NULL;
if (!inode)
return;
ci = inode->i_crypt_info;
if (ci) {
struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci);
if (ext_ci->ci_dd_info) {
dd_info("update dd trace policy ino:%ld\n", inode->i_ino);
ext_ci->ci_dd_info->policy.flags |= DD_POLICY_TRACE_FILE;
}
}
}
EXPORT_SYMBOL(fscrypt_dd_trace_inode);
#endif
struct inode *fscrypt_bio_get_inode(const struct bio *bio)
{
if (!bio)
return NULL;
if (!bio_has_data((struct bio *)bio))
return NULL;
if (!bio->bi_io_vec)
return NULL;
if (!bio->bi_io_vec->bv_page)
return NULL;
if (PageAnon(bio->bi_io_vec->bv_page)) {
struct inode *inode = NULL;
/* Using direct-io (O_DIRECT) without page cache */
// SDP - START BLOCK
// inode = dio_bio_get_inode((struct bio *)bio);
// dd_verbose("inode on direct-io, inode = 0x%pK.\n", inode);
// SDP - END BLOCK
return inode;
}
if (!page_mapping(bio->bi_io_vec->bv_page))
return NULL;
return page_mapping(bio->bi_io_vec->bv_page)->host;
}
/**
* prevent merging bios from different files when either is ddar enabled
*/
bool fscrypt_dd_can_merge_bio(struct bio *bio, struct address_space *mapping)
{
struct dd_info *info1, *info2;
if (!bio)
return true;
info1 = dd_get_info(fscrypt_bio_get_inode(bio));
info2 = dd_get_info(mapping->host);
if (info1 || info2) {
// either is ddar protected
if (!info1)
goto err_out;
if (!info2)
goto err_out;
if (info1->ino == info2->ino) {
dd_verbose("allowing bio merge ino:%ld\n", info1->ino);
return true;
}
err_out:
dd_verbose("disallowing bio merge ino1:%ld ino2:%ld\n",
info1 ? info1->ino : -1, info2 ? info2->ino : -1);
return false;
}
// none is ddar enabled
return true;
}
void *dd_get_info(const struct inode *inode)
{
struct ext_fscrypt_info *ext_ci;
if (!inode)
return NULL;
if (!inode->i_crypt_info)
return NULL;
ext_ci = GET_EXT_CI(inode->i_crypt_info);
return ext_ci->ci_dd_info;
}
int fscrypt_dd_decrypt_page(struct inode *inode, struct page *page)
{
struct ext_fscrypt_info *ext_ci = GET_EXT_CI(inode->i_crypt_info);
return dd_page_crypto(ext_ci->ci_dd_info, DD_DECRYPT, page, page);
}
void fscrypt_dd_set_count(long count)
{
set_ddar_count(count);
}
long fscrypt_dd_get_count(void)
{
return get_ddar_count();
}
void fscrypt_dd_inc_count(void)
{
inc_ddar_count();
}
void fscrypt_dd_dec_count(void)
{
dec_ddar_count();
}
int fscrypt_dd_is_locked(void)
{
return dd_is_user_deamon_locked();
}
long fscrypt_dd_ioctl(unsigned int cmd, unsigned long *arg, struct inode *inode)
{
if (inode && S_ISDIR(inode->i_mode) && !fscrypt_has_encryption_key(inode)
&& (cmd != FS_IOC_GET_DD_INODE_COUNT)) {
if (fscrypt_prepare_readdir(inode)) {
return -ENOTTY;
}
}
switch (cmd) {
case FS_IOC_GET_DD_POLICY: {
struct dd_info *info;
if (!fscrypt_dd_encrypted_inode(inode))
return -ENOENT;
info = dd_get_info(inode);
if (copy_to_user((void __user *) (*arg), &info->policy,
sizeof(struct dd_policy)))
return -EFAULT;
return 0;
}
case FS_IOC_SET_DD_POLICY: {
struct dd_policy policy;
if (fscrypt_dd_encrypted_inode(inode))
return -EEXIST;
if (copy_from_user(&policy, (struct dd_policy __user *) (*arg),
sizeof(policy))) {
return -EFAULT;
}
return update_encryption_context_with_dd_policy(inode, &policy);
}
case FS_IOC_GET_DD_INODE_COUNT: {
long ret = fscrypt_dd_get_count();
dd_info("FS_IOC_GET_DD_INODE_COUNT - dd_count : %ld", ret);
if (copy_to_user((void __user *) (*arg), &ret, sizeof(long)))
return -EFAULT;
return 0;
}
/* { KNOX_SUPPORT_DAR_DUAL_DO */
case FS_IOC_HAS_DD_POLICY: {
if (!fscrypt_dd_has_policy(inode)) {
dd_error("dd policy is not applied (ino:%ld)\n", inode->i_ino);
return -ENOENT;
}
dd_info("dd policy is applied (ino:%ld)\n", inode->i_ino);
return 0;
}
/* } KNOX_SUPPORT_DAR_DUAL_DO */
}
return 0;
}
int fscrypt_dd_submit_bio(struct inode *inode, struct bio *bio)
{
return dd_submit_bio(dd_get_info(inode), bio);
}
int fscrypt_dd_may_submit_bio(struct bio *bio)
{
struct inode *inode = fscrypt_bio_get_inode(bio);
if (!fscrypt_dd_encrypted_inode(inode))
return -EOPNOTSUPP;
return fscrypt_dd_submit_bio(inode, bio);
}
long fscrypt_dd_get_ino(struct bio *bio)
{
struct inode *inode = fscrypt_bio_get_inode(bio);
if (fscrypt_dd_encrypted_inode(inode))
return inode->i_ino;
return 0;
}
int fscrypt_dd_encrypted(struct bio *bio)
{
struct inode *inode = fscrypt_bio_get_inode(bio);
return fscrypt_dd_encrypted_inode(inode);
}
EXPORT_SYMBOL(fscrypt_dd_encrypted);