From 69d636860d29962d9276637970bf909a73deb573 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 2 Aug 2013 13:35:52 -0700 Subject: [PATCH] Add path normalization. --- src/protocols/rdp/guac_rdpdr/rdpdr_fs.c | 131 +++++++++++++++--- src/protocols/rdp/guac_rdpdr/rdpdr_fs.h | 22 +++ .../rdp/guac_rdpdr/rdpdr_fs_messages.c | 63 +++++---- 3 files changed, 164 insertions(+), 52 deletions(-) diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs.c index 31c50f1e..d1b98f92 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs.c +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs.c @@ -61,11 +61,12 @@ #include /** - * Translates an absolute Windows path to an absolute path which is within the "drive path" - * specified in the connection settings. + * Translates an absolute Windows virtual_path to an absolute virtual_path + * which is within the "drive virtual_path" specified in the connection + * settings. */ -static char* __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device, - const char* path, char* path_buffer) { +static void __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device, + const char* virtual_path, char* real_path) { /* Get drive path */ rdp_guac_client_data* client_data = (rdp_guac_client_data*) device->rdpdr->client->data; @@ -82,7 +83,7 @@ static char* __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device, break; /* Copy character */ - *(path_buffer++) = c; + *(real_path++) = c; } @@ -90,29 +91,21 @@ static char* __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device, for (; idata; - char path_buffer[GUAC_RDPDR_FS_MAX_PATH]; + char real_path[GUAC_RDPDR_FS_MAX_PATH]; + char normalized_path[GUAC_RDPDR_FS_MAX_PATH]; struct stat file_stat; int fd; @@ -192,15 +186,19 @@ int guac_rdpdr_fs_open(guac_rdpdr_device* device, const char* path, } - /* If path invalid, return no such file */ - if (__guac_rdpdr_fs_translate_path(device, path, path_buffer) == NULL) + /* Normalize path, return no-such-file if invalid */ + if (guac_rdpdr_fs_normalize_path(path, normalized_path)) return GUAC_RDPDR_FS_ENOENT; - guac_client_log_info(device->rdpdr->client, "Path \"%s\" translated to \"%s\"", - path, path_buffer); + /* Translate normalized path to real path */ + __guac_rdpdr_fs_translate_path(device, normalized_path, real_path); + + guac_client_log_info(device->rdpdr->client, + "Path virtual=\"%s\" -> normalized=\"%s\", real=\"%s\"", + path, normalized_path, real_path); /* Open file */ - fd = open(path_buffer, flags, mode); + fd = open(real_path, flags, mode); if (fd == -1) return GUAC_RDPDR_FS_ENOENT; @@ -209,6 +207,7 @@ int guac_rdpdr_fs_open(guac_rdpdr_device* device, const char* path, file = &(data->files[file_id]); file->fd = fd; file->dir = NULL; + file->absolute_path = strdup(path); /* Attempt to pull file information */ if (fstat(fd, &file_stat) == 0) { @@ -231,7 +230,7 @@ int guac_rdpdr_fs_open(guac_rdpdr_device* device, const char* path, else { guac_client_log_info(device->rdpdr->client, "Unable to read information for \"%s\"", - path_buffer); + real_path); /* Init information to 0, lacking any alternative */ file->size = 0; @@ -267,6 +266,9 @@ void guac_rdpdr_fs_close(guac_rdpdr_device* device, int file_id) { /* Close file */ close(file->fd); + /* Free name */ + free(file->absolute_path); + /* Free ID back to pool */ guac_pool_free_int(data->file_id_pool, file_id); data->open_files--; @@ -302,3 +304,86 @@ const char* guac_rdpdr_fs_read_dir(guac_rdpdr_device* device, int file_id) { } +int guac_rdpdr_fs_normalize_path(const char* path, char* abs_path) { + + int i; + int path_depth = 0; + char path_component_data[GUAC_RDPDR_FS_MAX_PATH]; + const char* path_components[64]; + + const char** current_path_component = &(path_components[0]); + const char* current_path_component_data = &(path_component_data[0]); + + /* If original path is not absolute, normalization fails */ + if (path[0] != '\\' && path[0] != '/') + return 1; + + /* Skip past leading slash */ + path++; + + /* Copy path into component data for parsing */ + strncpy(path_component_data, path, GUAC_RDPDR_FS_MAX_PATH-1); + + /* Find path components within path */ + for (i=0; i 0) + path_depth--; + } + + /* Otherwise, if component not current directory, add to list */ + else if (strcmp(current_path_component_data, ".") != 0) + path_components[path_depth++] = current_path_component_data; + + /* If end of string, stop */ + if (c == 0) + break; + + /* Update start of next component */ + current_path_component_data = &(path_component_data[i+1]); + + } /* end if separator */ + + } /* end for each character */ + + /* If no components, the path could not be parsed */ + if (path_depth == 0) + return 1; + + /* Ensure last component is null-terminated */ + path_component_data[i] = 0; + + /* Convert components back into path */ + for (; path_depth > 0; path_depth--) { + + const char* filename = *(current_path_component++); + + /* Add separator */ + *(abs_path++) = '\\'; + + /* Copy string */ + while (*filename != 0) + *(abs_path++) = *(filename++); + + } + + /* Terminate absolute path */ + *(abs_path++) = 0; + return 0; + +} + +int guac_rdpdr_fs_convert_path(const char* parent, const char* rel_path, char* abs_path) { + /* STUB */ + return 0; +} + diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs.h index bee64585..42aa3cd6 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs.h +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs.h @@ -160,6 +160,11 @@ typedef enum guac_rdpdr_fs_file_type { */ typedef struct guac_rdpdr_fs_file { + /** + * The absolute path, including filename, of this file. + */ + char* absolute_path; + /** * The type of this file. */ @@ -232,6 +237,12 @@ typedef struct guac_rdpdr_fs_data { */ void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr); +/** + * Converts the given relative path to an absolute path based on the given + * parent path. If the path cannot be converted, non-zero is returned. + */ +int guac_rdpdr_fs_convert_path(const char* parent, const char* rel_path, char* abs_path); + /** * Returns the next available file ID, or an error code less than zero * if an error occurs. @@ -244,6 +255,17 @@ int guac_rdpdr_fs_open(guac_rdpdr_device* device, const char* path, */ void guac_rdpdr_fs_close(guac_rdpdr_device* device, int file_id); +/** + * Given an arbitrary path, which may contain ".." and ".", creates an + * absolute path which does NOT contain ".." or ".". + */ +int guac_rdpdr_fs_normalize_path(const char* path, char* abs_path); + +/** + * Given a parent path and a relative path, produces a normalized absolute path. + */ +int guac_rdpdr_fs_convert_path(const char* parent, const char* rel_path, char* abs_path); + /** * Returns the next filename within the directory having the given file ID, * or NULL if no more files. diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c index 23e79aa8..623d50a1 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c @@ -275,12 +275,11 @@ void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device, void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream, int file_id, int completion_id) { - /*guac_rdpdr_fs_data* data = (guac_rdpdr_fs_data*) device->data; - guac_rdpdr_fs_file* file = &(data->files[file_id]);*/ - int fs_information_class, initial_query; int path_length; + const char* entry_name; + /* Read main header */ Stream_Read_UINT32(input_stream, fs_information_class); Stream_Read_UINT8(input_stream, initial_query); @@ -298,45 +297,51 @@ void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* i "Received initial dir query - class=0x%x, path_length=%i, path=%s", fs_information_class, path_length, path); - /* Open directory */ - /*file->dir = fdopendir(file->fd);*/ - - /* FIXME: Handle error */ - } else guac_client_log_info(device->rdpdr->client, "Received continued dir query - class=0x%x", fs_information_class); - /* Dispatch to appropriate class-specific handler */ - switch (fs_information_class) { + /* If entry exists, call appriate handler to send data */ + entry_name = guac_rdpdr_fs_read_dir(device, file_id); + if (entry_name != NULL) { - case FileDirectoryInformation: - guac_rdpdr_fs_process_query_directory_info(device, input_stream, - file_id, completion_id); - break; + guac_client_log_info(device->rdpdr->client, "NAME: %s", entry_name); - case FileFullDirectoryInformation: - guac_rdpdr_fs_process_query_full_directory_info(device, input_stream, - file_id, completion_id); - break; + /* Dispatch to appropriate class-specific handler */ + switch (fs_information_class) { - case FileBothDirectoryInformation: - guac_rdpdr_fs_process_query_both_directory_info(device, input_stream, - file_id, completion_id); - break; + case FileDirectoryInformation: + guac_rdpdr_fs_process_query_directory_info(device, input_stream, + file_id, completion_id); + break; - case FileNamesInformation: - guac_rdpdr_fs_process_query_names_info(device, input_stream, - file_id, completion_id); - break; + case FileFullDirectoryInformation: + guac_rdpdr_fs_process_query_full_directory_info(device, input_stream, + file_id, completion_id); + break; + + case FileBothDirectoryInformation: + guac_rdpdr_fs_process_query_both_directory_info(device, input_stream, + file_id, completion_id); + break; + + case FileNamesInformation: + guac_rdpdr_fs_process_query_names_info(device, input_stream, + file_id, completion_id); + break; + + default: + guac_client_log_info(device->rdpdr->client, + "Unknown dir information class: 0x%x", fs_information_class); + } - default: - guac_client_log_info(device->rdpdr->client, - "Unknown dir information class: 0x%x", fs_information_class); } + /* Otherwise, send STATUS_NO_MORE_FILES */ + else { + } }