/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NETLINK_FIPS_CRYPTO 29 #define PUB_CRYPTO_PID_SET 3001 #define PUB_CRYPTO_RESULT 3002 #define RESULT_ARRAY_MAX_LEN 100 #define CRYPTO_MAX_TIMEOUT HZ/5 #define PUB_CRYPTO_REQ_TIMEOUT 3000 pub_crypto_control_t g_pub_crypto_control; DEFINE_MUTEX(crypto_send_mutex); static int user_fipscryptod_pid = 0; static struct sock* crypto_sock = NULL; static int pub_crypto_request_get_msg(pub_crypto_request_t *req, char **msg); static void request_send(pub_crypto_control_t *con, pub_crypto_request_t *req); static void request_wait_answer(pub_crypto_control_t *con, pub_crypto_request_t *req); static pub_crypto_request_t *request_find(pub_crypto_control_t *con, u32 request_id); static pub_crypto_request_t *request_alloc(u32 opcode); static void request_free(pub_crypto_control_t *con, pub_crypto_request_t *req); static void req_dump(pub_crypto_request_t *req, const char *msg); static void dump(unsigned char *buf, int len, const char *msg); /* Debug */ #define PUB_CRYPTO_DEBUG 0 #if PUB_CRYPTO_DEBUG #define PUB_CRYPTO_LOGD(FMT, ...) printk("SDP_PUB_CRYPTO[%d] : %s " FMT , current->pid, __func__, ##__VA_ARGS__) #else #define PUB_CRYPTO_LOGD(FMT, ...) #endif /* PUB_CRYPTO_DEBUG */ #define PUB_CRYPTO_LOGE(FMT, ...) printk("SDP_PUB_CRYPTO[%d] : %s " FMT , current->pid, __func__, ##__VA_ARGS__) #define PUB_CRYPTO_LOGI(FMT, ...) printk("SDP_PUB_CRYPTO[%d] : %s " FMT , current->pid, __func__, ##__VA_ARGS__) //static char* process_crypto_request(u8 opcode, char* send_msg, // int send_msg_size, int* result_len, int* ret) { static int __do_dek_crypt(pub_crypto_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; PUB_CRYPTO_LOGD("====================== \t entred\n"); if(req == NULL) { PUB_CRYPTO_LOGE("invalid request\n"); return -1; } request_send(&g_pub_crypto_control, req); nl_msg_size = pub_crypto_request_get_msg(req, &nl_msg); if(nl_msg_size <= 0) { PUB_CRYPTO_LOGE("invalid opcode %d\n", req->opcode); return -1; } // sending netlink message skb_in = nlmsg_new(nl_msg_size, 0); if (!skb_in) { PUB_CRYPTO_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(&crypto_send_mutex); rc = nlmsg_unicast(crypto_sock, skb_in, user_fipscryptod_pid); mutex_unlock(&crypto_send_mutex); if (rc < 0) { PUB_CRYPTO_LOGE("Error while sending bak to user, err id: %d\n", rc); return -1; } /* * In a very rare case, response comes before request gets into pending list. */ if(req->state != PUB_CRYPTO_REQ_FINISHED) request_wait_answer(&g_pub_crypto_control, req); else PUB_CRYPTO_LOGE("request already finished, skip waiting\n"); skb_out = skb_dequeue(&crypto_sock->sk_receive_queue); if(req->state != PUB_CRYPTO_REQ_FINISHED) { PUB_CRYPTO_LOGE("FIPS_CRYPTO_ERROR!!!\n"); /* * TODO : * Request not finished by an interrupt or abort. */ rc = -EINTR; goto out; } if(req->aborted) { PUB_CRYPTO_LOGE("Request aborted!!!\n"); rc = -ETIMEDOUT; goto out; } if(req->result.ret < 0) { PUB_CRYPTO_LOGE("failed to opcode(%d)!!!\n", req->opcode); rc = req->result.ret; goto out; } switch(req->opcode) { case OP_DH_ENC: case OP_DH_DEC: case OP_ECDH_ENC: case OP_ECDH_DEC: case OP_ECDH_REQ_SS: dump(req->result.dek.buf, req->result.dek.len, "req->result.dek"); memcpy(ret, &(req->result.dek), sizeof(dek_t)); //dump(req->result.dek.buf, req->result.dek.len, "req->result.dek"); rc = 0; break; case OP_RSA_ENC: case OP_RSA_DEC: memcpy(ret, &(req->result.dek), sizeof(dek_t)); rc = 0; break; default: PUB_CRYPTO_LOGE("Not supported opcode(%d)!!!\n", req->opcode); rc = -EOPNOTSUPP; break; } out: if(skb_out) { kfree_skb(skb_out); } if(rc != 0) req_dump(req, "failed"); return rc; } static int pub_crypto_recv_msg(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 PUB_CRYPTO_PID_SET: status_get = (struct audit_status *)data; user_fipscryptod_pid = status_get->pid; PUB_CRYPTO_LOGE("crypto_receive_msg: pid = %d\n", user_fipscryptod_pid); break; case PUB_CRYPTO_RESULT: { result_t *result = (result_t *)data; pub_crypto_request_t *req = NULL; req = request_find(&g_pub_crypto_control, result->request_id); if(req) { memcpy(&req->result, result, sizeof(result_t)); req->state = PUB_CRYPTO_REQ_FINISHED; wake_up(&req->waitq); memset(result, 0, sizeof(result_t)); } break; } default: PUB_CRYPTO_LOGE("unknown message type : %d\n", msg_type); break; } return err; } /* Receive messages from netlink socket. */ static void crypto_recver(struct sk_buff *skb) { struct nlmsghdr *nlh; int len; int err; nlh = nlmsg_hdr(skb); len = skb->len; err = pub_crypto_recv_msg(skb, nlh); } static void dump(unsigned char *buf, int len, const char *msg) { #if PUB_CRYPTO_DEBUG int i; printk("%s len=%d: ", msg, len); for(i=0;iopcode) { case OP_RSA_ENC: case OP_RSA_DEC: case OP_DH_ENC: case OP_DH_DEC: case OP_ECDH_ENC: case OP_ECDH_DEC: case OP_ECDH_REQ_SS: req->cipher_param.request_id = req->id; req->cipher_param.opcode = req->opcode; memcpy(&req->cipher_param.in, (void *) in, sizeof(dek_t)); memcpy(&req->cipher_param.key, (void *) key, sizeof(kek_t)); break; default: PUB_CRYPTO_LOGE("opcode[%d] failed, not supported\n", opcode); goto error; break; } ret = __do_dek_crypt(req, (char *)out); if(ret != 0) { PUB_CRYPTO_LOGE("opcode[%d] failed\n", opcode); goto error; } } else { PUB_CRYPTO_LOGE("request allocation failed\n"); return -ENOMEM; } request_free(&g_pub_crypto_control, req); return 0; error: request_free(&g_pub_crypto_control, req); return -1; } int rsa_encryptByPub(dek_t *dek, dek_t *edek, kek_t *key){ return do_dek_crypt(OP_RSA_ENC, dek, edek, key); } int rsa_decryptByPair(dek_t *edek, dek_t *dek, kek_t *key){ return do_dek_crypt(OP_RSA_DEC, edek, dek, key); } int dh_encryptDEK(dek_t *dek, dek_t *edek, kek_t *key){ return do_dek_crypt(OP_DH_ENC, dek, edek, key); } int dh_decryptEDEK(dek_t *edek, dek_t *dek, kek_t *key){ return do_dek_crypt(OP_DH_DEC, edek, dek, key); } int ecdh_encryptDEK(dek_t *dek, dek_t *edek, kek_t *key){ return do_dek_crypt(OP_ECDH_ENC, dek, edek, key); } int ecdh_decryptEDEK(dek_t *edek, dek_t *dek, kek_t *key){ return do_dek_crypt(OP_ECDH_DEC, edek, dek, key); } int ecdh_deriveSS(dek_t *in, dek_t *out, kek_t *drv_key){ return do_dek_crypt(OP_ECDH_REQ_SS, in, out, drv_key); } static int pub_crypto_request_get_msg(pub_crypto_request_t *req, char **msg) { int msg_len = -1; switch(req->opcode) { case OP_RSA_ENC: case OP_RSA_DEC: case OP_DH_DEC: case OP_DH_ENC: case OP_ECDH_DEC: case OP_ECDH_ENC: case OP_ECDH_REQ_SS: *msg = (char *)&req->cipher_param; msg_len = (int) sizeof(cipher_param_t); break; default: *msg = NULL; msg_len = -1; break; } return msg_len; } static u32 pub_crypto_get_unique_id(pub_crypto_control_t *control) { u32 local_reqctr; spin_lock(&control->lock); ++(control->reqctr); /* zero is special */ if (control->reqctr == 0) control->reqctr = 1; local_reqctr = control->reqctr; spin_unlock(&control->lock); return local_reqctr; } static void req_dump(pub_crypto_request_t *req, const char *msg) { PUB_CRYPTO_LOGI("req %s {id:%d op:%d state:%d}\n", msg, req->id, req->opcode, req->state); } static void request_send(pub_crypto_control_t *con, pub_crypto_request_t *req) { spin_lock(&con->lock); list_add_tail(&req->list, &con->pending_list); req->state = PUB_CRYPTO_REQ_PENDING; req_dump(req, "added"); spin_unlock(&con->lock); } static void request_wait_answer(pub_crypto_control_t *con, pub_crypto_request_t *req) { int intr; while (req->state != PUB_CRYPTO_REQ_FINISHED) { /* * TODO : can anyone answer what happens when current process gets killed here? */ intr = wait_event_interruptible_timeout(req->waitq, req->state == PUB_CRYPTO_REQ_FINISHED, msecs_to_jiffies(PUB_CRYPTO_REQ_TIMEOUT)); if(req->state == PUB_CRYPTO_REQ_FINISHED) break; if(intr == 0) { PUB_CRYPTO_LOGE("timeout! %d [ID:%d] \n", intr, req->id); req->state = PUB_CRYPTO_REQ_FINISHED; req->aborted = 1; break; } if(intr == -ERESTARTSYS) { PUB_CRYPTO_LOGE("wait interrupted : intr %d(-ERESTARTSYS) \n", intr); break; } } } static pub_crypto_request_t *request_find(pub_crypto_control_t *con, u32 request_id) { struct list_head *entry; spin_lock(&con->lock); list_for_each(entry, &con->pending_list) { pub_crypto_request_t *req; req = list_entry(entry, pub_crypto_request_t, list); if (req->id == request_id) { req_dump(req, "found"); spin_unlock(&con->lock); return req; } } spin_unlock(&con->lock); PUB_CRYPTO_LOGE("Can't find request %d\n", request_id); return NULL; } static struct kmem_cache *pub_crypto_req_cachep; static void pub_crypto_request_init(pub_crypto_request_t *req, u32 opcode) { memset(req, 0, sizeof(pub_crypto_request_t)); req->state = PUB_CRYPTO_REQ_INIT; req->id = pub_crypto_get_unique_id(&g_pub_crypto_control); INIT_LIST_HEAD(&req->list); init_waitqueue_head(&req->waitq); atomic_set(&req->count, 1); req->aborted = 0; req->opcode = opcode; } static pub_crypto_request_t *request_alloc(u32 opcode) { pub_crypto_request_t *req = kmem_cache_alloc(pub_crypto_req_cachep, GFP_KERNEL); if(req) pub_crypto_request_init(req, opcode); return req; } static void request_free(pub_crypto_control_t *con, pub_crypto_request_t *req) { if(req) { req_dump(req, "freed"); spin_lock(&con->lock); list_del(&req->list); memset(req, 0, sizeof(pub_crypto_request_t)); kmem_cache_free(pub_crypto_req_cachep, req); spin_unlock(&con->lock); } else { PUB_CRYPTO_LOGE("req is NULL, skip free\n"); } } void pub_crypto_control_init(pub_crypto_control_t *con) { PUB_CRYPTO_LOGD("pub_crypto_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 pub_crypto_mod_init(void) { #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,4,0)) struct netlink_kernel_cfg cfg = { .input = crypto_recver, }; crypto_sock = netlink_kernel_create(&init_net, NETLINK_FIPS_CRYPTO, &cfg); #else crypto_sock = netlink_kernel_create(&init_net, NETLINK_FIPS_CRYPTO, 0, crypto_recver, NULL, THIS_MODULE); #endif if (!crypto_sock) { PUB_CRYPTO_LOGE("Failed to create Crypto Netlink Socket .. Exiting \n"); return -ENOMEM; } PUB_CRYPTO_LOGE("netlink socket is created successfully! \n"); pub_crypto_control_init(&g_pub_crypto_control); pub_crypto_req_cachep = kmem_cache_create("pub_crypto_requst", sizeof(pub_crypto_request_t), 0, 0, NULL); if (!pub_crypto_req_cachep) { netlink_kernel_release(crypto_sock); PUB_CRYPTO_LOGE("Failed to create pub_crypto_requst cache mem.. Exiting \n"); return -ENOMEM; } return 0; } static void __exit pub_crypto_mod_exit(void) { /* if (crypto_sock && crypto_sock->sk_socket) { sock_release(crypto_sock->sk_socket); } */ netlink_kernel_release(crypto_sock); kmem_cache_destroy(pub_crypto_req_cachep); } module_init(pub_crypto_mod_init); module_exit(pub_crypto_mod_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SDP pub crypto");