summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Markovytch <andreym@codeaurora.org>2015-06-08 11:29:36 +0300
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 21:24:03 -0700
commitecc052ba4efd2ee81fac2a35047bbd34a0344aca (patch)
tree8bf695f7e58b2bf44d03e82600a2b7bf31cb49d5
parenta56866b4eb9939e5e82d56eef4f172c069768712 (diff)
platform: msm: add Per-File-Tagger (PFT) driver
Integrated from msm-3.14. Additional fixes were made to compile with the new kernel and various new warnings and checkpatch issues were fixed Change-Id: I073db1041e41eac9066e37ee099f1da9e4eed6c0 Signed-off-by: Andrey Markovytch <andreym@codeaurora.org> [gbroner@codeaurora.org: fixed merge conflict and adapted the LSM security hooks] Signed-off-by: Gilad Broner <gbroner@codeaurora.org>
-rw-r--r--block/bio.c1
-rw-r--r--block/blk-merge.c6
-rw-r--r--drivers/platform/msm/Kconfig10
-rw-r--r--drivers/platform/msm/Makefile2
-rw-r--r--drivers/platform/msm/pft.c1869
-rw-r--r--fs/direct-io.c14
-rw-r--r--fs/namei.c14
-rw-r--r--include/linux/blk_types.h7
-rw-r--r--include/linux/fs.h2
-rw-r--r--include/linux/lsm_hooks.h7
-rw-r--r--include/linux/pft.h96
-rw-r--r--include/linux/security.h21
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/msm_pft.h134
-rw-r--r--security/security.c21
-rw-r--r--security/selinux/hooks.c59
-rw-r--r--security/selinux/include/objsec.h1
17 files changed, 2264 insertions, 1 deletions
diff --git a/block/bio.c b/block/bio.c
index 4f184d938942..fd3159b2d4ac 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -584,6 +584,7 @@ void __bio_clone_fast(struct bio *bio, struct bio *bio_src)
bio->bi_rw = bio_src->bi_rw;
bio->bi_iter = bio_src->bi_iter;
bio->bi_io_vec = bio_src->bi_io_vec;
+ bio->bi_dio_inode = bio_src->bi_dio_inode;
}
EXPORT_SYMBOL(__bio_clone_fast);
diff --git a/block/blk-merge.c b/block/blk-merge.c
index b966db8f3556..5346f2d79652 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -6,6 +6,7 @@
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/scatterlist.h>
+#include <linux/security.h>
#include "blk.h"
@@ -772,6 +773,11 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio)
!blk_write_same_mergeable(rq->bio, bio))
return false;
+ /* Don't merge bios of files with different encryption */
+ if (!security_allow_merge_bio(rq->bio, bio))
+ return false;
+
+
return true;
}
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 19510e5c2279..e5b95378a336 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -134,6 +134,16 @@ config SSM
This driver uses Secure Channel Manager interface for trustzone
communication and communicates with modem over SMD channel.
+config PFT
+ bool "Per-File-Tagger driver"
+ default n
+ help
+ This driver is used for tagging enterprise files.
+ It is part of the Per-File-Encryption (PFE) feature.
+ The driver is tagging files when created by
+ registered application.
+ Tagged files are encrypted using the dm-req-crypt driver.
+
config GPIO_USB_DETECT
tristate "GPIO-based USB VBUS Detection"
depends on POWER_SUPPLY
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index c33f5e53c1b3..866338c39760 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -1,6 +1,8 @@
#
# Makefile for the MSM specific device drivers.
#
+ccflags-y += -Isecurity/selinux -Isecurity/selinux/include
+obj-$(CONFIG_PFT) += pft.o
obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o
obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o
obj-$(CONFIG_MSM_MHI) += mhi/
diff --git a/drivers/platform/msm/pft.c b/drivers/platform/msm/pft.c
new file mode 100644
index 000000000000..9884fd3366ac
--- /dev/null
+++ b/drivers/platform/msm/pft.c
@@ -0,0 +1,1869 @@
+/*
+ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Per-File-Tagger (PFT).
+ *
+ * This driver tags enterprise file for encryption/decryption,
+ * as part of the Per-File-Encryption (PFE) feature.
+ *
+ * Enterprise registered applications are identified by their UID.
+ *
+ * The PFT exposes character-device interface to the user-space application,
+ * to handle the following commands:
+ * 1. Update registered applications list
+ * 2. Encryption (in-place) of a file that was created before.
+ * 3. Set State - update the state.
+ *
+ * The PFT exposes kernel API hooks that are intercepting file operations
+ * like create/open/read/write for tagging files and also for access control.
+ * It utilizes the existing security framework hooks
+ * that calls the selinux hooks.
+ *
+ * The PFT exposes kernel API to the dm-req-crypt driver to provide the info
+ * if a file is tagged or not. The dm-req-crypt driver is doing the
+ * actual encryption/decryptiom.
+ *
+ * Tagging the file:
+ * 1. Non-volatile tagging on storage using file extra-attribute (xattr).
+ * 2. Volatile tagging on the file's inode, for fast access.
+ *
+ */
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+
+#define pr_fmt(fmt) "pft [%s]: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/cred.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+#include <linux/blkdev.h>
+#include <linux/elevator.h>
+#include <linux/bio.h>
+#include <linux/bitops.h>
+#include <linux/fdtable.h>
+#include <linux/selinux.h>
+#include <linux/security.h>
+#include <linux/lsm_hooks.h>
+
+#include <linux/pft.h>
+#include <uapi/linux/msm_pft.h>
+
+#include "objsec.h"
+
+/* File tagging as encrypted/non-encrypted is valid */
+#define PFT_TAG_MAGIC ((u32)(0xABC00000))
+
+/* File tagged as encrypted */
+#define PFT_TAG_ENCRYPTED BIT(16)
+
+#define PFT_TAG_MAGIC_MASK 0xFFF00000
+#define PFT_TAG_FLAGS_MASK 0x000F0000
+#define PFT_TAG_KEY_MASK 0x0000FFFF
+
+/* The default encryption key index */
+#define PFT_DEFAULT_KEY_INDEX 1
+
+/* The default key index for non-encrypted files */
+#define PFT_NO_KEY 0
+
+/* PFT extended attribute name */
+#define XATTR_NAME_PFE "security.pfe"
+
+/* PFT driver requested major number */
+#define PFT_REQUESTED_MAJOR 213
+
+/* PFT driver name */
+#define DEVICE_NAME "pft"
+
+/* Maximum registered applications */
+#define PFT_MAX_APPS 1000
+
+/* Maximum command size */
+#define PFT_MAX_COMMAND_SIZE (PAGE_SIZE)
+
+/* Current Process ID */
+#define current_pid() ((u32)(current->pid))
+
+static const char *pft_state_name[PFT_STATE_MAX_INDEX] = {
+ "deactivated",
+ "deactivating",
+ "key_removed",
+ "removing_key",
+ "key_loaded",
+};
+
+/**
+ * struct pft_file_info - pft file node info.
+ * @file: pointer to file stucture.
+ * @pid: process ID.
+ * @list: next list item.
+ *
+ * A node in the list of the current open encrypted files.
+ */
+struct pft_file_info {
+ struct file *file;
+ pid_t pid;
+ struct list_head list;
+};
+
+/**
+ * struct pft_device - device state structure.
+ *
+ * @open_count: device open count.
+ * @major: device major number.
+ * @state: Per-File-Encryption state.
+ * @response: command response.
+ * @pfm_pid: PFM process id.
+ * @inplace_file: file for in-place encryption.
+ * @uid_table: registered application array (UID).
+ * @uid_count: number of registered applications.
+ * @open_file_list: open encrypted file list.
+ * @lock: lock protect list access.
+ *
+ * The open_count purpose is to ensure that only one user space
+ * application uses this driver.
+ * The open_file_list is used to close open encrypted files
+ * after the key is removed from the encryption hardware.
+ */
+struct pft_device {
+ struct cdev cdev;
+ dev_t device_no;
+ struct class *driver_class;
+ int open_count;
+ int major;
+ enum pft_state state;
+ struct pft_command_response response;
+ u32 pfm_pid;
+ struct file *inplace_file;
+ kuid_t *uid_table;
+ u32 uid_count;
+ struct list_head open_file_list;
+ struct mutex lock;
+ bool is_chosen_lsm;
+};
+
+/* Device Driver State */
+static struct pft_device *pft_dev;
+
+static struct inode *pft_bio_get_inode(struct bio *bio);
+
+static int pft_inode_alloc_security(struct inode *inode)
+{
+ struct inode_security_struct *i_sec = NULL;
+
+ i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL);
+
+ if (i_sec == NULL)
+ return -ENOMEM;
+
+ inode->i_security = i_sec;
+
+ return 0;
+}
+
+static void pft_inode_free_security(struct inode *inode)
+{
+ kzfree(inode->i_security);
+}
+
+static struct security_hook_list pft_hooks[] = {
+ LSM_HOOK_INIT(inode_create, pft_inode_create),
+ LSM_HOOK_INIT(inode_post_create, pft_inode_post_create),
+ LSM_HOOK_INIT(inode_unlink, pft_inode_unlink),
+ LSM_HOOK_INIT(inode_mknod, pft_inode_mknod),
+ LSM_HOOK_INIT(inode_rename, pft_inode_rename),
+ LSM_HOOK_INIT(inode_setxattr, pft_inode_set_xattr),
+ LSM_HOOK_INIT(inode_alloc_security, pft_inode_alloc_security),
+ LSM_HOOK_INIT(inode_free_security, pft_inode_free_security),
+
+ LSM_HOOK_INIT(file_open, pft_file_open),
+ LSM_HOOK_INIT(file_permission, pft_file_permission),
+ LSM_HOOK_INIT(file_close, pft_file_close),
+
+ LSM_HOOK_INIT(allow_merge_bio, pft_allow_merge_bio),
+};
+
+static int __init pft_lsm_init(struct pft_device *dev)
+{
+ /* Check if PFT is the chosen lsm via security_module_enable() */
+ if (security_module_enable("pft")) {
+ security_add_hooks(pft_hooks, ARRAY_SIZE(pft_hooks));
+ dev->is_chosen_lsm = true;
+ pr_debug("pft is the chosen lsm, registered successfully !\n");
+ } else {
+ pr_debug("pft is not the chosen lsm.\n");
+ }
+
+ return 0;
+}
+
+/**
+ * pft_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static bool pft_is_ready(void)
+{
+ return (pft_dev != NULL);
+}
+
+/**
+ * file_to_filename() - get the filename from file pointer.
+ * @filp: file pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+static char *file_to_filename(struct file *filp)
+{
+ struct dentry *dentry = NULL;
+ char *filename = NULL;
+
+ if (!filp || !filp->f_path.dentry)
+ return "unknown";
+
+ dentry = filp->f_path.dentry;
+ filename = dentry->d_iname;
+
+ return filename;
+}
+
+/**
+ * inode_to_filename() - get the filename from inode pointer.
+ * @inode: inode pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+static char *inode_to_filename(struct inode *inode)
+{
+ struct dentry *dentry = NULL;
+ char *filename = NULL;
+
+ if (hlist_empty(&inode->i_dentry))
+ return "unknown";
+
+ dentry = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
+ filename = dentry->d_iname;
+
+ return filename;
+}
+
+/**
+ * pft_set_response() - set response error code.
+ *
+ * @error_code: The error code to return on response.
+ */
+static inline void pft_set_response(u32 error_code)
+{
+ pft_dev->response.error_code = error_code;
+}
+
+/**
+ * pft_add_file()- Add the file to the list of opened encrypted
+ * files.
+ * @filp: file to add.
+ *
+ * Return: 0 of successful operation, negative value otherwise.
+ */
+static int pft_add_file(struct file *filp)
+{
+ struct pft_file_info *node = NULL;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ node->file = filp;
+ INIT_LIST_HEAD(&node->list);
+
+ mutex_lock(&pft_dev->lock);
+ list_add(&node->list, &pft_dev->open_file_list);
+ pr_debug("adding file %s to open list.\n", file_to_filename(filp));
+ mutex_unlock(&pft_dev->lock);
+
+ return 0;
+}
+
+/**
+ * pft_remove_file()- Remove the given file from the list of
+ * open encrypted files.
+ * @filp: file to remove.
+ *
+ * Return: 0 on success, negative value on failure.
+ */
+static int pft_remove_file(struct file *filp)
+{
+ int ret = -ENOENT;
+ struct pft_file_info *tmp = NULL;
+ struct list_head *pos = NULL;
+ struct list_head *next = NULL;
+ bool found = false;
+
+ mutex_lock(&pft_dev->lock);
+ list_for_each_safe(pos, next, &pft_dev->open_file_list) {
+ tmp = list_entry(pos, struct pft_file_info, list);
+ if (filp == tmp->file) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ pr_debug("remove file %s. from open list.\n ",
+ file_to_filename(filp));
+ list_del(&tmp->list);
+ kfree(tmp);
+ ret = 0;
+ }
+ mutex_unlock(&pft_dev->lock);
+
+ return ret;
+}
+
+/**
+ * pft_is_current_process_registered()- Check if current process
+ * is registered.
+ *
+ * Return: true if current process is registered.
+ */
+static bool pft_is_current_process_registered(void)
+{
+ int is_registered = false;
+ int i;
+ kuid_t uid = current_uid();
+
+ mutex_lock(&pft_dev->lock);
+ for (i = 0; i < pft_dev->uid_count; i++) {
+ if (uid_eq(pft_dev->uid_table[i], uid)) {
+ pr_debug("current UID [%u] is registered.\n",
+ __kuid_val(uid));
+ is_registered = true;
+ break;
+ }
+ }
+ mutex_unlock(&pft_dev->lock);
+
+ return is_registered;
+}
+
+/**
+ * pft_is_xattr_supported() - Check if the filesystem supports
+ * extended attributes.
+ * @indoe: pointer to the file inode
+ *
+ * Return: true if supported, false if not.
+ */
+static bool pft_is_xattr_supported(struct inode *inode)
+{
+ if (inode == NULL) {
+ pr_err("invalid argument inode passed as NULL");
+ return false;
+ }
+
+ if (inode->i_security == NULL) {
+ pr_debug("i_security is NULL, not ready yet\n");
+ return false;
+ }
+
+ if (inode->i_op == NULL) {
+ pr_debug("i_op is NULL\n");
+ return false;
+ }
+
+ if (inode->i_op->getxattr == NULL) {
+ pr_debug_once("getxattr() not supported , filename=%s\n",
+ inode_to_filename(inode));
+ return false;
+ }
+
+ if (inode->i_op->setxattr == NULL) {
+ pr_debug("setxattr() not supported\n");
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * pft_get_inode_tag() - get the file tag.
+ * @indoe: pointer to the file inode
+ *
+ * Return: tag
+ */
+static u32 pft_get_inode_tag(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ if (isec == NULL)
+ return 0;
+
+ return isec->tag;
+}
+
+/**
+ * pft_get_inode_key_index() - get the file key.
+ * @indoe: pointer to the file inode
+ *
+ * Return: key index
+ */
+static inline u32 pft_get_inode_key_index(struct inode *inode)
+{
+ return pft_get_inode_tag(inode) & PFT_TAG_KEY_MASK;
+}
+
+/**
+ * pft_is_tag_valid() - is the tag valid
+ * @indoe: pointer to the file inode
+ *
+ * The tagging is set to valid when an enterprise file is created
+ * or when an file is opened first time after power up and the
+ * xattr was checked to see if the file is encrypted or not.
+ *
+ * Return: true if the tag is valid.
+ */
+static inline bool pft_is_tag_valid(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ if (isec == NULL)
+ return false;
+
+ return ((isec->tag & PFT_TAG_MAGIC_MASK) == PFT_TAG_MAGIC) ?
+ true : false;
+}
+
+/**
+ * pft_is_file_encrypted() - is inode tagged as encrypted.
+ *
+ * @tag: holds the key index and tagging flags.
+ *
+ * Return: true if the file is encrypted.
+ */
+static inline bool pft_is_file_encrypted(u32 tag)
+{
+ return (tag & PFT_TAG_ENCRYPTED) ? true : false;
+}
+
+/**
+ * pft_tag_inode_non_encrypted() - Tag the inode as
+ * non-encrypted.
+ * @indoe: pointer to the file inode
+ *
+ * Tag file as non-encrypted, only the valid bit is set,
+ * the encrypted bit is not set.
+ */
+static inline void pft_tag_inode_non_encrypted(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ isec->tag = (u32)(PFT_TAG_MAGIC);
+}
+
+/**
+ * pft_tag_inode_encrypted() - Tag the inode as encrypted.
+ * @indoe: pointer to the file inode
+ *
+ * Set the valid bit, the encrypted bit, and the key index.
+ */
+static void pft_tag_inode_encrypted(struct inode *inode, u32 key_index)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ isec->tag = key_index | PFT_TAG_ENCRYPTED | PFT_TAG_MAGIC;
+}
+
+/**
+ * pft_get_file_tag()- get the file tag.
+ * @dentry: pointer to file dentry.
+ * @tag_ptr: pointer to tag.
+ *
+ * This is the major function for detecting tag files.
+ * Get the tag from the inode if tag is valid,
+ * or from the xattr if this is the 1st time after power up.
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+static int pft_get_file_tag(struct dentry *dentry, u32 *tag_ptr)
+{
+ ssize_t size = 0;
+ struct inode *inode;
+ const char *xattr_name = XATTR_NAME_PFE;
+ u32 key;
+
+ if (!dentry || !dentry->d_inode || !tag_ptr) {
+ pr_err("invalid param");
+ return -EINVAL;
+ }
+
+ inode = dentry->d_inode;
+ if (pft_is_tag_valid(inode)) {
+ *tag_ptr = pft_get_inode_tag(inode);
+ return 0;
+ }
+
+ /*
+ * For the first time reading the tag, the tag is not valid, hence
+ * get xattr.
+ */
+ size = inode->i_op->getxattr(dentry, xattr_name, &key, sizeof(key));
+
+ if (size == -ENODATA || size == -EOPNOTSUPP) {
+ pft_tag_inode_non_encrypted(inode);
+ *tag_ptr = pft_get_inode_tag(inode);
+ } else if (size > 0) {
+ pr_debug("First time file %s opened, found xattr = %u.\n",
+ inode_to_filename(inode), key);
+ pft_tag_inode_encrypted(inode, key);
+ *tag_ptr = pft_get_inode_tag(inode);
+ } else {
+ pr_err("getxattr() failure, ret=%zu.\n", size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * pft_tag_file() - Tag the file saving the key_index.
+ * @dentry: file dentry.
+ * @key_index: encryption key index.
+ *
+ * This is the major function for tagging a file.
+ * Tag the file on both the xattr and the inode.
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+static int pft_tag_file(struct dentry *dentry, u32 key_index)
+{
+ int size = 0;
+ const char *xattr_name = XATTR_NAME_PFE;
+
+ if (!dentry || !dentry->d_inode) {
+ pr_err("invalid NULL param");
+ return -EINVAL;
+ }
+
+ if (!pft_is_xattr_supported(dentry->d_inode)) {
+ pr_err("set xattr for file %s is not support.\n",
+ dentry->d_iname);
+ return -EINVAL;
+ }
+
+ size = dentry->d_inode->i_op->setxattr(dentry, xattr_name, &key_index,
+ sizeof(key_index), 0);
+ if (size < 0) {
+ pr_err("failed to set xattr for file %s, ret =%d.\n",
+ dentry->d_iname, size);
+ return -EFAULT;
+ }
+
+ pft_tag_inode_encrypted(dentry->d_inode, key_index);
+ pr_debug("file %s tagged encrypted\n", dentry->d_iname);
+
+ return 0;
+}
+
+/**
+ * pft_get_app_key_index() - get the application key index.
+ * @uid: registered application UID
+ *
+ * Get key index based on the given registered application UID.
+ * Currently only one key is supported.
+ *
+ * Return: encryption key index.
+ */
+static inline u32 pft_get_app_key_index(kuid_t uid)
+{
+ return PFT_DEFAULT_KEY_INDEX;
+}
+
+/**
+ * pft_is_encrypted_file() - is the file encrypted.
+ * @dentry: file pointer.
+ *
+ * Return: true if the file is encrypted, false otherwise.
+ */
+static bool pft_is_encrypted_file(struct dentry *dentry)
+{
+ int rc;
+ u32 tag;
+
+ if (!pft_is_ready())
+ return false;
+
+ if (!pft_is_xattr_supported(dentry->d_inode))
+ return false;
+
+ rc = pft_get_file_tag(dentry, &tag);
+ if (rc < 0)
+ return false;
+
+ return pft_is_file_encrypted(tag);
+}
+
+/**
+ * pft_is_inplace_inode() - is this the inode of file for
+ * in-place encryption.
+ * @inode: inode of file to check.
+ *
+ * Return: true if this file is being encrypted, false
+ * otherwise.
+ */
+static bool pft_is_inplace_inode(struct inode *inode)
+{
+ if (!pft_dev->inplace_file || !pft_dev->inplace_file->f_path.dentry)
+ return false;
+
+ return (pft_dev->inplace_file->f_path.dentry->d_inode == inode);
+}
+
+/**
+ * pft_is_inplace_file() - is this the file for in-place
+ * encryption.
+ * @filp: file to check.
+ *
+ * A file struct might be allocated per process, inode should be
+ * only one.
+ *
+ * Return: true if this file is being encrypted, false
+ * otherwise.
+ */
+static inline bool pft_is_inplace_file(struct file *filp)
+{
+ if (!filp || !filp->f_path.dentry || !filp->f_path.dentry->d_inode)
+ return false;
+
+ return pft_is_inplace_inode(filp->f_path.dentry->d_inode);
+}
+
+/**
+ * pft_get_key_index() - get the key index and other indications
+ * @inode: Pointer to inode struct
+ * @key_index: Pointer to the return value of key index
+ * @is_encrypted: Pointer to the return value.
+ * @is_inplace: Pointer to the return value.
+ *
+ * Provides the given inode's encryption key index, and well as
+ * indications whether the file is encrypted or is it currently
+ * being in-placed encrypted.
+ * This API is called by the dm-req-crypt to decide if to
+ * encrypt/decrypt the file.
+ * File tagging depends on the hooks to be called from selinux,
+ * so if selinux is disabled then tagging is also not
+ * valid.
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_get_key_index(struct bio *bio, u32 *key_index,
+ bool *is_encrypted, bool *is_inplace)
+{
+ u32 tag = 0;
+ struct inode *inode = NULL;
+
+ if (!pft_is_ready())
+ return -ENODEV;
+
+ if (!selinux_is_enabled() && !pft_dev->is_chosen_lsm)
+ return -ENODEV;
+
+ if (!bio)
+ return -EPERM;
+
+ if (!is_encrypted) {
+ pr_err("is_encrypted is NULL\n");
+ return -EPERM;
+ }
+ if (!is_inplace) {
+ pr_err("is_inplace is NULL\n");
+ return -EPERM;
+ }
+ if (!key_index) {
+ pr_err("key_index is NULL\n");
+ return -EPERM;
+ }
+
+ inode = pft_bio_get_inode(bio);
+ if (!inode)
+ return -EINVAL;
+
+ if (!pft_is_tag_valid(inode)) {
+ pr_debug("file %s, Tag not valid\n", inode_to_filename(inode));
+ return -EINVAL;
+ }
+
+ if (!pft_is_xattr_supported(inode)) {
+ *is_encrypted = false;
+ *is_inplace = false;
+ *key_index = 0;
+ return 0;
+ }
+
+ tag = pft_get_inode_tag(inode);
+
+ *is_encrypted = pft_is_file_encrypted(tag);
+ *key_index = pft_get_inode_key_index(inode);
+ *is_inplace = pft_is_inplace_inode(inode);
+
+ if (*is_encrypted)
+ pr_debug("file %s is encrypted\n", inode_to_filename(inode));
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_get_key_index);
+
+/**
+ * pft_bio_get_inode() - get the inode from a bio.
+ * @bio: Pointer to BIO structure.
+ *
+ * Walk the bio struct links to get the inode.
+ *
+ * Return: pointer to the inode struct if successful, or NULL otherwise.
+ */
+static struct inode *pft_bio_get_inode(struct bio *bio)
+{
+ if (!bio)
+ return NULL;
+ /* check bio vec count > 0 before using the bio->bi_io_vec[] array */
+ if (!bio->bi_vcnt)
+ 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;
+
+ /* Using direct-io (O_DIRECT) without page cache */
+ inode = dio_bio_get_inode(bio);
+ pr_debug("inode on direct-io, inode = 0x%p.\n", inode);
+
+ return inode;
+ }
+
+ if (!bio->bi_io_vec->bv_page->mapping)
+ return NULL;
+
+ if (!bio->bi_io_vec->bv_page->mapping->host)
+ return NULL;
+
+ return bio->bi_io_vec->bv_page->mapping->host;
+}
+
+/**
+ * pft_allow_merge_bio()- Check if 2 BIOs can be merged.
+ * @bio1: Pointer to first BIO structure.
+ * @bio2: Pointer to second BIO structure.
+ *
+ * Prevent merging of BIOs from encrypted and non-encrypted
+ * files, or files encrypted with different key.
+ * This API is called by the file system block layer.
+ *
+ * Return: true if the BIOs allowed to be merged, false
+ * otherwise.
+ */
+bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+{
+ u32 key_index1 = 0, key_index2 = 0;
+ bool is_encrypted1 = false, is_encrypted2 = false;
+ bool allow = false;
+ bool is_inplace = false; /* N.A. */
+ int ret;
+
+ if (!pft_is_ready())
+ return true;
+
+ /*
+ * Encrypted BIOs are created only when file encryption is enabled,
+ * which happens only when key is loaded.
+ */
+ if (pft_dev->state != PFT_STATE_KEY_LOADED)
+ return true;
+
+ ret = pft_get_key_index(bio1, &key_index1,
+ &is_encrypted1, &is_inplace);
+ if (ret)
+ is_encrypted1 = false;
+
+ ret = pft_get_key_index(bio2, &key_index2,
+ &is_encrypted2, &is_inplace);
+ if (ret)
+ is_encrypted2 = false;
+
+ allow = ((is_encrypted1 == is_encrypted2) &&
+ (key_index1 == key_index2));
+
+ return allow;
+}
+EXPORT_SYMBOL(pft_allow_merge_bio);
+
+/**
+ * pft_inode_create() - file creation callback.
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ * @mode: flags
+ *
+ * This hook is called when file is created by VFS.
+ * This hook is called from the selinux driver.
+ * This hooks check file creation permission for enterprise
+ * applications.
+ * Call path:
+ * vfs_create()->security_inode_create()->selinux_inode_create()
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ if (!dir || !dentry)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_LOADED:
+ break;
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /* At this state no new encrypted files can be created */
+ if (pft_is_current_process_registered()) {
+ pr_debug("key removed, registered uid %u is denied from creating new file %s\n",
+ __kuid_val(current_uid()), dentry->d_iname);
+ return -EACCES;
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL(pft_inode_create);
+
+/**
+ * pft_inode_post_create() - file creation callback.
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ * @mode: flags
+ *
+ * This hook is called when file is created by VFS.
+ * This hook is called from the selinux driver.
+ * This hooks tags new files as encrypted when created by
+ * enterprise applications.
+ * Call path:
+ * vfs_create()->security_inode_post_create()->selinux_inode_post_create()
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+{
+ int ret;
+
+ if (!dir || !dentry)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ break;
+ case PFT_STATE_KEY_LOADED:
+ /* Check whether the new file should be encrypted */
+ if (pft_is_current_process_registered()) {
+ u32 key_index = pft_get_app_key_index(current_uid());
+
+ ret = pft_tag_file(dentry, key_index);
+
+ if (ret == 0)
+ pr_debug("pid[%u] uid[%d] creating file %s\n",
+ current_pid(),
+ __kuid_val(current_uid()),
+ dentry->d_iname);
+ else {
+ pr_err("Failed to tag file %s by pid %d\n",
+ dentry->d_iname, current_pid());
+ return -EFAULT;
+ }
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_inode_post_create);
+
+/**
+ * pft_inode_mknod() - mknode file hook (callback)
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ * @mode: flags
+ * @dev:
+ *
+ * This hook checks encrypted file access permission by
+ * enterprise application.
+ * Call path:
+ * vfs_mknod()->security_inode_mknod()->selinux_inode_mknod()->pft_inode_mknod()
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+int pft_inode_mknod(struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+{
+ int rc;
+
+ /* Check if allowed to create new encrypted files */
+ rc = pft_inode_create(dir, dentry, mode);
+
+ return rc;
+}
+EXPORT_SYMBOL(pft_inode_mknod);
+
+/**
+ * pft_inode_rename() - file rename hook.
+ * @inode: directory inode
+ * @dentry: file dentry
+ * @new_inode
+ * @new_dentry
+ *
+ * Block attempt to rename enterprise file.
+ *
+ * Return: 0 on allowed operation, negative value otherwise.
+ */
+int pft_inode_rename(struct inode *inode, struct dentry *dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{
+ if (!inode || !dentry || !new_inode || !new_dentry || !dentry->d_inode)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(dentry))
+ return 0;
+
+ pr_debug("attempt to rename encrypted file [%s]\n", dentry->d_iname);
+
+ if (pft_is_inplace_inode(dentry->d_inode)) {
+ pr_err("access to file %s by uid [%d] pid [%d] is blocked.\n",
+ inode_to_filename(inode), __kuid_val(current_uid()),
+ current_pid());
+ return -EACCES;
+ }
+
+ if (!pft_is_current_process_registered()) {
+ pr_err("app (uid %u pid %u) can't access file %s\n",
+ __kuid_val(current_uid()), current_pid(),
+ dentry->d_iname);
+ return -EACCES;
+ }
+
+ pr_debug("rename file %s\n", dentry->d_iname);
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_inode_rename);
+
+/**
+ * pft_file_open() - file open hook (callback).
+ * @filp: file pointer
+ * @cred: credentials pointer
+ *
+ * This hook is called when file is opened by VFS.
+ * It is called from the selinux driver.
+ * It checks enterprise file xattr when first opened.
+ * It adds encrypted file to the list of open files.
+ * Call path:
+ * do_filp_open()->security_dentry_open()->selinux_dentry_open()
+ *
+ * Return: 0 on successe, negative value on failure.
+ */
+int pft_file_open(struct file *filp, const struct cred *cred)
+{
+ int ret;
+
+ if (!filp || !filp->f_path.dentry)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ if (filp->f_flags & O_DIRECT)
+ pr_debug("file %s using O_DIRECT.\n", file_to_filename(filp));
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(filp->f_path.dentry))
+ return 0;
+
+ /*
+ * Only PFM allowed to access in-place-encryption-file
+ * during in-place-encryption process
+ */
+ if (pft_is_inplace_file(filp) && current_pid() != pft_dev->pfm_pid) {
+ pr_err("Access to file %s by uid %d pid %d is blocked.\n",
+ file_to_filename(filp),
+ __kuid_val(current_uid()),
+ current_pid());
+ return -EACCES;
+ }
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /* Block any access for encrypted files when key not loaded */
+ pr_debug("key not loaded. uid (%u) can not access file %s\n",
+ __kuid_val(current_uid()), file_to_filename(filp));
+ return -EACCES;
+ case PFT_STATE_KEY_LOADED:
+ /* Only registered apps may access encrypted files. */
+ if (!pft_is_current_process_registered()) {
+ pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
+ __kuid_val(current_uid()), current_pid(),
+ file_to_filename(filp));
+ return -EACCES;
+ }
+
+ ret = pft_add_file(filp);
+ if (ret) {
+ pr_err("failed to add file %s to the list.\n",
+ file_to_filename(filp));
+ return -EFAULT;
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_file_open);
+
+/**
+ * pft_file_permission() - check file access permission.
+ * @filp: file pointer
+ * @mask: flags
+ *
+ * This hook is called when file is read/write by VFS.
+ * This hook is called from the selinux driver.
+ * This hook checks encrypted file access permission by
+ * enterprise application.
+ * Call path:
+ * vfs_read()->security_file_permission()->selinux_file_permission()
+ *
+ * Return: 0 on success, negative value on failure.
+ */
+int pft_file_permission(struct file *filp, int mask)
+{
+ if (!filp)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(filp->f_path.dentry))
+ return 0;
+
+ /*
+ * Only PFM allowed to access in-place-encryption-file
+ * during in-place encryption process
+ */
+ if (pft_is_inplace_file(filp)) {
+ if (current_pid() == pft_dev->pfm_pid) {
+ /* mask MAY_WRITE=2 / MAY_READ=4 */
+ pr_debug("r/w [mask 0x%x] file %s (UID %d, PID %d).\n",
+ mask, file_to_filename(filp),
+ __kuid_val(current_uid()), current_pid());
+ return 0;
+ }
+ pr_err("Access to file %s by (UID %d, PID %d) is blocked.\n",
+ file_to_filename(filp),
+ __kuid_val(current_uid()), current_pid());
+ return -EACCES;
+
+ }
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_REMOVED:
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /* Block any access for encrypted files when key not loaded */
+ pr_debug("key not loaded. uid (%u) can not access file %s\n",
+ __kuid_val(current_uid()), file_to_filename(filp));
+ return -EACCES;
+ case PFT_STATE_KEY_LOADED:
+ /* Only registered apps can access encrypted files. */
+ if (!pft_is_current_process_registered()) {
+ pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
+ __kuid_val(current_uid()), current_pid(),
+ file_to_filename(filp));
+ return -EACCES;
+ }
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_file_permission);
+
+/**
+ * pft_sync_file() - sync the file.
+ * @filp: file pointer
+ *
+ * Complete writing any pending write request of encrypted data
+ * before key is removed, to avoid writing garbage to
+ * enterprise files.
+ */
+static void pft_sync_file(struct file *filp)
+{
+ int ret;
+
+ ret = vfs_fsync(filp, false);
+
+ if (ret)
+ pr_debug("failed to sync file %s, ret = %d.\n",
+ file_to_filename(filp), ret);
+ else
+ pr_debug("Sync file %s ok.\n", file_to_filename(filp));
+
+}
+
+/**
+ * pft_file_close()- handle file close event
+ * @filp: file pointer
+ *
+ * This hook is called when file is closed by VFS.
+ * This hook is called from the selinux driver.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+int pft_file_close(struct file *filp)
+{
+ if (!filp)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(filp->f_path.dentry))
+ return 0;
+
+ if (pft_is_inplace_file(filp)) {
+ pr_debug("pid [%u] uid [%u] is closing in-place-encryption file %s\n",
+ current_pid(), __kuid_val(current_uid()),
+ file_to_filename(filp));
+ pft_dev->inplace_file = NULL;
+ }
+
+ switch (pft_dev->state) {
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ /*
+ * Do not allow apps to close file when
+ * pft_close_opened_enc_files() is closing files.
+ * Normally, all enterprise apps are closed by PFM
+ * before getting to this state, so the apps files are
+ * norammly closed by now.
+ * pft_close_opened_enc_files() is running in PFM context.
+ */
+ if (current_pid() != pft_dev->pfm_pid)
+ return -EACCES;
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_LOADED:
+ case PFT_STATE_KEY_REMOVED:
+ break;
+ default:
+ BUG(); /* State is set by "set state" command */
+ break;
+ }
+
+ pft_sync_file(filp);
+ pft_remove_file(filp);
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_file_close);
+
+/**
+ * pft_inode_unlink() - Delete file hook.
+ * @dir: directory inode pointer
+ * @dentry: file dentry pointer
+ *
+ * call path: vfs_unlink()->security_inode_unlink().
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+int pft_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+
+ if (!dir || !dentry || !dentry->d_inode)
+ return 0;
+
+ if (!pft_is_ready())
+ return 0;
+
+ inode = dentry->d_inode;
+
+ /* do nothing for non-encrypted files */
+ if (!pft_is_encrypted_file(dentry))
+ return 0;
+
+ if (pft_is_inplace_inode(inode)) {
+ pr_err("can't delete file %s by uid [%d] pid [%d]\n",
+ inode_to_filename(inode),
+ __kuid_val(current_uid()),
+ current_pid());
+ return -EBUSY;
+ }
+
+ if (!pft_is_current_process_registered()) {
+ pr_err("app (uid %u pid %u) is trying to access file %s\n",
+ __kuid_val(current_uid()),
+ current_pid(),
+ inode_to_filename(inode));
+ return -EACCES;
+ }
+ pr_debug("delete file %s\n", inode_to_filename(inode));
+
+ return 0;
+}
+EXPORT_SYMBOL(pft_inode_unlink);
+
+/**
+ * pft_inode_set_xattr() - set/remove xattr callback.
+ * @dentry: file dentry pointer
+ * @name: xattr name.
+ *
+ * This hook checks attempt to set/remove PFE xattr.
+ * Only this kernel driver allows to set the PFE xattr, so block
+ * any attempt to do it from user space. Allow access for other
+ * xattr.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+
+int pft_inode_set_xattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode *inode = NULL;
+
+ if (!dentry || !dentry->d_inode)
+ return 0;
+
+ inode = dentry->d_inode;
+
+ if (strcmp(name, XATTR_NAME_PFE) != 0) {
+ pr_debug("xattr name=%s file %s\n", name,
+ inode_to_filename(inode));
+ return 0; /* Not PFE xattr so it is ok */
+ }
+
+ pr_err("Attemp to set/remove PFE xattr for file %s\n",
+ inode_to_filename(inode));
+
+ /* Only PFT kernel driver allows to set the PFE xattr */
+ return -EACCES;
+}
+EXPORT_SYMBOL(pft_inode_set_xattr);
+
+/**
+ * pft_close_opened_enc_files() - Close all the currently open
+ * encrypted files
+ *
+ * Close all open encrypted file when removing key or
+ * deactivating.
+ */
+static void pft_close_opened_enc_files(void)
+{
+ struct pft_file_info *tmp = NULL;
+ struct list_head *pos = NULL;
+ struct list_head *next = NULL;
+
+ list_for_each_safe(pos, next, &pft_dev->open_file_list) {
+ struct file *filp;
+
+ tmp = list_entry(pos, struct pft_file_info, list);
+ filp = tmp->file;
+ pr_debug("closing file %s.\n", file_to_filename(filp));
+ /* filp_close() eventually calls pft_file_close() */
+ filp_close(filp, NULL);
+ }
+}
+
+/**
+ * pft_set_state() - Handle "Set State" command
+ * @command: command buffer.
+ * @size: size of command buffer.
+ *
+ * The command execution status is reported by the response.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int pft_set_state(struct pft_command *command, int size)
+{
+ u32 state = command->set_state.state;
+ int expected_size = sizeof(command->opcode) +
+ sizeof(command->set_state);
+
+ if (size != expected_size) {
+ pr_err("Invalid buffer size\n");
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ if (state >= PFT_STATE_MAX_INDEX) {
+ pr_err("Invalid state %d\n", command->set_state.state);
+ pft_set_response(PFT_CMD_RESP_INVALID_STATE);
+ return 0;
+ }
+
+ pr_debug("Set State %d [%s].\n", state, pft_state_name[state]);
+
+ switch (command->set_state.state) {
+ case PFT_STATE_DEACTIVATING:
+ case PFT_STATE_REMOVING_KEY:
+ pft_close_opened_enc_files();
+ /* Fall through */
+ case PFT_STATE_DEACTIVATED:
+ case PFT_STATE_KEY_LOADED:
+ case PFT_STATE_KEY_REMOVED:
+ pft_dev->state = command->set_state.state;
+ pft_set_response(PFT_CMD_RESP_SUCCESS);
+ break;
+ default:
+ pr_err("Invalid state %d\n", command->set_state.state);
+ pft_set_response(PFT_CMD_RESP_INVALID_STATE);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * pft_get_process_open_file() - get file pointer using file
+ * descriptor index.
+ * @index: file descriptor index.
+ *
+ * Return: file pointer on success, NULL on failure.
+ */
+static struct file *pft_get_process_open_file(int index)
+{
+ struct fdtable *files_table;
+
+ files_table = files_fdtable(current->files);
+ if (files_table == NULL)
+ return NULL;
+
+ if (index >= files_table->max_fds)
+ return NULL;
+ else
+ return files_table->fd[index];
+}
+
+/**
+ * pft_set_inplace_file() - handle "inplace file encryption"
+ * command.
+ * @command: command buffer.
+ * @size: size of command buffer.
+ *
+ * The command execution status is reported by the response.
+ *
+ * Return: 0 if command is valid, negative value otherwise.
+ */
+static int pft_set_inplace_file(struct pft_command *command, int size)
+{
+ int expected_size;
+ u32 fd;
+ int rc;
+ struct file *filp = NULL;
+ struct inode *inode = NULL;
+ int writecount;
+
+ expected_size = sizeof(command->opcode) +
+ sizeof(command->preform_in_place_file_enc.file_descriptor);
+
+ if (size != expected_size) {
+ pr_err("invalid command size %d expected %d.\n",
+ size, expected_size);
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ if (pft_dev->state != (u32) PFT_STATE_KEY_LOADED) {
+ pr_err("Key not loaded, state [%d], In-place-encryption is not allowed.\n",
+ pft_dev->state);
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+ /* allow only one in-place file encryption at a time */
+ if (pft_dev->inplace_file != NULL) {
+ pr_err("file %s in-place-encryption in progress.\n",
+ file_to_filename(pft_dev->inplace_file));
+ /* @todo - use new error code */
+ pft_set_response(PFT_CMD_RESP_INPLACE_FILE_IS_OPEN);
+ return 0;
+ }
+
+ fd = command->preform_in_place_file_enc.file_descriptor;
+ filp = pft_get_process_open_file(fd);
+
+ if (filp == NULL) {
+ pr_err("failed to find file by fd %d.\n", fd);
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+ /* Verify the file is not already open by other than PFM */
+ if (!filp->f_path.dentry || !filp->f_path.dentry->d_inode) {
+ pr_err("failed to get inode of inplace-file.\n");
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+ inode = filp->f_path.dentry->d_inode;
+ writecount = atomic_read(&inode->i_writecount);
+ if (writecount > 1) {
+ pr_err("file %s is opened %d times for write.\n",
+ file_to_filename(filp), writecount);
+ pft_set_response(PFT_CMD_RESP_INPLACE_FILE_IS_OPEN);
+ return 0;
+ }
+
+ /*
+ * Check if the file was already encryprted.
+ * In practice, it is unlikely to happen,
+ * because PFM is not an enterprise application
+ * it won't be able to open encrypted file.
+ */
+ if (pft_is_encrypted_file(filp->f_path.dentry)) {
+ pr_err("file %s is already encrypted.\n",
+ file_to_filename(filp));
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ return 0;
+ }
+
+
+ /* Update the current in-place-encryption file */
+ pft_dev->inplace_file = filp;
+
+ /*
+ * Now, any new access to this file is allowed only to PFM.
+ * Lets make sure that all pending writes are completed
+ * before encrypting the file.
+ */
+ pft_sync_file(filp);
+
+ rc = pft_tag_file(pft_dev->inplace_file->f_path.dentry,
+ pft_get_app_key_index(current_uid()));
+
+ if (!rc) {
+ pr_debug("tagged file %s to be encrypted.\n",
+ file_to_filename(pft_dev->inplace_file));
+ pft_set_response(PFT_CMD_RESP_SUCCESS);
+ } else {
+ pr_err("failed to tag file %s for encryption.\n",
+ file_to_filename(pft_dev->inplace_file));
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ }
+
+ return 0;
+}
+
+/**
+ * pft_update_reg_apps() - Update the registered application
+ * list.
+ * @command: command buffer.
+ * @size: size of command buffer.
+ *
+ * The command execution status is reported by the response.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int pft_update_reg_apps(struct pft_command *command, int size)
+{
+ int i;
+ int expected_size;
+ void *buf;
+ int buf_size;
+ u32 items_count = command->update_app_list.items_count;
+
+ if (items_count > PFT_MAX_APPS) {
+ pr_err("Number of apps [%d] > max apps [%d]\n",
+ items_count , PFT_MAX_APPS);
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ expected_size =
+ sizeof(command->opcode) +
+ sizeof(command->update_app_list.items_count) +
+ (command->update_app_list.items_count * sizeof(u32));
+
+ if (size != expected_size) {
+ pr_err("invalid command size %d expected %d.\n",
+ size, expected_size);
+ pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
+ return -EINVAL;
+ }
+
+ mutex_lock(&pft_dev->lock);
+
+ /* Free old table */
+ kfree(pft_dev->uid_table);
+ pft_dev->uid_table = NULL;
+ pft_dev->uid_count = 0;
+
+ if (items_count == 0) {
+ pr_info("empty app list - clear list.\n");
+ mutex_unlock(&pft_dev->lock);
+ return 0;
+ }
+
+ buf_size = command->update_app_list.items_count * sizeof(u32);
+ buf = kzalloc(buf_size, GFP_KERNEL);
+
+ if (!buf) {
+ pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
+ mutex_unlock(&pft_dev->lock);
+ return 0;
+ }
+
+ pft_dev->uid_table = buf;
+ pft_dev->uid_count = command->update_app_list.items_count;
+ pr_debug("uid_count = %d\n", pft_dev->uid_count);
+ for (i = 0; i < pft_dev->uid_count; i++)
+ pft_dev->uid_table[i] =
+ KUIDT_INIT(command->update_app_list.table[i]);
+ pft_set_response(PFT_CMD_RESP_SUCCESS);
+ mutex_unlock(&pft_dev->lock);
+
+ return 0;
+}
+
+/**
+ * pft_handle_command() - Handle user space app commands.
+ * @buf: command buffer.
+ * @buf_size: command buffer size.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int pft_handle_command(void *buf, int buf_size)
+{
+ int ret = 0;
+ struct pft_command *command = NULL;
+
+ /* opcode field is the minimum length of command */
+ if (buf_size < sizeof(command->opcode)) {
+ pr_err("Invalid argument used buffer size\n");
+ return -EINVAL;
+ }
+
+ command = (struct pft_command *)buf;
+
+ pft_dev->response.command_id = command->opcode;
+
+ switch (command->opcode) {
+ case PFT_CMD_OPCODE_SET_STATE:
+ ret = pft_set_state(command, buf_size);
+ break;
+ case PFT_CMD_OPCODE_UPDATE_REG_APP_UID:
+ ret = pft_update_reg_apps(command, buf_size);
+ break;
+ case PFT_CMD_OPCODE_PERFORM_IN_PLACE_FILE_ENC:
+ ret = pft_set_inplace_file(command, buf_size);
+ break;
+ default:
+ pr_err("Invalid command_op_code %u\n", command->opcode);
+ pft_set_response(PFT_CMD_RESP_INVALID_COMMAND);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int pft_device_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ mutex_lock(&pft_dev->lock);
+ if (pft_dev->open_count > 0) {
+ pr_err("PFT device is already opened (%d)\n",
+ pft_dev->open_count);
+ ret = -EBUSY;
+ } else {
+ pft_dev->open_count++;
+ pft_dev->pfm_pid = current_pid();
+ pr_debug("PFT device opened by %d (%d)\n",
+ pft_dev->pfm_pid, pft_dev->open_count);
+ ret = 0;
+ }
+ mutex_unlock(&pft_dev->lock);
+
+ pr_debug("device opened, count %d\n", pft_dev->open_count);
+
+ return ret;
+}
+
+static int pft_device_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&pft_dev->lock);
+ if (pft_dev->open_count > 0)
+ pft_dev->open_count--;
+ pft_dev->pfm_pid = UINT_MAX;
+ mutex_unlock(&pft_dev->lock);
+
+ pr_debug("device released, count %d\n", pft_dev->open_count);
+
+ return 0;
+}
+
+/**
+ * pft_device_write() - Get commands from user sapce.
+ *
+ * Return: number of bytes to write on success to get the
+ * command buffer, negative value on failure.
+ * The error code for handling the command should be retrieve by
+ * reading the response.
+ * Note: any reurn value of 0..size-1 will cause retry by the
+ * OS, so avoid it.
+ */
+static ssize_t pft_device_write(struct file *filp, const char __user *user_buff,
+ size_t size, loff_t *f_pos)
+{
+ int ret;
+ char *cmd_buf;
+
+ if (size > PFT_MAX_COMMAND_SIZE || !user_buff || !f_pos) {
+ pr_err("inavlid parameters.\n");
+ return -EINVAL;
+ }
+
+ cmd_buf = kzalloc(size, GFP_KERNEL);
+ if (cmd_buf == NULL)
+ return -ENOMEM;
+
+ ret = copy_from_user(cmd_buf, user_buff, size);
+ if (ret) {
+ pr_err("Unable to copy from user (err %d)\n", ret);
+ kfree(cmd_buf);
+ return -EFAULT;
+ }
+
+ ret = pft_handle_command(cmd_buf, size);
+ if (ret) {
+ kfree(cmd_buf);
+ return -EFAULT;
+ }
+
+ kfree(cmd_buf);
+
+ return size;
+}
+
+/**
+ * pft_device_read() - return response of last command.
+ *
+ * Return: number of bytes to read on success, negative value on
+ * failure.
+ */
+static ssize_t pft_device_read(struct file *filp, char __user *buffer,
+ size_t length, loff_t *f_pos)
+{
+ int ret = 0;
+
+ if (!buffer || !f_pos || length < sizeof(pft_dev->response)) {
+ pr_err("inavlid parameters.\n");
+ return -EFAULT;
+ }
+
+ ret = copy_to_user(buffer, &(pft_dev->response),
+ sizeof(pft_dev->response));
+ if (ret) {
+ pr_err("Unable to copy to user, err = %d.\n", ret);
+ return -EINVAL;
+ }
+
+ return sizeof(pft_dev->response);
+}
+
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .read = pft_device_read,
+ .write = pft_device_write,
+ .open = pft_device_open,
+ .release = pft_device_release,
+};
+
+static int __init pft_register_chardev(void)
+{
+ int rc;
+ unsigned baseminor = 0;
+ unsigned count = 1;
+ struct device *class_dev;
+
+ rc = alloc_chrdev_region(&pft_dev->device_no, baseminor, count,
+ DEVICE_NAME);
+ if (rc < 0) {
+ pr_err("alloc_chrdev_region failed %d\n", rc);
+ return rc;
+ }
+
+ pft_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(pft_dev->driver_class)) {
+ rc = -ENOMEM;
+ pr_err("class_create failed %d\n", rc);
+ goto exit_unreg_chrdev_region;
+ }
+
+ class_dev = device_create(pft_dev->driver_class, NULL,
+ pft_dev->device_no, NULL,
+ DEVICE_NAME);
+ if (!class_dev) {
+ pr_err("class_device_create failed %d\n", rc);
+ rc = -ENOMEM;
+ goto exit_destroy_class;
+ }
+
+ cdev_init(&pft_dev->cdev, &fops);
+ pft_dev->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&pft_dev->cdev, MKDEV(MAJOR(pft_dev->device_no), 0), 1);
+ if (rc < 0) {
+ pr_err("cdev_add failed %d\n", rc);
+ goto exit_destroy_device;
+ }
+
+ return 0;
+
+exit_destroy_device:
+ device_destroy(pft_dev->driver_class, pft_dev->device_no);
+exit_destroy_class:
+ class_destroy(pft_dev->driver_class);
+exit_unreg_chrdev_region:
+ unregister_chrdev_region(pft_dev->device_no, 1);
+ return rc;
+}
+
+static void __exit pft_unregister_chrdev(void)
+{
+ cdev_del(&pft_dev->cdev);
+ device_destroy(pft_dev->driver_class, pft_dev->device_no);
+ class_destroy(pft_dev->driver_class);
+ unregister_chrdev_region(pft_dev->device_no, 1);
+
+}
+
+static void __exit pft_free_open_files_list(void)
+{
+ struct pft_file_info *tmp = NULL;
+ struct list_head *pos = NULL;
+ struct list_head *next = NULL;
+
+ mutex_lock(&pft_dev->lock);
+ list_for_each_safe(pos, next, &pft_dev->open_file_list) {
+ tmp = list_entry(pos, struct pft_file_info, list);
+ list_del(&tmp->list);
+ kfree(tmp);
+ }
+ mutex_unlock(&pft_dev->lock);
+}
+
+static void __exit pft_exit(void)
+{
+ if (pft_dev == NULL)
+ return;
+
+ pft_unregister_chrdev();
+ pft_free_open_files_list();
+
+ kfree(pft_dev->uid_table);
+ kfree(pft_dev);
+ pft_dev = NULL;
+}
+
+static int __init pft_init(void)
+{
+ int ret;
+ struct pft_device *dev = NULL;
+
+ dev = kzalloc(sizeof(struct pft_device), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+
+ pft_dev = dev;
+
+ dev->state = PFT_STATE_DEACTIVATED;
+ dev->pfm_pid = UINT_MAX;
+
+ INIT_LIST_HEAD(&dev->open_file_list);
+ mutex_init(&dev->lock);
+
+ ret = pft_register_chardev();
+ if (ret) {
+ pr_err("create character device failed.\n");
+ goto fail;
+ }
+
+ pft_lsm_init(dev);
+
+ return 0;
+
+fail:
+ pr_err("Failed to init driver.\n");
+ kfree(dev);
+ pft_dev = NULL;
+
+ return -ENODEV;
+}
+
+module_init(pft_init);
+module_exit(pft_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Per-File-Tagger driver");
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 602e8441bc0f..cfcf7db05812 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -399,6 +399,7 @@ static inline void dio_bio_submit(struct dio *dio, struct dio_submit *sdio)
if (dio->is_async && dio->rw == READ && dio->should_dirty)
bio_set_pages_dirty(bio);
+ bio->bi_dio_inode = dio->inode;
dio->bio_bdev = bio->bi_bdev;
if (sdio->submit_io) {
@@ -413,6 +414,19 @@ static inline void dio_bio_submit(struct dio *dio, struct dio_submit *sdio)
sdio->logical_offset_in_bio = 0;
}
+struct inode *dio_bio_get_inode(struct bio *bio)
+{
+ struct inode *inode = NULL;
+
+ if (bio == NULL)
+ return NULL;
+
+ inode = bio->bi_dio_inode;
+
+ return inode;
+}
+EXPORT_SYMBOL(dio_bio_get_inode);
+
/*
* Release any resources in case of a failure
*/
diff --git a/fs/namei.c b/fs/namei.c
index 0c3974cd3ecd..5fd8a57d132b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2657,8 +2657,14 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
if (error)
return error;
error = dir->i_op->create(dir, dentry, mode, want_excl);
+ if (error)
+ return error;
+ error = security_inode_post_create(dir, dentry, mode);
+ if (error)
+ return error;
if (!error)
fsnotify_create(dir, dentry);
+
return error;
}
EXPORT_SYMBOL(vfs_create);
@@ -3510,8 +3516,16 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
return error;
error = dir->i_op->mknod(dir, dentry, mode, dev);
+ if (error)
+ return error;
+
+ error = security_inode_post_create(dir, dentry, mode);
+ if (error)
+ return error;
+
if (!error)
fsnotify_create(dir, dentry);
+
return error;
}
EXPORT_SYMBOL(vfs_mknod);
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index d32400f5402b..e897f4d3810c 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -88,6 +88,13 @@ struct bio {
unsigned short bi_vcnt; /* how many bio_vec's */
/*
+ * When using dircet-io (O_DIRECT), we can't get the inode from a bio
+ * by walking bio->bi_io_vec->bv_page->mapping->host
+ * since the page is anon.
+ */
+ struct inode *bi_dio_inode;
+
+ /*
* Everything starting with bi_max_vecs will be preserved by bio_reset()
*/
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 605454ccaa0e..522b582e61c7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2734,6 +2734,8 @@ static inline void inode_dio_end(struct inode *inode)
wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
}
+struct inode *dio_bio_get_inode(struct bio *bio);
+
extern void inode_set_flags(struct inode *inode, unsigned int flags,
unsigned int mask);
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index ec3a6bab29de..1919b06f28f4 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1384,6 +1384,8 @@ union security_list_options {
size_t *len);
int (*inode_create)(struct inode *dir, struct dentry *dentry,
umode_t mode);
+ int (*inode_post_create)(struct inode *dir, struct dentry *dentry,
+ umode_t mode);
int (*inode_link)(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry);
int (*inode_unlink)(struct inode *dir, struct dentry *dentry);
@@ -1440,6 +1442,8 @@ union security_list_options {
struct fown_struct *fown, int sig);
int (*file_receive)(struct file *file);
int (*file_open)(struct file *file, const struct cred *cred);
+ int (*file_close)(struct file *file);
+ bool (*allow_merge_bio)(struct bio *bio1, struct bio *bio2);
int (*task_create)(unsigned long clone_flags);
void (*task_free)(struct task_struct *task);
@@ -1666,6 +1670,7 @@ struct security_hook_heads {
struct list_head inode_free_security;
struct list_head inode_init_security;
struct list_head inode_create;
+ struct list_head inode_post_create;
struct list_head inode_link;
struct list_head inode_unlink;
struct list_head inode_symlink;
@@ -1702,6 +1707,8 @@ struct security_hook_heads {
struct list_head file_send_sigiotask;
struct list_head file_receive;
struct list_head file_open;
+ struct list_head file_close;
+ struct list_head allow_merge_bio;
struct list_head task_create;
struct list_head task_free;
struct list_head cred_alloc_blank;
diff --git a/include/linux/pft.h b/include/linux/pft.h
new file mode 100644
index 000000000000..f2173b89a2a0
--- /dev/null
+++ b/include/linux/pft.h
@@ -0,0 +1,96 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PFT_H_
+#define PFT_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/bio.h>
+
+#ifdef CONFIG_PFT
+
+/* dm-req-crypt API */
+int pft_get_key_index(struct bio *bio, u32 *key_index,
+ bool *is_encrypted, bool *is_inplace);
+
+/* block layer API */
+bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2);
+
+/* --- security hooks , called from selinux --- */
+int pft_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
+
+int pft_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode);
+
+int pft_file_open(struct file *filp, const struct cred *cred);
+
+int pft_file_permission(struct file *file, int mask);
+
+int pft_file_close(struct file *filp);
+
+int pft_inode_unlink(struct inode *dir, struct dentry *dentry);
+
+int pft_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ dev_t dev);
+
+int pft_inode_rename(struct inode *inode, struct dentry *dentry,
+ struct inode *new_inode, struct dentry *new_dentry);
+
+int pft_inode_set_xattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags);
+
+
+#else
+static inline int pft_get_key_index(struct bio *bio, u32 *key_index,
+ bool *is_encrypted, bool *is_inplace)
+{ return -ENODEV; }
+
+static inline bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+{ return true; }
+
+static inline int pft_file_permission(struct file *file, int mask)
+{ return 0; }
+
+static inline int pft_inode_create(
+ struct inode *dir, struct dentry *dentry, umode_t mode)
+{ return 0; }
+
+static inline int pft_inode_post_create(
+ struct inode *dir, struct dentry *dentry, umode_t mode)
+{ return 0; }
+
+static inline int pft_file_open(struct file *filp, const struct cred *cred)
+{ return 0; }
+
+static inline int pft_file_close(struct file *filp)
+{ return 0; }
+
+static inline int pft_inode_unlink(struct inode *dir, struct dentry *dentry)
+{ return 0; }
+
+static inline int pft_inode_mknod(struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+{ return 0; }
+
+static inline int pft_inode_rename(struct inode *inode, struct dentry *dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{ return 0; }
+
+static inline int pft_inode_set_xattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size,
+ int flags)
+{ return 0; }
+
+#endif /* CONFIG_PFT */
+
+#endif /* PFT_H */
diff --git a/include/linux/security.h b/include/linux/security.h
index 2f4c1f7aa7db..3de0302aecf2 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -28,6 +28,7 @@
#include <linux/err.h>
#include <linux/string.h>
#include <linux/mm.h>
+#include <linux/bio.h>
struct linux_binprm;
struct cred;
@@ -244,6 +245,7 @@ int security_old_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, const char **name,
void **value, size_t *len);
int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
+int security_inode_post_create(struct inode *dir, struct dentry *dentry, umode_t mode);
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry);
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
@@ -290,6 +292,8 @@ int security_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int sig);
int security_file_receive(struct file *file);
int security_file_open(struct file *file, const struct cred *cred);
+int security_file_close(struct file *file);
+bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2);
int security_task_create(unsigned long clone_flags);
void security_task_free(struct task_struct *task);
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
@@ -605,6 +609,13 @@ static inline int security_inode_create(struct inode *dir,
return 0;
}
+static inline int security_inode_post_create(struct inode *dir,
+ struct dentry *dentry,
+ umode_t mode)
+{
+ return 0;
+}
+
static inline int security_inode_link(struct dentry *old_dentry,
struct inode *dir,
struct dentry *new_dentry)
@@ -810,6 +821,16 @@ static inline int security_file_open(struct file *file,
return 0;
}
+static inline int security_file_close(struct file *file)
+{
+ return 0;
+}
+
+static inline int security_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+{
+ return true;
+}
+
static inline int security_task_create(unsigned long clone_flags)
{
return 0;
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 4c0fd6369a76..16b7e327818d 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -302,6 +302,7 @@ header-y += msm_audio_alac.h
header-y += msm_audio_ape.h
header-y += msm_ion.h
header-y += msm_kgsl.h
+header-y += msm_pft.h
header-y += msm_mdp.h
header-y += msm_mdp_ext.h
header-y += msm_rmnet.h
diff --git a/include/uapi/linux/msm_pft.h b/include/uapi/linux/msm_pft.h
new file mode 100644
index 000000000000..1127337eb5de
--- /dev/null
+++ b/include/uapi/linux/msm_pft.h
@@ -0,0 +1,134 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MSM_PFT_H_
+#define MSM_PFT_H_
+
+#include <linux/types.h>
+
+/**
+ * enum pft_command_opcode - PFT driver command ID
+ *
+ * @PFT_CMD_OPCODE_SET_STATE -
+ * command ID to set PFT driver state
+ * @PFT_CMD_OPCODE_UPDATE_REG_APP_UID -
+ * command ID to update the list of registered application
+ * UID
+ * @PFT_CMD_OPCODE_PERFORM_IN_PLACE_FILE_ENC -
+ * command ID to perfrom in-place file encryption
+ */
+enum pft_command_opcode {
+ PFT_CMD_OPCODE_SET_STATE,
+ PFT_CMD_OPCODE_UPDATE_REG_APP_UID,
+ PFT_CMD_OPCODE_PERFORM_IN_PLACE_FILE_ENC,
+ /* */
+ PFT_CMD_OPCODE_MAX_COMMAND_INDEX
+};
+
+/**
+ * enum pft_state - PFT driver operational states
+ *
+ * @PFT_STATE_DEACTIVATED - driver is deativated.
+ * @PFT_STATE_DEACTIVATING - driver is in the process of being deativated.
+ * @PFT_STATE_KEY_REMOVED - driver is active but no encryption key is loaded.
+ * @PFT_STATE_REMOVING_KEY - driver is active, but the encryption key is being
+ * removed.
+ * @PFT_STATE_KEY_LOADED - driver is active, and the encryption key is loaded
+ * to encryption block, hence registered apps can perform file operations
+ * on encrypted files.
+ */
+enum pft_state {
+ PFT_STATE_DEACTIVATED,
+ PFT_STATE_DEACTIVATING,
+ PFT_STATE_KEY_REMOVED,
+ PFT_STATE_REMOVING_KEY,
+ PFT_STATE_KEY_LOADED,
+ /* Internal */
+ PFT_STATE_MAX_INDEX
+};
+
+/**
+ * enum pft_command_response_code - PFT response on the previous
+ * command
+ *
+ * @PFT_CMD_RESP_SUCCESS - The command was properly processed
+ * without an error.
+ * @PFT_CMD_RESP_GENERAL_ERROR -
+ * Indicates an error that cannot be better described by a
+ * more specific errors below.
+ * @PFT_CMD_RESP_INVALID_COMMAND - Invalid or unsupported
+ * command id.
+ * @PFT_CMD_RESP_INVALID_CMD_PARAMS - Invalid command
+ * parameters.
+ * @PFT_CMD_RESP_INVALID_STATE - Invalid state
+ * @PFT_CMD_RESP_ALREADY_IN_STATE - Used to indicates that
+ * the new state is equal to the existing one.
+ * @PFT_CMD_RESP_INPLACE_FILE_IS_OPEN - Used to indicates
+ * that the file that should be encrypted is already open
+ * and can be encrypted.
+ * @PFT_CMD_RESP_ENT_FILES_CLOSING_FAILURE
+ * Indicates about failure of the PFT to close Enterprise files
+ * @PFT_CMD_RESP_MAX_INDEX
+ */
+enum pft_command_response_code {
+ PFT_CMD_RESP_SUCCESS,
+ PFT_CMD_RESP_GENERAL_ERROR,
+ PFT_CMD_RESP_INVALID_COMMAND,
+ PFT_CMD_RESP_INVALID_CMD_PARAMS,
+ PFT_CMD_RESP_INVALID_STATE,
+ PFT_CMD_RESP_ALREADY_IN_STATE,
+ PFT_CMD_RESP_INPLACE_FILE_IS_OPEN,
+ PFT_CMD_RESP_ENT_FILES_CLOSING_FAILURE,
+ /* Internal */
+ PFT_CMD_RESP_MAX_INDEX
+};
+
+/**
+ * struct pft_command_response - response structure
+ *
+ * @command_id - see enum pft_command_response_code
+ * @error_codee - see enum pft_command_response_code
+ */
+struct pft_command_response {
+ __u32 command_id;
+ __u32 error_code;
+};
+
+/**
+ * struct pft_command - pft command
+ *
+ * @opcode - see enum pft_command_opcode.
+ * @set_state.state - see enum pft_state.
+ * @update_app_list.count - number of items in the
+ * registered applications list.
+ * @update_app_list.table - registered applications array
+ * @preform_in_place_file_enc.file_descriptor - file descriptor
+ * of the opened file to be in-placed encrypted.
+ */
+struct pft_command {
+ __u32 opcode;
+ union {
+ struct {
+ /* @see pft_state */
+ __u32 state;
+ } set_state;
+ struct {
+ __u32 items_count; /* number of items */
+ uid_t table[0]; /* array of UIDs */
+ } update_app_list;
+ struct {
+ __u32 file_descriptor;
+ } preform_in_place_file_enc;
+ };
+};
+
+#endif /* MSM_PFT_H_ */
diff --git a/security/security.c b/security/security.c
index 46f405ce6b0f..81a555c14a35 100644
--- a/security/security.c
+++ b/security/security.c
@@ -513,6 +513,14 @@ int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode
}
EXPORT_SYMBOL_GPL(security_inode_create);
+int security_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+ return call_int_hook(inode_post_create, 0, dir, dentry, mode);
+}
+
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry)
{
@@ -844,6 +852,16 @@ int security_file_open(struct file *file, const struct cred *cred)
return fsnotify_perm(file, MAY_OPEN);
}
+int security_file_close(struct file *file)
+{
+ return call_int_hook(file_close, 0, file);
+}
+
+bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+{
+ return call_int_hook(allow_merge_bio, 1, bio1, bio2);
+}
+
int security_task_create(unsigned long clone_flags)
{
return call_int_hook(task_create, 0, clone_flags);
@@ -1614,6 +1632,7 @@ struct security_hook_heads security_hook_heads = {
.inode_init_security =
LIST_HEAD_INIT(security_hook_heads.inode_init_security),
.inode_create = LIST_HEAD_INIT(security_hook_heads.inode_create),
+ .inode_post_create = LIST_HEAD_INIT(security_hook_heads.inode_post_create),
.inode_link = LIST_HEAD_INIT(security_hook_heads.inode_link),
.inode_unlink = LIST_HEAD_INIT(security_hook_heads.inode_unlink),
.inode_symlink =
@@ -1673,6 +1692,8 @@ struct security_hook_heads security_hook_heads = {
LIST_HEAD_INIT(security_hook_heads.file_send_sigiotask),
.file_receive = LIST_HEAD_INIT(security_hook_heads.file_receive),
.file_open = LIST_HEAD_INIT(security_hook_heads.file_open),
+ .file_close = LIST_HEAD_INIT(security_hook_heads.file_close),
+ .allow_merge_bio = LIST_HEAD_INIT(security_hook_heads.allow_merge_bio),
.task_create = LIST_HEAD_INIT(security_hook_heads.task_create),
.task_free = LIST_HEAD_INIT(security_hook_heads.task_free),
.cred_alloc_blank =
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 7c22a15c7e4b..a00bb5f9fcde 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -83,6 +83,7 @@
#include <linux/export.h>
#include <linux/msg.h>
#include <linux/shm.h>
+#include <linux/pft.h>
#include "avc.h"
#include "objsec.h"
@@ -1777,9 +1778,15 @@ static int may_create(struct inode *dir,
if (rc)
return rc;
- return avc_has_perm(newsid, sbsec->sid,
+ rc = avc_has_perm(newsid, sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE, &ad);
+ if (rc)
+ return rc;
+
+ rc = pft_inode_mknod(dir, dentry, 0, 0);
+
+ return rc;
}
/* Check whether a task can create a key. */
@@ -1836,6 +1843,12 @@ static int may_link(struct inode *dir,
}
rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad);
+ if (rc)
+ return rc;
+
+ if (kind == MAY_UNLINK)
+ rc = pft_inode_unlink(dir, dentry);
+
return rc;
}
@@ -2806,9 +2819,21 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
{
+ int ret;
+
+ ret = pft_inode_create(dir, dentry, mode);
+ if (ret < 0)
+ return ret;
+
return may_create(dir, dentry, SECCLASS_FILE);
}
+static int selinux_inode_post_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+{
+ return pft_inode_post_create(dir, dentry, mode);
+}
+
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
return may_link(dir, old_dentry, MAY_LINK);
@@ -2842,6 +2867,12 @@ static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t
static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry)
{
+ int rc;
+
+ rc = pft_inode_rename(old_inode, old_dentry, new_inode, new_dentry);
+ if (rc)
+ return rc;
+
return may_rename(old_inode, old_dentry, new_inode, new_dentry);
}
@@ -2966,6 +2997,9 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
{
const struct cred *cred = current_cred();
+ if (pft_inode_set_xattr(dentry, name, NULL, 0, 0) < 0)
+ return -EACCES;
+
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof XATTR_SECURITY_PREFIX - 1)) {
if (!strcmp(name, XATTR_NAME_CAPS)) {
@@ -3216,11 +3250,16 @@ static int selinux_file_permission(struct file *file, int mask)
struct file_security_struct *fsec = file->f_security;
struct inode_security_struct *isec = inode->i_security;
u32 sid = current_sid();
+ int ret;
if (!mask)
/* No permission to check. Existence test. */
return 0;
+ ret = pft_file_permission(file, mask);
+ if (ret < 0)
+ return ret;
+
if (sid == fsec->sid && fsec->isid == isec->sid &&
fsec->pseqno == avc_policy_seqno())
/* No change since file_open check. */
@@ -3511,6 +3550,11 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
{
struct file_security_struct *fsec;
struct inode_security_struct *isec;
+ int ret;
+
+ ret = pft_file_open(file, cred);
+ if (ret < 0)
+ return ret;
fsec = file->f_security;
isec = file_inode(file)->i_security;
@@ -3534,6 +3578,16 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
return file_path_has_perm(cred, file, open_file_to_av(file));
}
+static int selinux_file_close(struct file *file)
+{
+ return pft_file_close(file);
+}
+
+static bool selinux_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+{
+ return pft_allow_merge_bio(bio1, bio2);
+}
+
/* task security operations */
static int selinux_task_create(unsigned long clone_flags)
@@ -5905,6 +5959,7 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(inode_free_security, selinux_inode_free_security),
LSM_HOOK_INIT(inode_init_security, selinux_inode_init_security),
LSM_HOOK_INIT(inode_create, selinux_inode_create),
+ LSM_HOOK_INIT(inode_post_create, selinux_inode_post_create),
LSM_HOOK_INIT(inode_link, selinux_inode_link),
LSM_HOOK_INIT(inode_unlink, selinux_inode_unlink),
LSM_HOOK_INIT(inode_symlink, selinux_inode_symlink),
@@ -5941,6 +5996,8 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(file_receive, selinux_file_receive),
LSM_HOOK_INIT(file_open, selinux_file_open),
+ LSM_HOOK_INIT(file_close, selinux_file_close),
+ LSM_HOOK_INIT(allow_merge_bio, selinux_allow_merge_bio),
LSM_HOOK_INIT(task_create, selinux_task_create),
LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 81fa718d5cb3..a6204e701224 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -47,6 +47,7 @@ struct inode_security_struct {
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */
+ u32 tag; /* Per-File-Encryption tag */
struct mutex lock;
};