/* * Create a response packet from a request, referencing the requestors * message identifier. */ Packet *packet_create_response(Packet *request) { Packet *response = NULL; Tlv method, requestId; BOOL success = FALSE; PacketTlvType responseType; if (packet_get_type(request) == PACKET_TLV_TYPE_PLAIN_REQUEST) responseType = PACKET_TLV_TYPE_PLAIN_RESPONSE; else responseType = PACKET_TLV_TYPE_RESPONSE; do { // Get the request TLV's method if (packet_get_tlv_string(request, TLV_TYPE_METHOD, &method) != ERROR_SUCCESS) break; // Try to allocate a response packet if (!(response = packet_create(responseType, (PCHAR)method.buffer))) break; // Get the request TLV's request identifier if (packet_get_tlv_string(request, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) break; // Add the request identifier to the packet packet_add_tlv_string(response, TLV_TYPE_REQUEST_ID, (PCHAR)requestId.buffer); success = TRUE; } while (0); // Cleanup on failure if (!success) { if (response) packet_destroy(response); response = NULL; } return response; }
/* * Get the value of a string TLV */ PCHAR packet_get_tlv_value_string(Packet *packet, TlvType type) { Tlv stringTlv; PCHAR string = NULL; if (packet_get_tlv_string(packet, type, &stringTlv) == ERROR_SUCCESS) string = (PCHAR)stringTlv.buffer; return string; }
/*! * @brief Call the register completion handler(s) for the given request identifier. * @details Only those handlers that match the given request are executed. * @param remote Pointer to the \c Remote instance for this call. * @param response Pointer to the response \c Packet. * @param requestId ID of the request to execute the completion handlers of. * @return Indication of success or failure. * @retval ERROR_NOT_FOUND Unable to find any matching completion handlers for the request. * @retval ERROR_SUCCESS Execution was successful. */ DWORD packet_call_completion_handlers( Remote *remote, Packet *response, LPCSTR requestId ) { PacketCompletionRoutineEntry *current; DWORD result = packet_get_tlv_value_uint(response, TLV_TYPE_RESULT); DWORD matches = 0; Tlv methodTlv; LPCSTR method = NULL; // Get the method associated with this packet if (packet_get_tlv_string(response, TLV_TYPE_METHOD, &methodTlv) == ERROR_SUCCESS) { method = (LPCSTR)methodTlv.buffer; } // Enumerate the completion routine list for (current = packetCompletionRoutineList; current; current = current->next) { // Does the request id of the completion entry match the packet's request // id? if (strcmp(requestId, current->requestId)) { continue; } // Call the completion routine current->handler.routine(remote, response, current->handler.context, method, result); // Increment the number of matched handlers matches++; } if (matches) { packet_remove_completion_handler(requestId); } return (matches > 0) ? ERROR_SUCCESS : ERROR_NOT_FOUND; }
/*! * @brief Create a response packet from a request. * @details Create a response packet from a request, referencing the requestors * message identifier. * @param request The request \c Packet to build a response for. * @return Pointer to a new \c Packet. */ Packet *packet_create_response(Packet *request) { Packet *response = NULL; Tlv method, requestId; BOOL success = FALSE; PacketTlvType responseType; if (packet_get_type(request) == PACKET_TLV_TYPE_PLAIN_REQUEST) { responseType = PACKET_TLV_TYPE_PLAIN_RESPONSE; } else { responseType = PACKET_TLV_TYPE_RESPONSE; } do { // Get the request TLV's method if (packet_get_tlv_string(request, TLV_TYPE_METHOD, &method) != ERROR_SUCCESS) { break; } // Try to allocate a response packet if (!(response = packet_create(responseType, (PCHAR)method.buffer))) { break; } // Get the request TLV's request identifier if (packet_get_tlv_string(request, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { break; } // Add the request identifier to the packet packet_add_tlv_string(response, TLV_TYPE_REQUEST_ID, (PCHAR)requestId.buffer); // If the packet that is being handled is considered local, then we // associate the response with the request so that it can be handled // locally (and vice versa) if (request->local) { request->partner = response; response->partner = request; } success = TRUE; } while (0); // Cleanup on failure if (!success) { if (response) { packet_destroy(response); } response = NULL; } return response; }
/* * Transmit and destroy a packet */ DWORD packet_transmit(Remote *remote, Packet *packet, PacketRequestCompletion *completion) { CryptoContext *crypto; Tlv requestId; DWORD res; DWORD idx; #ifdef _UNIX int local_error = -1; #endif // If the packet does not already have a request identifier, create // one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid) - 1] = 0; for (index = 0; index < sizeof(rid) - 1; index++) rid[index] = (rand() % 0x5e) + 0x21; packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) packet_add_completion_handler((LPCSTR)requestId.buffer, completion); // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } idx = 0; while( idx < sizeof(packet->header)) { // Transmit the packet's header (length, type) res = SSL_write( remote->ssl, (LPCSTR)(&packet->header) + idx, sizeof(packet->header) - idx ); if(res <= 0) { dprintf("transmit header failed with return %d at index %d\n", res, idx); break; } idx += res; } if(res < 0) break; idx = 0; while( idx < packet->payloadLength) { // Transmit the packet's payload (length, type) res = SSL_write( remote->ssl, packet->payload + idx, packet->payloadLength - idx ); if(res < 0) break; idx += res; } if(res < 0) { dprintf("transmit header failed with return %d at index %d\n", res, idx); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); return res; }
/*! * @brief Transmit a packet via SSL _and_ destroy it. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the \c Packet that is to be sent. * @param completion Pointer to the completion routines to process. * @return An indication of the result of processing the transmission request. * @remark This uses an SSL-encrypted TCP channel, and does not imply the use of HTTPS. */ DWORD packet_transmit_via_ssl(Remote* remote, Packet* packet, PacketRequestCompletion* completion) { CryptoContext* crypto; Tlv requestId; DWORD res; DWORD idx; TcpTransportContext* ctx = (TcpTransportContext*)remote->transport->ctx; lock_acquire(remote->lock); // If the packet does not already have a request identifier, create one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid)-1] = 0; for (index = 0; index < sizeof(rid)-1; index++) { rid[index] = (rand() % 0x5e) + 0x21; } packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) { packet_add_completion_handler((LPCSTR)requestId.buffer, completion); } // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } dprintf("[PACKET] New xor key for sending"); packet->header.xor_key = rand_xor_key(); // before transmission, xor the whole lot, starting with the body xor_bytes(packet->header.xor_key, (LPBYTE)packet->payload, packet->payloadLength); // then the header xor_bytes(packet->header.xor_key, (LPBYTE)&packet->header.length, 8); // be sure to switch the xor header before writing packet->header.xor_key = htonl(packet->header.xor_key); idx = 0; while (idx < sizeof(packet->header)) { // Transmit the packet's header (length, type) res = SSL_write( ctx->ssl, (LPCSTR)(&packet->header) + idx, sizeof(packet->header) - idx ); if (res <= 0) { dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx); break; } idx += res; } if (res < 0) { break; } idx = 0; while (idx < packet->payloadLength) { // Transmit the packet's payload (length, type) res = SSL_write( ctx->ssl, packet->payload + idx, packet->payloadLength - idx ); if (res < 0) { break; } idx += res; } if (res < 0) { dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); lock_release(remote->lock); return res; }
/*! * @brief Process a single command in a seperate thread of execution. * @param thread Pointer to the thread to execute. * @return Result of processing. */ 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; }
/* * Transmit and destroy a packet over HTTP(S) */ DWORD packet_transmit_via_http(Remote *remote, Packet *packet, PacketRequestCompletion *completion) { CryptoContext *crypto; Tlv requestId; DWORD res; #ifdef _UNIX int local_error = -1; #endif lock_acquire( remote->lock ); // If the packet does not already have a request identifier, create one for it if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,&requestId) != ERROR_SUCCESS) { DWORD index; CHAR rid[32]; rid[sizeof(rid) - 1] = 0; for (index = 0; index < sizeof(rid) - 1; index++) rid[index] = (rand() % 0x5e) + 0x21; packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid); } do { // If a completion routine was supplied and the packet has a request // identifier, insert the completion routine into the list if ((completion) && (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID, &requestId) == ERROR_SUCCESS)) packet_add_completion_handler((LPCSTR)requestId.buffer, completion); // If the endpoint has a cipher established and this is not a plaintext // packet, we encrypt if ((crypto = remote_get_cipher(remote)) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) && (packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE)) { ULONG origPayloadLength = packet->payloadLength; PUCHAR origPayload = packet->payload; // Encrypt if ((res = crypto->handlers.encrypt(crypto, packet->payload, packet->payloadLength, &packet->payload, &packet->payloadLength)) != ERROR_SUCCESS) { SetLastError(res); break; } // Destroy the original payload as we no longer need it free(origPayload); // Update the header length packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader)); } #ifdef _WIN32 dprintf("Transmitting packet of length %d to remote", packet->payloadLength); res = packet_transmit_via_http_wininet(remote, packet, completion); #else // XXX: Implement non-windows HTTP delivery #endif if(res < 0) { dprintf("[PACKET] transmit failed with return %d\n", res); break; } SetLastError(ERROR_SUCCESS); } while (0); res = GetLastError(); // Destroy the packet packet_destroy(packet); lock_release( remote->lock ); return res; }