123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- /**
- * eCryptfs: Linux filesystem encryption layer
- *
- * Copyright (C) 2004-2006 International Business Machines Corp.
- * Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
- * Tyler Hicks <tyhicks@ou.edu>
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
- #include <linux/sched.h>
- #include "ecryptfs_kernel.h"
- static LIST_HEAD(ecryptfs_msg_ctx_free_list);
- static LIST_HEAD(ecryptfs_msg_ctx_alloc_list);
- static struct mutex ecryptfs_msg_ctx_lists_mux;
- static struct hlist_head *ecryptfs_daemon_id_hash;
- static struct mutex ecryptfs_daemon_id_hash_mux;
- static int ecryptfs_hash_buckets;
- #define ecryptfs_uid_hash(uid) \
- hash_long((unsigned long)uid, ecryptfs_hash_buckets)
- static unsigned int ecryptfs_msg_counter;
- static struct ecryptfs_msg_ctx *ecryptfs_msg_ctx_arr;
- /**
- * ecryptfs_acquire_free_msg_ctx
- * @msg_ctx: The context that was acquired from the free list
- *
- * Acquires a context element from the free list and locks the mutex
- * on the context. Returns zero on success; non-zero on error or upon
- * failure to acquire a free context element. Be sure to lock the
- * list mutex before calling.
- */
- static int ecryptfs_acquire_free_msg_ctx(struct ecryptfs_msg_ctx **msg_ctx)
- {
- struct list_head *p;
- int rc;
- if (list_empty(&ecryptfs_msg_ctx_free_list)) {
- ecryptfs_printk(KERN_WARNING, "The eCryptfs free "
- "context list is empty. It may be helpful to "
- "specify the ecryptfs_message_buf_len "
- "parameter to be greater than the current "
- "value of [%d]\n", ecryptfs_message_buf_len);
- rc = -ENOMEM;
- goto out;
- }
- list_for_each(p, &ecryptfs_msg_ctx_free_list) {
- *msg_ctx = list_entry(p, struct ecryptfs_msg_ctx, node);
- if (mutex_trylock(&(*msg_ctx)->mux)) {
- (*msg_ctx)->task = current;
- rc = 0;
- goto out;
- }
- }
- rc = -ENOMEM;
- out:
- return rc;
- }
- /**
- * ecryptfs_msg_ctx_free_to_alloc
- * @msg_ctx: The context to move from the free list to the alloc list
- *
- * Be sure to lock the list mutex and the context mutex before
- * calling.
- */
- static void ecryptfs_msg_ctx_free_to_alloc(struct ecryptfs_msg_ctx *msg_ctx)
- {
- list_move(&msg_ctx->node, &ecryptfs_msg_ctx_alloc_list);
- msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_PENDING;
- msg_ctx->counter = ++ecryptfs_msg_counter;
- }
- /**
- * ecryptfs_msg_ctx_alloc_to_free
- * @msg_ctx: The context to move from the alloc list to the free list
- *
- * Be sure to lock the list mutex and the context mutex before
- * calling.
- */
- static void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx)
- {
- list_move(&(msg_ctx->node), &ecryptfs_msg_ctx_free_list);
- if (msg_ctx->msg)
- kfree(msg_ctx->msg);
- msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_FREE;
- }
- /**
- * ecryptfs_find_daemon_id
- * @uid: The user id which maps to the desired daemon id
- * @id: If return value is zero, points to the desired daemon id
- * pointer
- *
- * Search the hash list for the given user id. Returns zero if the
- * user id exists in the list; non-zero otherwise. The daemon id hash
- * mutex should be held before calling this function.
- */
- static int ecryptfs_find_daemon_id(uid_t uid, struct ecryptfs_daemon_id **id)
- {
- struct hlist_node *elem;
- int rc;
- hlist_for_each_entry(*id, elem,
- &ecryptfs_daemon_id_hash[ecryptfs_uid_hash(uid)],
- id_chain) {
- if ((*id)->uid == uid) {
- rc = 0;
- goto out;
- }
- }
- rc = -EINVAL;
- out:
- return rc;
- }
- static int ecryptfs_send_raw_message(unsigned int transport, u16 msg_type,
- pid_t pid)
- {
- int rc;
- switch(transport) {
- case ECRYPTFS_TRANSPORT_NETLINK:
- rc = ecryptfs_send_netlink(NULL, 0, NULL, msg_type, 0, pid);
- break;
- case ECRYPTFS_TRANSPORT_CONNECTOR:
- case ECRYPTFS_TRANSPORT_RELAYFS:
- default:
- rc = -ENOSYS;
- }
- return rc;
- }
- /**
- * ecryptfs_process_helo
- * @transport: The underlying transport (netlink, etc.)
- * @uid: The user ID owner of the message
- * @pid: The process ID for the userspace program that sent the
- * message
- *
- * Adds the uid and pid values to the daemon id hash. If a uid
- * already has a daemon pid registered, the daemon will be
- * unregistered before the new daemon id is put into the hash list.
- * Returns zero after adding a new daemon id to the hash list;
- * non-zero otherwise.
- */
- int ecryptfs_process_helo(unsigned int transport, uid_t uid, pid_t pid)
- {
- struct ecryptfs_daemon_id *new_id;
- struct ecryptfs_daemon_id *old_id;
- int rc;
- mutex_lock(&ecryptfs_daemon_id_hash_mux);
- new_id = kmalloc(sizeof(*new_id), GFP_KERNEL);
- if (!new_id) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Failed to allocate memory; unable "
- "to register daemon [%d] for user [%d]\n",
- pid, uid);
- goto unlock;
- }
- if (!ecryptfs_find_daemon_id(uid, &old_id)) {
- printk(KERN_WARNING "Received request from user [%d] "
- "to register daemon [%d]; unregistering daemon "
- "[%d]\n", uid, pid, old_id->pid);
- hlist_del(&old_id->id_chain);
- rc = ecryptfs_send_raw_message(transport, ECRYPTFS_NLMSG_QUIT,
- old_id->pid);
- if (rc)
- printk(KERN_WARNING "Failed to send QUIT "
- "message to daemon [%d]; rc = [%d]\n",
- old_id->pid, rc);
- kfree(old_id);
- }
- new_id->uid = uid;
- new_id->pid = pid;
- hlist_add_head(&new_id->id_chain,
- &ecryptfs_daemon_id_hash[ecryptfs_uid_hash(uid)]);
- rc = 0;
- unlock:
- mutex_unlock(&ecryptfs_daemon_id_hash_mux);
- return rc;
- }
- /**
- * ecryptfs_process_quit
- * @uid: The user ID owner of the message
- * @pid: The process ID for the userspace program that sent the
- * message
- *
- * Deletes the corresponding daemon id for the given uid and pid, if
- * it is the registered that is requesting the deletion. Returns zero
- * after deleting the desired daemon id; non-zero otherwise.
- */
- int ecryptfs_process_quit(uid_t uid, pid_t pid)
- {
- struct ecryptfs_daemon_id *id;
- int rc;
- mutex_lock(&ecryptfs_daemon_id_hash_mux);
- if (ecryptfs_find_daemon_id(uid, &id)) {
- rc = -EINVAL;
- ecryptfs_printk(KERN_ERR, "Received request from user [%d] to "
- "unregister unrecognized daemon [%d]\n", uid,
- pid);
- goto unlock;
- }
- if (id->pid != pid) {
- rc = -EINVAL;
- ecryptfs_printk(KERN_WARNING, "Received request from user [%d] "
- "with pid [%d] to unregister daemon [%d]\n",
- uid, pid, id->pid);
- goto unlock;
- }
- hlist_del(&id->id_chain);
- kfree(id);
- rc = 0;
- unlock:
- mutex_unlock(&ecryptfs_daemon_id_hash_mux);
- return rc;
- }
- /**
- * ecryptfs_process_reponse
- * @msg: The ecryptfs message received; the caller should sanity check
- * msg->data_len
- * @pid: The process ID of the userspace application that sent the
- * message
- * @seq: The sequence number of the message
- *
- * Processes a response message after sending a operation request to
- * userspace. Returns zero upon delivery to desired context element;
- * non-zero upon delivery failure or error.
- */
- int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t uid,
- pid_t pid, u32 seq)
- {
- struct ecryptfs_daemon_id *id;
- struct ecryptfs_msg_ctx *msg_ctx;
- int msg_size;
- int rc;
- if (msg->index >= ecryptfs_message_buf_len) {
- rc = -EINVAL;
- ecryptfs_printk(KERN_ERR, "Attempt to reference "
- "context buffer at index [%d]; maximum "
- "allowable is [%d]\n", msg->index,
- (ecryptfs_message_buf_len - 1));
- goto out;
- }
- msg_ctx = &ecryptfs_msg_ctx_arr[msg->index];
- mutex_lock(&msg_ctx->mux);
- if (ecryptfs_find_daemon_id(msg_ctx->task->euid, &id)) {
- rc = -EBADMSG;
- ecryptfs_printk(KERN_WARNING, "User [%d] received a "
- "message response from process [%d] but does "
- "not have a registered daemon\n",
- msg_ctx->task->euid, pid);
- goto wake_up;
- }
- if (msg_ctx->task->euid != uid) {
- rc = -EBADMSG;
- ecryptfs_printk(KERN_WARNING, "Received message from user "
- "[%d]; expected message from user [%d]\n",
- uid, msg_ctx->task->euid);
- goto unlock;
- }
- if (id->pid != pid) {
- rc = -EBADMSG;
- ecryptfs_printk(KERN_ERR, "User [%d] received a "
- "message response from an unrecognized "
- "process [%d]\n", msg_ctx->task->euid, pid);
- goto unlock;
- }
- if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_PENDING) {
- rc = -EINVAL;
- ecryptfs_printk(KERN_WARNING, "Desired context element is not "
- "pending a response\n");
- goto unlock;
- } else if (msg_ctx->counter != seq) {
- rc = -EINVAL;
- ecryptfs_printk(KERN_WARNING, "Invalid message sequence; "
- "expected [%d]; received [%d]\n",
- msg_ctx->counter, seq);
- goto unlock;
- }
- msg_size = sizeof(*msg) + msg->data_len;
- msg_ctx->msg = kmalloc(msg_size, GFP_KERNEL);
- if (!msg_ctx->msg) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
- goto unlock;
- }
- memcpy(msg_ctx->msg, msg, msg_size);
- msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_DONE;
- rc = 0;
- wake_up:
- wake_up_process(msg_ctx->task);
- unlock:
- mutex_unlock(&msg_ctx->mux);
- out:
- return rc;
- }
- /**
- * ecryptfs_send_message
- * @transport: The transport over which to send the message (i.e.,
- * netlink)
- * @data: The data to send
- * @data_len: The length of data
- * @msg_ctx: The message context allocated for the send
- */
- int ecryptfs_send_message(unsigned int transport, char *data, int data_len,
- struct ecryptfs_msg_ctx **msg_ctx)
- {
- struct ecryptfs_daemon_id *id;
- int rc;
- mutex_lock(&ecryptfs_daemon_id_hash_mux);
- if (ecryptfs_find_daemon_id(current->euid, &id)) {
- mutex_unlock(&ecryptfs_daemon_id_hash_mux);
- rc = -ENOTCONN;
- ecryptfs_printk(KERN_ERR, "User [%d] does not have a daemon "
- "registered\n", current->euid);
- goto out;
- }
- mutex_unlock(&ecryptfs_daemon_id_hash_mux);
- mutex_lock(&ecryptfs_msg_ctx_lists_mux);
- rc = ecryptfs_acquire_free_msg_ctx(msg_ctx);
- if (rc) {
- mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
- ecryptfs_printk(KERN_WARNING, "Could not claim a free "
- "context element\n");
- goto out;
- }
- ecryptfs_msg_ctx_free_to_alloc(*msg_ctx);
- mutex_unlock(&(*msg_ctx)->mux);
- mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
- switch (transport) {
- case ECRYPTFS_TRANSPORT_NETLINK:
- rc = ecryptfs_send_netlink(data, data_len, *msg_ctx,
- ECRYPTFS_NLMSG_REQUEST, 0, id->pid);
- break;
- case ECRYPTFS_TRANSPORT_CONNECTOR:
- case ECRYPTFS_TRANSPORT_RELAYFS:
- default:
- rc = -ENOSYS;
- }
- if (rc) {
- printk(KERN_ERR "Error attempting to send message to userspace "
- "daemon; rc = [%d]\n", rc);
- }
- out:
- return rc;
- }
- /**
- * ecryptfs_wait_for_response
- * @msg_ctx: The context that was assigned when sending a message
- * @msg: The incoming message from userspace; not set if rc != 0
- *
- * Sleeps until awaken by ecryptfs_receive_message or until the amount
- * of time exceeds ecryptfs_message_wait_timeout. If zero is
- * returned, msg will point to a valid message from userspace; a
- * non-zero value is returned upon failure to receive a message or an
- * error occurs.
- */
- int ecryptfs_wait_for_response(struct ecryptfs_msg_ctx *msg_ctx,
- struct ecryptfs_message **msg)
- {
- signed long timeout = ecryptfs_message_wait_timeout * HZ;
- int rc = 0;
- sleep:
- timeout = schedule_timeout_interruptible(timeout);
- mutex_lock(&ecryptfs_msg_ctx_lists_mux);
- mutex_lock(&msg_ctx->mux);
- if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_DONE) {
- if (timeout) {
- mutex_unlock(&msg_ctx->mux);
- mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
- goto sleep;
- }
- rc = -ENOMSG;
- } else {
- *msg = msg_ctx->msg;
- msg_ctx->msg = NULL;
- }
- ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
- mutex_unlock(&msg_ctx->mux);
- mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
- return rc;
- }
- int ecryptfs_init_messaging(unsigned int transport)
- {
- int i;
- int rc = 0;
- if (ecryptfs_number_of_users > ECRYPTFS_MAX_NUM_USERS) {
- ecryptfs_number_of_users = ECRYPTFS_MAX_NUM_USERS;
- ecryptfs_printk(KERN_WARNING, "Specified number of users is "
- "too large, defaulting to [%d] users\n",
- ecryptfs_number_of_users);
- }
- mutex_init(&ecryptfs_daemon_id_hash_mux);
- mutex_lock(&ecryptfs_daemon_id_hash_mux);
- ecryptfs_hash_buckets = 1;
- while (ecryptfs_number_of_users >> ecryptfs_hash_buckets)
- ecryptfs_hash_buckets++;
- ecryptfs_daemon_id_hash = kmalloc(sizeof(struct hlist_head)
- * ecryptfs_hash_buckets, GFP_KERNEL);
- if (!ecryptfs_daemon_id_hash) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
- goto out;
- }
- for (i = 0; i < ecryptfs_hash_buckets; i++)
- INIT_HLIST_HEAD(&ecryptfs_daemon_id_hash[i]);
- mutex_unlock(&ecryptfs_daemon_id_hash_mux);
- ecryptfs_msg_ctx_arr = kmalloc((sizeof(struct ecryptfs_msg_ctx)
- * ecryptfs_message_buf_len), GFP_KERNEL);
- if (!ecryptfs_msg_ctx_arr) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Failed to allocate memory\n");
- goto out;
- }
- mutex_init(&ecryptfs_msg_ctx_lists_mux);
- mutex_lock(&ecryptfs_msg_ctx_lists_mux);
- ecryptfs_msg_counter = 0;
- for (i = 0; i < ecryptfs_message_buf_len; i++) {
- INIT_LIST_HEAD(&ecryptfs_msg_ctx_arr[i].node);
- mutex_init(&ecryptfs_msg_ctx_arr[i].mux);
- mutex_lock(&ecryptfs_msg_ctx_arr[i].mux);
- ecryptfs_msg_ctx_arr[i].index = i;
- ecryptfs_msg_ctx_arr[i].state = ECRYPTFS_MSG_CTX_STATE_FREE;
- ecryptfs_msg_ctx_arr[i].counter = 0;
- ecryptfs_msg_ctx_arr[i].task = NULL;
- ecryptfs_msg_ctx_arr[i].msg = NULL;
- list_add_tail(&ecryptfs_msg_ctx_arr[i].node,
- &ecryptfs_msg_ctx_free_list);
- mutex_unlock(&ecryptfs_msg_ctx_arr[i].mux);
- }
- mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
- switch(transport) {
- case ECRYPTFS_TRANSPORT_NETLINK:
- rc = ecryptfs_init_netlink();
- if (rc)
- ecryptfs_release_messaging(transport);
- break;
- case ECRYPTFS_TRANSPORT_CONNECTOR:
- case ECRYPTFS_TRANSPORT_RELAYFS:
- default:
- rc = -ENOSYS;
- }
- out:
- return rc;
- }
- void ecryptfs_release_messaging(unsigned int transport)
- {
- if (ecryptfs_msg_ctx_arr) {
- int i;
- mutex_lock(&ecryptfs_msg_ctx_lists_mux);
- for (i = 0; i < ecryptfs_message_buf_len; i++) {
- mutex_lock(&ecryptfs_msg_ctx_arr[i].mux);
- if (ecryptfs_msg_ctx_arr[i].msg)
- kfree(ecryptfs_msg_ctx_arr[i].msg);
- mutex_unlock(&ecryptfs_msg_ctx_arr[i].mux);
- }
- kfree(ecryptfs_msg_ctx_arr);
- mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
- }
- if (ecryptfs_daemon_id_hash) {
- struct hlist_node *elem;
- struct ecryptfs_daemon_id *id;
- int i;
- mutex_lock(&ecryptfs_daemon_id_hash_mux);
- for (i = 0; i < ecryptfs_hash_buckets; i++) {
- hlist_for_each_entry(id, elem,
- &ecryptfs_daemon_id_hash[i],
- id_chain) {
- hlist_del(elem);
- kfree(id);
- }
- }
- kfree(ecryptfs_daemon_id_hash);
- mutex_unlock(&ecryptfs_daemon_id_hash_mux);
- }
- switch(transport) {
- case ECRYPTFS_TRANSPORT_NETLINK:
- ecryptfs_release_netlink();
- break;
- case ECRYPTFS_TRANSPORT_CONNECTOR:
- case ECRYPTFS_TRANSPORT_RELAYFS:
- default:
- break;
- }
- return;
- }
|