blob: 62dda8d95e68f04218c811c5ba9175752039969a [file] [log] [blame]
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
*
* Sensitive Data Protection
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <crypto/internal/hash.h>
#include <crypto/scatterwalk.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/wakelock.h>
#include <linux/sched.h>
#include <linux/netlink.h>
#include <linux/net.h>
#include <net/netlink.h>
#include <net/sock.h>
#include <net/net_namespace.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/audit.h>
#include <linux/jiffies.h>
#include <linux/version.h>
#include <sdp/fs_handler.h>
#include <sdp/fs_request.h>
#define RESULT_ARRAY_MAX_LEN 100
#define CRYPTO_MAX_TIMEOUT HZ/5
#define SDP_FS_HANDLER_REQ_TIMEOUT 3000
sdp_fs_handler_control_t g_sdp_fs_handler_control;
DEFINE_MUTEX(g_send_mutex);
static int g_user_pid = 0;
static struct sock* g_sock = NULL;
static int to_netlink_msg(sdp_fs_handler_request_t *req, char **msg);
static void request_send(sdp_fs_handler_control_t *con,
sdp_fs_handler_request_t *req);
static sdp_fs_handler_request_t *request_find(sdp_fs_handler_control_t *con,
u32 request_id);
static sdp_fs_handler_request_t *request_alloc(u32 opcode);
static void request_free(sdp_fs_handler_request_t *req);
static void req_dump(sdp_fs_handler_request_t *req, const char *msg);
/* Debug */
#define SDP_FS_HANDLER_DEBUG 0
#if SDP_FS_HANDLER_DEBUG
#define SDP_FS_HANDLER_LOGD(FMT, ...) printk("SDP_FS_HANDLER[%d] : %s " FMT , current->pid, __func__, ##__VA_ARGS__)
#else
#define SDP_FS_HANDLER_LOGD(FMT, ...)
#endif /* SDP_FS_HANDLER_DEBUG */
#define SDP_FS_HANDLER_LOGE(FMT, ...) printk("SDP_FS_HANDLER[%d] : %s " FMT , current->pid, __func__, ##__VA_ARGS__)
static int __handle_request(sdp_fs_handler_request_t *req, char *ret) {
int rc = 0;
struct sk_buff *skb_in = NULL;
struct sk_buff *skb_out = NULL;
struct nlmsghdr *nlh = NULL;
char *nl_msg = NULL;
int nl_msg_size = 0;
SDP_FS_HANDLER_LOGD("====================== \t entred\n");
if(req == NULL) {
SDP_FS_HANDLER_LOGE("invalid request\n");
return -1;
}
request_send(&g_sdp_fs_handler_control, req);
nl_msg_size = to_netlink_msg(req, &nl_msg);
if(nl_msg_size <= 0) {
SDP_FS_HANDLER_LOGE("invalid opcode %d\n", req->opcode);
return -1;
}
// sending netlink message
skb_in = nlmsg_new(nl_msg_size, 0);
if (!skb_in) {
SDP_FS_HANDLER_LOGE("Failed to allocate new skb: \n");
return -1;
}
nlh = nlmsg_put(skb_in, 0, 0, NLMSG_DONE, nl_msg_size, 0);
NETLINK_CB(skb_in).dst_group = 0;
memcpy(nlmsg_data(nlh), nl_msg, nl_msg_size);
mutex_lock(&g_send_mutex);
rc = nlmsg_unicast(g_sock, skb_in, g_user_pid);
mutex_unlock(&g_send_mutex);
skb_out = skb_dequeue(&g_sock->sk_receive_queue);
if(skb_out) {
kfree_skb(skb_out);
}
return 0;
}
int sdp_fs_request(sdp_fs_command_t *cmd, fs_request_cb_t callback){
sdp_fs_handler_request_t *req = request_alloc(cmd->opcode);
int ret = -1;
req_dump(req, "request allocated");
if(req) {
memcpy(&req->command, cmd, sizeof(sdp_fs_command_t));
req->command.req_id = req->id;
req_dump(req, "__handle_reqeust start");
ret = __handle_request(req, NULL);
req_dump(req, "__handle_reqeust end");
if(ret != 0) {
SDP_FS_HANDLER_LOGE("opcode[%d] failed\n", cmd->opcode);
goto error;
}
} else {
SDP_FS_HANDLER_LOGE("request allocation failed\n");
return -ENOMEM;
}
return 0;
error:
request_free(req);
return -1;
}
static int __recver(struct sk_buff *skb, struct nlmsghdr *nlh)
{
void *data;
u16 msg_type = nlh->nlmsg_type;
u32 err = 0;
struct audit_status *status_get = NULL;
u16 len = 0;
data = NLMSG_DATA(nlh);
len = ntohs(*(uint16_t*) (data+1));
switch (msg_type) {
case SDP_FS_HANDLER_PID_SET:
status_get = (struct audit_status *)data;
g_user_pid = status_get->pid;
break;
case SDP_FS_HANDLER_RESULT:
{
result_t *result = (result_t *)data;
sdp_fs_handler_request_t *req = NULL;
printk("result : req_id[%d], opcode[%d] ret[%d]\n",
result->request_id, result->opcode, result->ret);
spin_lock(&g_sdp_fs_handler_control.lock);
req = request_find(&g_sdp_fs_handler_control, result->request_id);
spin_unlock(&g_sdp_fs_handler_control.lock);
if(req == NULL) {
SDP_FS_HANDLER_LOGE("crypto result :: error! can't find request %d\n",
result->request_id);
} else {
memcpy(&req->result, result, sizeof(result_t));
req->state = SDP_FS_HANDLER_REQ_FINISHED;
if(req->callback)
req->callback(req->opcode, req->result.ret, req->command.ino);
memset(result, 0, sizeof(result_t));
request_free(req);
}
break;
}
default:
SDP_FS_HANDLER_LOGE("unknown message type : %d\n", msg_type);
break;
}
return err;
}
/* Receive messages from netlink socket. */
static void recver(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int len;
int err;
nlh = nlmsg_hdr(skb);
len = skb->len;
err = __recver(skb, nlh);
}
static int to_netlink_msg(sdp_fs_handler_request_t *req, char **msg)
{
*msg = (char *)&req->command;
return sizeof(sdp_fs_command_t);
}
static u32 get_unique_id(sdp_fs_handler_control_t *control)
{
SDP_FS_HANDLER_LOGD("locked\n");
spin_lock(&control->lock);
control->reqctr++;
/* zero is special */
if (control->reqctr == 0)
control->reqctr = 1;
spin_unlock(&control->lock);
SDP_FS_HANDLER_LOGD("unlocked\n");
return control->reqctr;
}
static void req_dump(sdp_fs_handler_request_t *req, const char *msg) {
#if SDP_FS_HANDLER_DEBUG
SDP_FS_HANDLER_LOGD("DUMP REQUEST [%s] ID[%d] opcode[%d] state[%d]\n", msg, req->id, req->opcode, req->state);
#endif
}
static void request_send(sdp_fs_handler_control_t *con,
sdp_fs_handler_request_t *req) {
spin_lock(&con->lock);
SDP_FS_HANDLER_LOGD("entered, control lock\n");
list_add_tail(&req->list, &con->pending_list);
req->state = SDP_FS_HANDLER_REQ_PENDING;
SDP_FS_HANDLER_LOGD("exit, control unlock\n");
spin_unlock(&con->lock);
}
static sdp_fs_handler_request_t *request_find(sdp_fs_handler_control_t *con,
u32 request_id) {
struct list_head *entry;
list_for_each(entry, &con->pending_list) {
sdp_fs_handler_request_t *req;
req = list_entry(entry, sdp_fs_handler_request_t, list);
if (req->id == request_id)
return req;
}
return NULL;
}
static struct kmem_cache *req_cachep;
static void request_init(sdp_fs_handler_request_t *req, u32 opcode) {
memset(req, 0, sizeof(sdp_fs_handler_request_t));
req->state = SDP_FS_HANDLER_REQ_INIT;
req->id = get_unique_id(&g_sdp_fs_handler_control);
INIT_LIST_HEAD(&req->list);
atomic_set(&req->count, 1);
req->aborted = 0;
req->opcode = opcode;
req->callback = NULL;
}
static sdp_fs_handler_request_t *request_alloc(u32 opcode) {
sdp_fs_handler_request_t *req = kmem_cache_alloc(req_cachep, GFP_KERNEL);
if(req)
request_init(req, opcode);
return req;
}
static void request_free(sdp_fs_handler_request_t *req)
{
if(req) {
req_dump(req, "request freed");
/*
* TODO : lock needed here?
*/
list_del(&req->list);
memset(req, 0, sizeof(sdp_fs_handler_request_t));
kmem_cache_free(req_cachep, req);
} else {
SDP_FS_HANDLER_LOGE("req is NULL, skip free\n");
}
}
static void control_init(sdp_fs_handler_control_t *con) {
SDP_FS_HANDLER_LOGD("sdp_fs_handler_control_init");
spin_lock_init(&con->lock);
INIT_LIST_HEAD(&con->pending_list);
spin_lock(&con->lock);
con->reqctr = 0;
spin_unlock(&con->lock);
}
static int __init sdp_fs_handler_mod_init(void) {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3,4,0))
struct netlink_kernel_cfg cfg = {
.input = recver,
};
g_sock = netlink_kernel_create(&init_net, SDP_FS_HANDLER_NETLINK, &cfg);
#else
g_sock = netlink_kernel_create(&init_net, SDP_FS_HANDLER_NETLINK, 0, recver, NULL, THIS_MODULE);
#endif
if (!g_sock) {
SDP_FS_HANDLER_LOGE("Failed to create Crypto Netlink Socket .. Exiting \n");
return -ENOMEM;
}
SDP_FS_HANDLER_LOGE("netlink socket is created successfully! \n");
control_init(&g_sdp_fs_handler_control);
req_cachep = kmem_cache_create("sdp_fs_handler_requst",
sizeof(sdp_fs_handler_request_t),
0, 0, NULL);
if (!req_cachep) {
netlink_kernel_release(g_sock);
SDP_FS_HANDLER_LOGE("Failed to create sdp_fs_handler_requst cache mem.. Exiting \n");
return -ENOMEM;
}
return 0;
}
static void __exit sdp_fs_handler_mod_exit(void) {
netlink_kernel_release(g_sock);
kmem_cache_destroy(req_cachep);
}
module_init(sdp_fs_handler_mod_init);
module_exit(sdp_fs_handler_mod_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SDP FS netlink");