#include "common.h" // Local remote request implementors extern DWORD remote_request_core_console_write(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_open(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_write(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_read(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_close(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_seek(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_eof(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_tell(Remote *remote, Packet *packet); extern DWORD remote_request_core_channel_interact(Remote *remote, Packet *packet); extern DWORD remote_request_core_crypto_negotiate(Remote *remote, Packet *packet); extern DWORD remote_request_core_shutdown(Remote *remote, Packet *packet); extern DWORD remote_request_core_migrate(Remote *remote, Packet *packet); // Local remote response implementors extern DWORD remote_response_core_console_write(Remote *remote, Packet *packet); extern DWORD remote_response_core_channel_open(Remote *remote, Packet *packet); extern DWORD remote_response_core_channel_close(Remote *remote, Packet *packet); DWORD remote_request_core_console_write(Remote *remote, Packet *packet) { return ERROR_SUCCESS; } DWORD remote_response_core_console_write(Remote *remote, Packet *packet) { return ERROR_SUCCESS; } /* * Base RPC dispatch table */ Command commands[] = { /* * Core commands */ // Console commands { "core_console_write", { remote_request_core_console_write, { TLV_META_TYPE_STRING }, 1 | ARGUMENT_FLAG_REPEAT }, { remote_response_core_console_write, { 0 }, 0 }, }, // Native Channel commands { "core_channel_open", { remote_request_core_channel_open, { 0 }, 0 }, { remote_response_core_channel_open, { 0 }, 0 }, }, { "core_channel_write", { remote_request_core_channel_write, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, { "core_channel_close", { remote_request_core_channel_close, { 0 }, 0 }, { remote_response_core_channel_close, { 0 }, 0 }, }, // Buffered/Pool channel commands { "core_channel_read", { remote_request_core_channel_read, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, // Pool channel commands { "core_channel_seek", { remote_request_core_channel_seek, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, { "core_channel_eof", { remote_request_core_channel_eof, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, { "core_channel_tell", { remote_request_core_channel_tell, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, // Soon to be deprecated { "core_channel_interact", { remote_request_core_channel_interact, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, // Crypto { "core_crypto_negotiate", { remote_request_core_crypto_negotiate, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, // Migration { "core_migrate", { remote_request_core_migrate, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, // Shutdown { "core_shutdown", { remote_request_core_shutdown, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER }, }, // Terminator { NULL, { NULL, { 0 }, 0 }, { NULL, { 0 }, 0 }, }, }; // Dynamically registered command extensions Command *extension_commands = NULL; /* * Dynamically register a custom command handler */ DWORD command_register(Command *command) { Command *newCommand; dprintf("Registering a new command (%s)...", command->method); if (!(newCommand = (Command *)malloc(sizeof(Command)))) return ERROR_NOT_ENOUGH_MEMORY; dprintf("Allocated memory..."); memcpy(newCommand, command, sizeof(Command)); dprintf("Setting new command..."); if (extension_commands) extension_commands->prev = newCommand; dprintf("Fixing next/prev..."); newCommand->next = extension_commands; newCommand->prev = NULL; extension_commands = newCommand; dprintf("Done..."); return ERROR_SUCCESS; } /* * Dynamically deregister a custom command handler */ DWORD command_deregister(Command *command) { Command *current, *prev; DWORD res = ERROR_NOT_FOUND; // Search the extension list for the command for (current = extension_commands, prev = NULL; current; prev = current, current = current->next) { if (strcmp(command->method, current->method)) continue; if (prev) prev->next = current->next; else extension_commands = current->next; if (current->next) current->next->prev = prev; // Deallocate it free(current); res = ERROR_SUCCESS; break; } return res; } /* * A list of all command threads currenlty executing. */ LIST * commandThreadList = NULL; /* * Block untill all running command threads have finished. */ VOID command_join_threads( VOID ) { while( list_count( commandThreadList ) > 0 ) { THREAD * thread = (THREAD *)list_get( commandThreadList, 0 ); if( thread ) thread_join( thread ); } } /* * Crude method of throttling the ammount of concurrent command * threads we allow in the system at a given time. */ /* VOID command_throtle( int maxthreads ) { while( list_count( commandThreadList ) >= maxthreads ) { Sleep( 250 ); } } */ #ifndef _WIN32 /* * Reap child zombie threads on linux 2.4 (before NPTL) * each thread appears as a process and pthread_join don't necessarily reap it * threads are created using the clone syscall, so use special __WCLONE flag in waitpid */ VOID reap_zombie_thread(void * param) { while(1) { waitpid(-1, NULL, __WCLONE); // on 2.6 kernels, don't chew 100% CPU usleep(500000); } } #endif /* * Process a single command in a seperate thread of execution. */ DWORD THREADCALL command_process_thread( THREAD * thread ) { DWORD index = 0; DWORD result = ERROR_SUCCESS; Tlv methodTlv = {0}; Tlv requestIdTlv = {0}; PCHAR method = NULL; PCHAR requestId = NULL; Command * current = NULL; Remote * remote = NULL; Packet * packet = NULL; if( thread == NULL ) return ERROR_INVALID_HANDLE; remote = (Remote *)thread->parameter1; if( remote == NULL ) return ERROR_INVALID_HANDLE; packet = (Packet *)thread->parameter2; if( packet == NULL ) return ERROR_INVALID_DATA; if( commandThreadList == NULL ) { commandThreadList = list_create(); if( commandThreadList == NULL ) return ERROR_INVALID_HANDLE; #ifndef _WIN32 pthread_t tid; pthread_create(&tid, NULL, reap_zombie_thread, NULL); dprintf("reap_zombie_thread created, thread_id : 0x%x",tid); #endif } list_add( commandThreadList, thread ); __try { do { // Extract the method result = packet_get_tlv_string( packet, TLV_TYPE_METHOD, &methodTlv ); if( result != ERROR_SUCCESS ) break; dprintf( "[COMMAND] Processing method %s", methodTlv.buffer ); #ifdef _WIN32 // Impersonate the thread token if needed (only on Windows) if(remote->hServerToken != remote->hThreadToken) { if(! ImpersonateLoggedOnUser(remote->hThreadToken)) { dprintf( "[COMMAND] Failed to impersonate thread token (%s) (%u)", methodTlv.buffer, GetLastError()); } } #endif // Get the request identifier if the packet has one. result = packet_get_tlv_string( packet, TLV_TYPE_REQUEST_ID, &requestIdTlv ); if( result == ERROR_SUCCESS ) requestId = (PCHAR)requestIdTlv.buffer; method = (PCHAR)methodTlv.buffer; result = ERROR_NOT_FOUND; // Try to find a match in the dispatch type for( index = 0, result = ERROR_NOT_FOUND ; result == ERROR_NOT_FOUND && commands[index].method ; index++ ) { if( strcmp( commands[index].method, method ) ) continue; // Call the base handler result = command_call_dispatch( &commands[index], remote, packet ); } // Regardless of error code, try to see if someone has overriden a base handler for( current = extension_commands, result = ERROR_NOT_FOUND ; result == ERROR_NOT_FOUND && current && current->method ; current = current->next ) { if( strcmp( current->method, method ) ) continue; // Call the custom handler result = command_call_dispatch( current, remote, packet ); } dprintf("[COMMAND] Calling completion handlers..."); // Finally, call completion routines for the provided identifier if( ((packet_get_type(packet) == PACKET_TLV_TYPE_RESPONSE) || (packet_get_type(packet) == PACKET_TLV_TYPE_PLAIN_RESPONSE)) && (requestId)) packet_call_completion_handlers( remote, packet, requestId ); // If we get here, we're successful. result = ERROR_SUCCESS; } while( 0 ); } __except( EXCEPTION_EXECUTE_HANDLER ) { dprintf("[COMMAND] Exception hit in command thread 0x%08X!", thread ); } packet_destroy( packet ); if( list_remove( commandThreadList, thread ) ) thread_destroy( thread ); return ERROR_SUCCESS; } /* * Process a single command */ /* DWORD command_process_remote(Remote *remote, Packet *inPacket) { DWORD res = ERROR_SUCCESS, index; Tlv methodTlv, requestIdTlv; Packet *localPacket = NULL; PCHAR method, requestId = NULL; Command *current; do { // If no packet was providied, try to receive one. if (!inPacket) { if ((res = packet_receive(remote, &localPacket)) != ERROR_SUCCESS) break; else inPacket = localPacket; } // Extract the method if ((packet_get_tlv_string(inPacket, TLV_TYPE_METHOD, &methodTlv) != ERROR_SUCCESS)) break; dprintf("Processing method %s", methodTlv.buffer); // Get the request identifier if the packet has one. if (packet_get_tlv_string(inPacket, TLV_TYPE_REQUEST_ID, &requestIdTlv) == ERROR_SUCCESS) requestId = (PCHAR)requestIdTlv.buffer; method = (PCHAR)methodTlv.buffer; res = ERROR_NOT_FOUND; // Try to find a match in the dispatch type for (index = 0, res = ERROR_NOT_FOUND; res = ERROR_NOT_FOUND && commands[index].method; index++) { if (strcmp(commands[index].method, method)) continue; // Call the base handler res = command_call_dispatch(&commands[index], remote, inPacket); } // Regardless of error code, try to see if someone has overriden // a base handler for (current = extension_commands, res = ERROR_NOT_FOUND; res == ERROR_NOT_FOUND && current && current->method; current = current->next) { if (strcmp(current->method, method)) continue; // Call the custom handler res = command_call_dispatch(current, remote, inPacket); } dprintf("Calling completion handlers..."); // Finally, call completion routines for the provided identifier if (((packet_get_type(inPacket) == PACKET_TLV_TYPE_RESPONSE) || (packet_get_type(inPacket) == PACKET_TLV_TYPE_PLAIN_RESPONSE)) && (requestId)) packet_call_completion_handlers(remote, inPacket, requestId); // If we get here, we're successful. res = ERROR_SUCCESS; } while (0); if (localPacket) packet_destroy(localPacket); return res; }*/ /* * Process incoming commands, calling dispatch tables appropriately */ /* DWORD command_process_remote_loop(Remote *remote) { DWORD res = ERROR_SUCCESS; Packet *packet; while ((res = packet_receive(remote, &packet)) == ERROR_SUCCESS) { res = command_process_remote(remote, packet); // Destroy the packet packet_destroy(packet); // If a command returned exit, we shall return. if (res == ERROR_INSTALL_USEREXIT) break; } return res; } */ /* * Call the dispatch routine for a given command */ DWORD command_call_dispatch(Command *command, Remote *remote, Packet *packet) { DWORD res; // Validate the arguments, if requested. Always make sure argument // lengths are sane. if ((res = command_validate_arguments(command, packet)) != ERROR_SUCCESS) return res; switch (packet_get_type(packet)) { case PACKET_TLV_TYPE_REQUEST: case PACKET_TLV_TYPE_PLAIN_REQUEST: if (command->request.handler) res = command->request.handler(remote, packet); break; case PACKET_TLV_TYPE_RESPONSE: case PACKET_TLV_TYPE_PLAIN_RESPONSE: if (command->response.handler) res = command->response.handler(remote, packet); break; default: res = ERROR_NOT_FOUND; break; } return res; } /* * Validate command arguments */ DWORD command_validate_arguments(Command *command, Packet *packet) { PacketDispatcher *dispatcher = NULL; PacketTlvType type = packet_get_type(packet); DWORD res = ERROR_SUCCESS, packetIndex, commandIndex; Tlv current; // Select the dispatcher table if ((type == PACKET_TLV_TYPE_RESPONSE) || (type == PACKET_TLV_TYPE_PLAIN_RESPONSE)) dispatcher = &command->response; else dispatcher = &command->request; // Enumerate the arguments, validating the meta types of each for (commandIndex = 0, packetIndex = 0; ((packet_enum_tlv(packet, packetIndex, TLV_TYPE_ANY, ¤t) == ERROR_SUCCESS) && (res == ERROR_SUCCESS)); commandIndex++, packetIndex++) { TlvMetaType tlvMetaType; // Check to see if we've reached the end of the command arguments if ((dispatcher->numArgumentTypes) && (commandIndex == (dispatcher->numArgumentTypes & ARGUMENT_FLAG_MASK))) { // If the repeat flag is set, reset the index if (commandIndex & ARGUMENT_FLAG_REPEAT) commandIndex = 0; else break; } // Make sure the argument is at least one of the meta types tlvMetaType = packet_get_tlv_meta(packet, ¤t); // Validate argument meta types switch (tlvMetaType) { case TLV_META_TYPE_STRING: if (packet_is_tlv_null_terminated(packet, ¤t) != ERROR_SUCCESS) res = ERROR_INVALID_PARAMETER; break; default: break; } if ((res != ERROR_SUCCESS) && (commandIndex < dispatcher->numArgumentTypes)) break; } return res; }