Add path normalization.

This commit is contained in:
Michael Jumper 2013-08-02 13:35:52 -07:00
parent 52f9bac805
commit 69d636860d
3 changed files with 164 additions and 52 deletions

View File

@ -61,11 +61,12 @@
#include <freerdp/utils/svc_plugin.h> #include <freerdp/utils/svc_plugin.h>
/** /**
* Translates an absolute Windows path to an absolute path which is within the "drive path" * Translates an absolute Windows virtual_path to an absolute virtual_path
* specified in the connection settings. * which is within the "drive virtual_path" specified in the connection
* settings.
*/ */
static char* __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device, static void __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device,
const char* path, char* path_buffer) { const char* virtual_path, char* real_path) {
/* Get drive path */ /* Get drive path */
rdp_guac_client_data* client_data = (rdp_guac_client_data*) device->rdpdr->client->data; 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; break;
/* Copy character */ /* Copy character */
*(path_buffer++) = c; *(real_path++) = c;
} }
@ -90,29 +91,21 @@ static char* __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device,
for (; i<GUAC_RDPDR_FS_MAX_PATH-1; i++) { for (; i<GUAC_RDPDR_FS_MAX_PATH-1; i++) {
/* Stop at end of string */ /* Stop at end of string */
char c = *path; char c = *(virtual_path++);
if (c == 0) if (c == 0)
break; break;
/* Disallow ".." */
if (c == '.' && *(path-1) == '.')
return NULL;
/* Translate backslashes to forward slashes */ /* Translate backslashes to forward slashes */
if (c == '\\') if (c == '\\')
c = '/'; c = '/';
/* Store in real path buffer */ /* Store in real path buffer */
*(path_buffer++)= c; *(real_path++)= c;
/* Next path character */
path++;
} }
/* Null terminator */ /* Null terminator */
*(path_buffer++)= 0; *real_path = 0;
return path_buffer;
} }
@ -120,7 +113,8 @@ int guac_rdpdr_fs_open(guac_rdpdr_device* device, const char* path,
int access, int create_disposition) { int access, int create_disposition) {
guac_rdpdr_fs_data* data = (guac_rdpdr_fs_data*) device->data; guac_rdpdr_fs_data* data = (guac_rdpdr_fs_data*) device->data;
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; struct stat file_stat;
int fd; 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 */ /* Normalize path, return no-such-file if invalid */
if (__guac_rdpdr_fs_translate_path(device, path, path_buffer) == NULL) if (guac_rdpdr_fs_normalize_path(path, normalized_path))
return GUAC_RDPDR_FS_ENOENT; return GUAC_RDPDR_FS_ENOENT;
guac_client_log_info(device->rdpdr->client, "Path \"%s\" translated to \"%s\"", /* Translate normalized path to real path */
path, path_buffer); __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 */ /* Open file */
fd = open(path_buffer, flags, mode); fd = open(real_path, flags, mode);
if (fd == -1) if (fd == -1)
return GUAC_RDPDR_FS_ENOENT; 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 = &(data->files[file_id]);
file->fd = fd; file->fd = fd;
file->dir = NULL; file->dir = NULL;
file->absolute_path = strdup(path);
/* Attempt to pull file information */ /* Attempt to pull file information */
if (fstat(fd, &file_stat) == 0) { if (fstat(fd, &file_stat) == 0) {
@ -231,7 +230,7 @@ int guac_rdpdr_fs_open(guac_rdpdr_device* device, const char* path,
else { else {
guac_client_log_info(device->rdpdr->client, "Unable to read information for \"%s\"", guac_client_log_info(device->rdpdr->client, "Unable to read information for \"%s\"",
path_buffer); real_path);
/* Init information to 0, lacking any alternative */ /* Init information to 0, lacking any alternative */
file->size = 0; file->size = 0;
@ -267,6 +266,9 @@ void guac_rdpdr_fs_close(guac_rdpdr_device* device, int file_id) {
/* Close file */ /* Close file */
close(file->fd); close(file->fd);
/* Free name */
free(file->absolute_path);
/* Free ID back to pool */ /* Free ID back to pool */
guac_pool_free_int(data->file_id_pool, file_id); guac_pool_free_int(data->file_id_pool, file_id);
data->open_files--; 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<GUAC_RDPDR_FS_MAX_PATH; i++) {
/* If current character is a path separator, parse as component */
char c = path_component_data[i];
if (c == '/' || c == '\\' || c == 0) {
/* Terminate current component */
path_component_data[i] = 0;
/* If component refers to parent, just move up in depth */
if (strcmp(current_path_component_data, "..") == 0) {
if (path_depth > 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;
}

View File

@ -160,6 +160,11 @@ typedef enum guac_rdpdr_fs_file_type {
*/ */
typedef struct guac_rdpdr_fs_file { typedef struct guac_rdpdr_fs_file {
/**
* The absolute path, including filename, of this file.
*/
char* absolute_path;
/** /**
* The type of this file. * The type of this file.
*/ */
@ -232,6 +237,12 @@ typedef struct guac_rdpdr_fs_data {
*/ */
void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr); 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 * Returns the next available file ID, or an error code less than zero
* if an error occurs. * 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); 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, * Returns the next filename within the directory having the given file ID,
* or NULL if no more files. * or NULL if no more files.

View File

@ -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, void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
int file_id, int completion_id) { 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 fs_information_class, initial_query;
int path_length; int path_length;
const char* entry_name;
/* Read main header */ /* Read main header */
Stream_Read_UINT32(input_stream, fs_information_class); Stream_Read_UINT32(input_stream, fs_information_class);
Stream_Read_UINT8(input_stream, initial_query); 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", "Received initial dir query - class=0x%x, path_length=%i, path=%s",
fs_information_class, path_length, path); fs_information_class, path_length, path);
/* Open directory */
/*file->dir = fdopendir(file->fd);*/
/* FIXME: Handle error */
} }
else else
guac_client_log_info(device->rdpdr->client, guac_client_log_info(device->rdpdr->client,
"Received continued dir query - class=0x%x", "Received continued dir query - class=0x%x",
fs_information_class); fs_information_class);
/* Dispatch to appropriate class-specific handler */ /* If entry exists, call appriate handler to send data */
switch (fs_information_class) { entry_name = guac_rdpdr_fs_read_dir(device, file_id);
if (entry_name != NULL) {
case FileDirectoryInformation: guac_client_log_info(device->rdpdr->client, "NAME: %s", entry_name);
guac_rdpdr_fs_process_query_directory_info(device, input_stream,
file_id, completion_id);
break;
case FileFullDirectoryInformation: /* Dispatch to appropriate class-specific handler */
guac_rdpdr_fs_process_query_full_directory_info(device, input_stream, switch (fs_information_class) {
file_id, completion_id);
break;
case FileBothDirectoryInformation: case FileDirectoryInformation:
guac_rdpdr_fs_process_query_both_directory_info(device, input_stream, guac_rdpdr_fs_process_query_directory_info(device, input_stream,
file_id, completion_id); file_id, completion_id);
break; break;
case FileNamesInformation: case FileFullDirectoryInformation:
guac_rdpdr_fs_process_query_names_info(device, input_stream, guac_rdpdr_fs_process_query_full_directory_info(device, input_stream,
file_id, completion_id); file_id, completion_id);
break; 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 {
}
} }