www.pudn.com > wtls.rar > wtls.c
/* * wtls.c - WTLS Server-side implementation * * Nick Clarey*/ #if (HAVE_WTLS_OPENSSL) #include "gwlib/gwlib.h" #include "wtls.h" #include "timers.h" #include "wap_events.h" #include "wtls_pdu.h" #include "wtls_statesupport.h" #include "gw/msg.h" #include "wtp.h" /*********************************************************************** * Internal data structures. * * List of WTLS Server machines. */ static List *wtls_machines = NULL; /* * Counter for WTLS Server machine id numbers, to make sure they are unique. */ static Counter *wtls_machine_id_counter = NULL; /* * Give the status of wtls server layer: * limbo - not running at all * running - operating normally * terminating - waiting for operations to terminate, returning to limbo */ static enum { limbo, running, terminating } wtls_run_status = limbo; /* * Queue of events to be handled by WTLS Server machines. */ static List *wtls_queue = NULL; /***************************************************************************** * * Prototypes of internal functions: */ /* * Create and destroy an uninitialized wtls server state machine. */ static WTLSMachine* wtls_machine_create(WAPAddrTuple *tuple); static void wtls_machine_create_destroy(void *sm); static void wtls_machine_destroy(void * p); /* * Checks whether the list of wlts server machines includes a specific machine. * * The machine in question is identified with with source and destination * address and port. If the machine does not exist and the event is either; * - A SEC-Create-Request.req or * - A T-Unitdata.ind containing a ClientHello packet or * - A T-Unitdata.ind containing an Alert(no_renegotiation) packet * a new machine is created and added in the machines data structure. * * See WTLS 7.2 for details of this check. */ static WTLSMachine *wtls_machine_find_or_create(WAPEvent *event); /* * Feed an event to a WTLS Server state machine. Handle all errors by * itself, do not report them to the caller. */ static void wtls_event_handle(WTLSMachine *machine, WAPEvent *event); /* * Print a WTLS Server machine state as a string. */ static unsigned char *name_wtlser_state(int name); /* * Find a WTLS Server machine from the global list of wtls server * structures that corresponds to the four-tuple of source and destination * addresses and ports. Return a pointer to the machine, or NULL if not found. */ static WTLSMachine *wtls_machine_find(WAPAddrTuple *tuple, long mid); static void main_thread(void *); static WTLSMachine *find_wtls_machine_using_mid(long mid); static void add_wtls_address(Msg *msg, WTLSMachine *wtls_machine); /* The match* functions are used for searches through lists */ static int match_handshake_type(void* item, void* pattern); static int match_pdu_type(void* item, void* pattern); /*static WAPEvent *create_tr_invoke_ind(WTPRespMachine *sm, Octstr *user_data); static WAPEvent *create_tr_abort_ind(WTPRespMachine *sm, long abort_reason); static WAPEvent *create_tr_result_cnf(WTPRespMachine *sm); */ /****************************************************************************** * * EXTERNAL FUNCTIONS: * */ WAPEvent *wtls_unpack_wdp_datagram(Msg *msg) { WAPEvent* unitdataIndEvent; List* wtlsPayloadList; /* Dump the Msg */ msg_dump(msg,0); /* Then, stuff it into a T_Unitdata_Ind Event */ unitdataIndEvent = wap_event_create(T_Unitdata_Ind); info(0,"Event created"); /* Firstly, the address */ unitdataIndEvent->u.T_Unitdata_Ind.addr_tuple = wap_addr_tuple_create(msg->wdp_datagram.source_address, msg->wdp_datagram.source_port, msg->wdp_datagram.destination_address, msg->wdp_datagram.destination_port); info(0,"Set address and stuff"); /* Attempt to stuff this baby into a list-of-WTLS-PDUs */ wtlsPayloadList = wtls_unpack_payloadlist (msg->wdp_datagram.user_data); info(0,"Datagram unpacked!"); /* Then, the pdu material */ unitdataIndEvent->u.T_Unitdata_Ind.pdu_list = wtlsPayloadList; /* And return the event */ return unitdataIndEvent; } void wtls_init(void) { /* Initialise our various lists and counters */ wtls_machines = list_create(); wtls_machine_id_counter = counter_create(); wtls_queue = list_create(); list_add_producer(wtls_queue); /* Idiot check - ensure that we are able to start running */ gw_assert(wtls_run_status == limbo); wtls_run_status = running; gwthread_create(main_thread, NULL); } void wtls_shutdown(void) { /* Make sure that we're actually running; if so, then prepare for termination */ gw_assert(wtls_run_status == running); wtls_run_status = terminating; list_remove_producer(wtls_queue); gwthread_join_every(main_thread); /* Print out a friendly message stating that we're going to die */ debug("wap.wtls", 0, "wtls_shutdown: %ld wtls machines left", list_len(wtls_machines)); /* And clean up nicely after ourselves */ list_destroy(wtls_machines, wtls_machine_destroy); list_destroy(wtls_queue, wap_event_destroy_item); counter_destroy(wtls_machine_id_counter); } void wtls_dispatch_event(WAPEvent *event) { /* Stick the event on the incoming events queue */ list_produce(wtls_queue, event); } int wtls_get_address_tuple(long mid, WAPAddrTuple **tuple) { WTLSMachine *sm; sm = find_wtls_machine_using_mid(mid); if (sm == NULL) return -1; *tuple = wap_addr_tuple_duplicate(sm->addr_tuple); return 0; } void send_alert(int alertLevel, int alertDescription, WTLSMachine* wtls_machine) { wtls_Payload* alertPayload; wtls_PDU* alertPDU; Octstr* packedAlert; Msg* msg = NULL; alertPDU = (wtls_PDU*) wtls_pdu_create(Alert_PDU); alertPDU->u.alert.level = alertLevel; alertPDU->u.alert.desc = alertDescription; /* Here's where we should get the current checksum from the wtls_machine */ alertPDU->u.alert.chksum = 0; /* Pack the PDU */ msg = msg_create(wdp_datagram); add_wtls_address(msg, wtls_machine); /* Pack the message */ alertPayload = wtls_pdu_pack(alertPDU, wtls_machine); packedAlert = (Octstr*) wtls_payload_pack(alertPayload); msg->wdp_datagram.user_data = packedAlert; /* And destroy the structure */ wtls_payload_destroy(alertPayload); alertPayload = NULL; /* Send it off */ write_to_bearerbox(msg); } void clear_queuedpdus(WTLSMachine* wtls_machine) { } void add_pdu(WTLSMachine* wtls_machine, wtls_PDU* pduToAdd) { int currentLength; wtls_Payload* payloadToAdd; Octstr* packedPDU; /* Check to see if we've already allocated some memory for the list */ if (wtls_machine->packet_to_send == NULL) { wtls_machine->packet_to_send = octstr_create(""); } /* Pack and encrypt the pdu */ payloadToAdd = wtls_pdu_pack(pduToAdd, wtls_machine); /* If the pdu is a Handshake pdu, append the Octstr to our wtls_machine's exchanged_handshakes Octstr */ packedPDU = wtls_payload_pack(payloadToAdd); /* Add it to our list */ currentLength = octstr_len(wtls_machine->packet_to_send); octstr_insert(wtls_machine->packet_to_send, packedPDU, currentLength); } /* * Send the pdu_to_send list to the destination specified by the address in the machine * structure. Don't return anything, handle all errors internally. */ void send_queuedpdus(WTLSMachine* wtls_machine) { Msg* msg = NULL; gw_assert(wtls_machine->packet_to_send != NULL); /* Pack the PDU */ msg = msg_create(wdp_datagram); add_wtls_address(msg, wtls_machine); msg->wdp_datagram.user_data = octstr_duplicate(wtls_machine->packet_to_send); /* Send it off */ write_to_bearerbox(msg); /* Destroy our copy of the sent string */ octstr_destroy(wtls_machine->packet_to_send); wtls_machine->packet_to_send = NULL; } /* * Add address from state machine. */ void add_wtls_address(Msg *msg, WTLSMachine *wtls_machine){ debug("wap.wtls", 0, "adding address"); msg->wdp_datagram.source_address = octstr_duplicate(wtls_machine->addr_tuple->local->address); msg->wdp_datagram.source_port = wtls_machine->addr_tuple->local->port; msg->wdp_datagram.destination_address = octstr_duplicate(wtls_machine->addr_tuple->remote->address); msg->wdp_datagram.destination_port = wtls_machine->addr_tuple->remote->port; } /***************************************************************************** * * INTERNAL FUNCTIONS: * */ static void main_thread(void *arg) { WTLSMachine *sm; WAPEvent *e; while (wtls_run_status == running && (e = list_consume(wtls_queue)) != NULL) { sm = wtls_machine_find_or_create(e); if (sm == NULL) wap_event_destroy(e); else wtls_event_handle(sm, e); } } /* * Give the name of a WTLS Server state in a readable form. */ static unsigned char *name_wtls_state(int s){ switch (s){ #define STATE_NAME(state) case state: return #state; #define ROW(state, event, condition, action, new_state) #include "wtls_state-decl.h" default: return "unknown state"; } } /* * Feed an event to a WTP responder state machine. Handle all errors yourself, * do not report them to the caller. Note: Do not put {}s of the else block * inside the macro definition. */ static void wtls_event_handle(WTLSMachine *wtls_machine, WAPEvent *event){ debug("wap.wtls", 0, "WTLS: wtls_machine %ld, state %s, event %s.", wtls_machine->mid, name_wtls_state(wtls_machine->state), wap_event_name(event->type)); /* for T_Unitdata_Ind PDUs */ if(event->type == T_Unitdata_Ind) { /* if encryption: decrypt all pdus in the list */ if( wtls_machine->encrypted ) { wtls_decrypt_pdu_list(wtls_machine, event->u.T_Unitdata_Ind.pdu_list); } /* add all handshake data to wtls_machine->handshake_data */ //add_all_handshake_data(wtls_machine, event->u.T_Unitdata_Ind.pdu_list); } #define STATE_NAME(state) #define ROW(wtls_state, event_type, condition, action, next_state) \ if (wtls_machine->state == wtls_state && \ event->type == event_type && \ (condition)) { \ action \ wtls_machine->state = next_state; \ debug("wap.wtls", 0, "WTLS %ld: New state %s", wtls_machine->mid, #next_state); \ } else #include "wtls_state-decl.h" { error(0, "WTLS: handle_event: unhandled event!"); debug("wap.wtls", 0, "WTLS: handle_event: Unhandled event was:"); wap_event_destroy(event); return; } if (event != NULL) { wap_event_destroy(event); } if (wtls_machine->state == NULL_STATE) wtls_machine_destroy(wtls_machine); } /* * Checks whether wtls machines data structure includes a specific machine. * The machine in question is identified with with source and destination * address and port. */ static WTLSMachine *wtls_machine_find_or_create(WAPEvent *event) { WTLSMachine *wtls_machine = NULL; long mid; WAPAddrTuple *tuple; tuple = NULL; mid = -1; debug("wap.wtls",0, "event->type = %d", event->type); /* Get the address that this PDU came in from */ switch (event->type) { case T_Unitdata_Ind: case T_DUnitdata_Ind: tuple = event->u.T_Unitdata_Ind.addr_tuple; break; case SEC_Create_Request_Req: case SEC_Terminate_Req: case SEC_Exception_Req: case SEC_Create_Res: case SEC_Exchange_Req: case SEC_Commit_Req: case SEC_Unitdata_Req: tuple = event->u.T_Unitdata_Ind.addr_tuple; break; default: debug("wap.wtls", 0, "WTLS: wtls_machine_find_or_create:" "unhandled event (1)"); wap_event_dump(event); return NULL; } /* Either the address or the machine id must be available at this point */ gw_assert(tuple != NULL || mid != -1); /* Look for the machine owning this address */ wtls_machine = wtls_machine_find(tuple, mid); /* Oh well, we didn't find one. We'll create one instead, provided it meets certain criteria */ if (wtls_machine == NULL){ switch (event->type){ case SEC_Create_Request_Req: /* State NULL, case 1 */ debug("wap.wtls",0,"WTLS: received a SEC_Create_Request_Req, and don't know what to do with it..."); /* Create and dispatch a T_Unitdata_Req containing a HelloRequest */ /* And there's no need to do anything else, 'cause we return to state NULL */ break; case T_Unitdata_Ind: case T_DUnitdata_Ind: /* State NULL, case 3 */ /* if (wtls_event_type(event) == Alert_No_Renegotiation) { */ /* Create and dispatch a SEC_Exception_Ind event */ /* debug("wap.wtls",0,"WTLS: received an Alert_no_Renegotiation; just dropped it."); */ /* And there's no need to do anything else, 'cause we return to state NULL */ /* break; */ /* } else */ /* if (event->u.T_Unitdata_Ind == ClientHello) { */ /* State NULL, case 2 */ wtls_machine = wtls_machine_create(tuple); /* And stick said event into machine, which should push us into state CREATING after a SEC_Create_Ind */ /* } */ break; default: error(0, "WTLS: wtls_machine_find_or_create:" " unhandled event (2)"); wap_event_dump(event); break; } } return wtls_machine; } static int is_wanted_wtls_machine(void *a, void *b) { machine_pattern *pat; WTLSMachine *m; m = a; pat = b; if (m->mid == pat->mid) return 1; if (pat->mid != -1) return 0; return wap_addr_tuple_same(m->addr_tuple, pat->tuple); } static WTLSMachine *wtls_machine_find(WAPAddrTuple *tuple, long mid) { machine_pattern pat; WTLSMachine *m; pat.tuple = tuple; pat.mid = mid; m = list_search(wtls_machines, &pat, is_wanted_wtls_machine); return m; } static WTLSMachine *wtls_machine_create(WAPAddrTuple *tuple) { WTLSMachine *wtls_machine; wtls_machine = gw_malloc(sizeof(WTLSMachine)); #define MACHINE(field) field #define ENUM(name) wtls_machine->name = NULL_STATE; #define ADDRTUPLE(name) wtls_machine->name = NULL; #define INTEGER(name) wtls_machine->name = 0; #define OCTSTR(name) wtls_machine->name = NULL; #define PDULIST(name) wtls_machine->name = NULL; #include "wtls_machine-decl.h" list_append(wtls_machines, wtls_machine); wtls_machine->mid = counter_increase(wtls_machine_id_counter); wtls_machine->addr_tuple = wap_addr_tuple_duplicate(tuple); wtls_machine->handshake_data = octstr_create(""); debug("wap.wtls", 0, "WTLS: Created WTLSMachine %p (%ld)", (void *) wtls_machine, wtls_machine->mid); return wtls_machine; } /* * Destroys a WTLSMachine. Assumes it is safe to do so. Assumes it has * already been deleted from the machines list. */ static void wtls_machine_destroy(void * p) { WTLSMachine *wtls_machine; wtls_machine = p; debug("wap.wtls", 0, "WTLS: Destroying WTLSMachine %p (%ld)", (void *) wtls_machine, wtls_machine->mid); list_delete_equal(wtls_machines, wtls_machine); #define MACHINE(field) field #define ENUM(name) wtls_machine->name = NULL_STATE; #define ADDRTUPLE(name) wap_addr_tuple_destroy(wtls_machine->name); #define INTEGER(name) wtls_machine->name = 0; #define OCTSTR(name) octstr_destroy(wtls_machine->name); #define PDULIST(name) wtls_machine->name = NULL; #include "wtls_machine-decl.h" gw_free(wtls_machine); } /* * Create a TR-Invoke.ind event. static WAPEvent *create_tr_invoke_ind(WTPRespMachine *sm, Octstr *user_data) { WAPEvent *event; event = wap_event_create(TR_Invoke_Ind); event->u.TR_Invoke_Ind.ack_type = sm->u_ack; event->u.TR_Invoke_Ind.user_data = octstr_duplicate(user_data); event->u.TR_Invoke_Ind.tcl = sm->tcl; event->u.TR_Invoke_Ind.addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple); event->u.TR_Invoke_Ind.handle = sm->mid; return event; } */ /* * Create a TR-Result.cnf event. static WAPEvent *create_tr_result_cnf(WTPRespMachine *sm) { WAPEvent *event; event = wap_event_create(TR_Result_Cnf); event->u.TR_Result_Cnf.addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple); event->u.TR_Result_Cnf.handle = sm->mid; return event; } */ /* * Creates TR-Abort.ind event from a responder state machine. static WAPEvent *create_tr_abort_ind(WTPRespMachine *sm, long abort_reason) { WAPEvent *event; event = wap_event_create(TR_Abort_Ind); event->u.TR_Abort_Ind.abort_code = abort_reason; event->u.TR_Abort_Ind.addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple); event->u.TR_Abort_Ind.handle = sm->mid; return event; } */ static int wtls_machine_has_mid(void *a, void *b) { WTLSMachine *sm; long mid; sm = a; mid = *(long *) b; return sm->mid == mid; } static WTLSMachine *find_wtls_machine_using_mid(long mid) { return list_search(wtls_machines, &mid, wtls_machine_has_mid); } /* Used for list searches */ static int match_handshake_type(void* item, void* pattern) { wtls_Payload* matchingPayload; int type; int retrievedType; matchingPayload = (wtls_Payload*) item; type = (int) pattern; retrievedType = octstr_get_char(matchingPayload->data, 0); if (matchingPayload->type == Handshake_PDU && retrievedType == type) { return 1; } else { return 0; } } static int match_pdu_type(void* item, void* pattern) { wtls_Payload* matchingPayload; int type; matchingPayload = (wtls_Payload*) item; type = (int) pattern; if (matchingPayload->type == type) { return 1; } else { return 0; } } #endif