|  | /* krxsecd.c: Rx security daemon | 
|  | * | 
|  | * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. | 
|  | * Written by David Howells (dhowells@redhat.com) | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License | 
|  | * as published by the Free Software Foundation; either version | 
|  | * 2 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This daemon deals with: | 
|  | * - consulting the application as to whether inbound peers and calls should be authorised | 
|  | * - generating security challenges for inbound connections | 
|  | * - responding to security challenges on outbound connections | 
|  | */ | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/sched.h> | 
|  | #include <linux/completion.h> | 
|  | #include <linux/spinlock.h> | 
|  | #include <linux/init.h> | 
|  | #include <rxrpc/krxsecd.h> | 
|  | #include <rxrpc/transport.h> | 
|  | #include <rxrpc/connection.h> | 
|  | #include <rxrpc/message.h> | 
|  | #include <rxrpc/peer.h> | 
|  | #include <rxrpc/call.h> | 
|  | #include <linux/udp.h> | 
|  | #include <linux/ip.h> | 
|  | #include <net/sock.h> | 
|  | #include "internal.h" | 
|  |  | 
|  | static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxsecd_sleepq); | 
|  | static DECLARE_COMPLETION(rxrpc_krxsecd_dead); | 
|  | static volatile int rxrpc_krxsecd_die; | 
|  |  | 
|  | static atomic_t rxrpc_krxsecd_qcount; | 
|  |  | 
|  | /* queue of unprocessed inbound messages with seqno #1 and | 
|  | * RXRPC_CLIENT_INITIATED flag set */ | 
|  | static LIST_HEAD(rxrpc_krxsecd_initmsgq); | 
|  | static DEFINE_SPINLOCK(rxrpc_krxsecd_initmsgq_lock); | 
|  |  | 
|  | static void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg); | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * Rx security daemon | 
|  | */ | 
|  | static int rxrpc_krxsecd(void *arg) | 
|  | { | 
|  | DECLARE_WAITQUEUE(krxsecd, current); | 
|  |  | 
|  | int die; | 
|  |  | 
|  | printk("Started krxsecd %d\n", current->pid); | 
|  |  | 
|  | daemonize("krxsecd"); | 
|  |  | 
|  | /* loop around waiting for work to do */ | 
|  | do { | 
|  | /* wait for work or to be told to exit */ | 
|  | _debug("### Begin Wait"); | 
|  | if (!atomic_read(&rxrpc_krxsecd_qcount)) { | 
|  | set_current_state(TASK_INTERRUPTIBLE); | 
|  |  | 
|  | add_wait_queue(&rxrpc_krxsecd_sleepq, &krxsecd); | 
|  |  | 
|  | for (;;) { | 
|  | set_current_state(TASK_INTERRUPTIBLE); | 
|  | if (atomic_read(&rxrpc_krxsecd_qcount) || | 
|  | rxrpc_krxsecd_die || | 
|  | signal_pending(current)) | 
|  | break; | 
|  |  | 
|  | schedule(); | 
|  | } | 
|  |  | 
|  | remove_wait_queue(&rxrpc_krxsecd_sleepq, &krxsecd); | 
|  | set_current_state(TASK_RUNNING); | 
|  | } | 
|  | die = rxrpc_krxsecd_die; | 
|  | _debug("### End Wait"); | 
|  |  | 
|  | /* see if there're incoming calls in need of authenticating */ | 
|  | _debug("### Begin Inbound Calls"); | 
|  |  | 
|  | if (!list_empty(&rxrpc_krxsecd_initmsgq)) { | 
|  | struct rxrpc_message *msg = NULL; | 
|  |  | 
|  | spin_lock(&rxrpc_krxsecd_initmsgq_lock); | 
|  |  | 
|  | if (!list_empty(&rxrpc_krxsecd_initmsgq)) { | 
|  | msg = list_entry(rxrpc_krxsecd_initmsgq.next, | 
|  | struct rxrpc_message, link); | 
|  | list_del_init(&msg->link); | 
|  | atomic_dec(&rxrpc_krxsecd_qcount); | 
|  | } | 
|  |  | 
|  | spin_unlock(&rxrpc_krxsecd_initmsgq_lock); | 
|  |  | 
|  | if (msg) { | 
|  | rxrpc_krxsecd_process_incoming_call(msg); | 
|  | rxrpc_put_message(msg); | 
|  | } | 
|  | } | 
|  |  | 
|  | _debug("### End Inbound Calls"); | 
|  |  | 
|  | try_to_freeze(PF_FREEZE); | 
|  |  | 
|  | /* discard pending signals */ | 
|  | rxrpc_discard_my_signals(); | 
|  |  | 
|  | } while (!die); | 
|  |  | 
|  | /* and that's all */ | 
|  | complete_and_exit(&rxrpc_krxsecd_dead, 0); | 
|  |  | 
|  | } /* end rxrpc_krxsecd() */ | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * start up a krxsecd daemon | 
|  | */ | 
|  | int __init rxrpc_krxsecd_init(void) | 
|  | { | 
|  | return kernel_thread(rxrpc_krxsecd, NULL, 0); | 
|  |  | 
|  | } /* end rxrpc_krxsecd_init() */ | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * kill the krxsecd daemon and wait for it to complete | 
|  | */ | 
|  | void rxrpc_krxsecd_kill(void) | 
|  | { | 
|  | rxrpc_krxsecd_die = 1; | 
|  | wake_up_all(&rxrpc_krxsecd_sleepq); | 
|  | wait_for_completion(&rxrpc_krxsecd_dead); | 
|  |  | 
|  | } /* end rxrpc_krxsecd_kill() */ | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * clear all pending incoming calls for the specified transport | 
|  | */ | 
|  | void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans) | 
|  | { | 
|  | LIST_HEAD(tmp); | 
|  |  | 
|  | struct rxrpc_message *msg; | 
|  | struct list_head *_p, *_n; | 
|  |  | 
|  | _enter("%p",trans); | 
|  |  | 
|  | /* move all the messages for this transport onto a temp list */ | 
|  | spin_lock(&rxrpc_krxsecd_initmsgq_lock); | 
|  |  | 
|  | list_for_each_safe(_p, _n, &rxrpc_krxsecd_initmsgq) { | 
|  | msg = list_entry(_p, struct rxrpc_message, link); | 
|  | if (msg->trans == trans) { | 
|  | list_del(&msg->link); | 
|  | list_add_tail(&msg->link, &tmp); | 
|  | atomic_dec(&rxrpc_krxsecd_qcount); | 
|  | } | 
|  | } | 
|  |  | 
|  | spin_unlock(&rxrpc_krxsecd_initmsgq_lock); | 
|  |  | 
|  | /* zap all messages on the temp list */ | 
|  | while (!list_empty(&tmp)) { | 
|  | msg = list_entry(tmp.next, struct rxrpc_message, link); | 
|  | list_del_init(&msg->link); | 
|  | rxrpc_put_message(msg); | 
|  | } | 
|  |  | 
|  | _leave(""); | 
|  | } /* end rxrpc_krxsecd_clear_transport() */ | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * queue a message on the incoming calls list | 
|  | */ | 
|  | void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg) | 
|  | { | 
|  | _enter("%p", msg); | 
|  |  | 
|  | /* queue for processing by krxsecd */ | 
|  | spin_lock(&rxrpc_krxsecd_initmsgq_lock); | 
|  |  | 
|  | if (!rxrpc_krxsecd_die) { | 
|  | rxrpc_get_message(msg); | 
|  | list_add_tail(&msg->link, &rxrpc_krxsecd_initmsgq); | 
|  | atomic_inc(&rxrpc_krxsecd_qcount); | 
|  | } | 
|  |  | 
|  | spin_unlock(&rxrpc_krxsecd_initmsgq_lock); | 
|  |  | 
|  | wake_up(&rxrpc_krxsecd_sleepq); | 
|  |  | 
|  | _leave(""); | 
|  | } /* end rxrpc_krxsecd_queue_incoming_call() */ | 
|  |  | 
|  | /*****************************************************************************/ | 
|  | /* | 
|  | * process the initial message of an incoming call | 
|  | */ | 
|  | void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg) | 
|  | { | 
|  | struct rxrpc_transport *trans = msg->trans; | 
|  | struct rxrpc_service *srv; | 
|  | struct rxrpc_call *call; | 
|  | struct list_head *_p; | 
|  | unsigned short sid; | 
|  | int ret; | 
|  |  | 
|  | _enter("%p{tr=%p}", msg, trans); | 
|  |  | 
|  | ret = rxrpc_incoming_call(msg->conn, msg, &call); | 
|  | if (ret < 0) | 
|  | goto out; | 
|  |  | 
|  | /* find the matching service on the transport */ | 
|  | sid = ntohs(msg->hdr.serviceId); | 
|  | srv = NULL; | 
|  |  | 
|  | spin_lock(&trans->lock); | 
|  | list_for_each(_p, &trans->services) { | 
|  | srv = list_entry(_p, struct rxrpc_service, link); | 
|  | if (srv->service_id == sid && try_module_get(srv->owner)) { | 
|  | /* found a match (made sure it won't vanish) */ | 
|  | _debug("found service '%s'", srv->name); | 
|  | call->owner = srv->owner; | 
|  | break; | 
|  | } | 
|  | } | 
|  | spin_unlock(&trans->lock); | 
|  |  | 
|  | /* report the new connection | 
|  | * - the func must inc the call's usage count to keep it | 
|  | */ | 
|  | ret = -ENOENT; | 
|  | if (_p != &trans->services) { | 
|  | /* attempt to accept the call */ | 
|  | call->conn->service = srv; | 
|  | call->app_attn_func = srv->attn_func; | 
|  | call->app_error_func = srv->error_func; | 
|  | call->app_aemap_func = srv->aemap_func; | 
|  |  | 
|  | ret = srv->new_call(call); | 
|  |  | 
|  | /* send an abort if an error occurred */ | 
|  | if (ret < 0) { | 
|  | rxrpc_call_abort(call, ret); | 
|  | } | 
|  | else { | 
|  | /* formally receive and ACK the new packet */ | 
|  | ret = rxrpc_conn_receive_call_packet(call->conn, | 
|  | call, msg); | 
|  | } | 
|  | } | 
|  |  | 
|  | rxrpc_put_call(call); | 
|  | out: | 
|  | if (ret < 0) | 
|  | rxrpc_trans_immediate_abort(trans, msg, ret); | 
|  |  | 
|  | _leave(" (%d)", ret); | 
|  | } /* end rxrpc_krxsecd_process_incoming_call() */ |