@@ -117,10 +117,11 @@ #endif /* Configuration options that work threads need to be aware of */ struct filed_options { int vhosts_enabled; + const char *fake_newroot; }; /* Arguments for worker threads */ struct filed_worker_thread_args { int fd; @@ -864,13 +865,58 @@ (unsigned long long) random(), (unsigned long long) random(), (unsigned long long) random() ); } + +#ifdef FILED_FAKE_CHROOT +/* Translate a path into a fake chroot path */ +static const char *filed_path_translate(const char *path, struct filed_options *options) { + static __thread char pathBuffer[8192]; + int snprintf_ret; + + /* If no alternative root is specified, return the unadorned path */ + if (!options->fake_newroot) { + return(path); + } + + /* Verify that this request will not go outside of the specified root */ + if (strstr(path, "/../") != NULL || path[0] != '/') { + filed_log_msg_debug("Unable to translate path \"%s\", contains invalid characters", path); + + return(options->fake_newroot); + } + + /* Create the new path into our local (TLS) static buffer */ + snprintf_ret = snprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", options->fake_newroot, path); + if (snprintf_ret < 0 || ((unsigned int) snprintf_ret) >= sizeof(pathBuffer)) { + filed_log_msg_debug("Unable to translate path \"%s\", will not fit into new buffer", path); + + return(options->fake_newroot); + } + + filed_log_msg_debug("Translating path \"%s\" into \"%s\"", path, pathBuffer); + + /* Return the new path */ + return(pathBuffer); +} + +static void filed_path_translate_set_root(const char *var, struct filed_options *options, const char *val) { + options->fake_newroot = strdup(val); + + return; + + /* var is only used in the macro -- discard it here */ + var = var; +} +#else +#define filed_path_translate(path, options) path +#define filed_path_translate_set_root(var, options, val) var = strdup(val) +#endif /* Open a file and return file information */ -static struct filed_fileinfo *filed_open_file(const char *path, struct filed_fileinfo *buffer) { +static struct filed_fileinfo *filed_open_file(const char *path, struct filed_fileinfo *buffer, struct filed_options *options) { struct filed_fileinfo *cache; unsigned int cache_idx; off_t len; int fd; @@ -892,11 +938,11 @@ } if (strcmp(path, cache->path) != 0) { filed_log_msg_debug("Cache miss for idx: %lu: OLD \"%s\", NEW \"%s\"", (unsigned long) cache_idx, cache->path, path); - fd = open(path, O_RDONLY | O_LARGEFILE); + fd = open(filed_path_translate(path, options), O_RDONLY | O_LARGEFILE); if (fd < 0) { if (filed_fileinfo_fdcache_size != 0) { pthread_mutex_unlock(&cache->mutex); } @@ -943,10 +989,13 @@ pthread_mutex_unlock(&cache->mutex); } return(buffer); + + /* options is only used if fake chroot is enabled, confuse the compiler */ + options = options; } /* Process an HTTP request and return the path requested */ static struct filed_http_request *filed_get_http_request(FILE *fp, struct filed_http_request *buffer_st, struct filed_options *options) { char *method, *path; @@ -1237,11 +1286,11 @@ filed_redirect_index(fp, date_current, path, log); return(FILED_CONNECTION_CLOSE); } - fileinfo = filed_open_file(path, &request->fileinfo); + fileinfo = filed_open_file(path, &request->fileinfo, options); if (fileinfo == NULL) { filed_error_page(fp, date_current, 404, request->method, "open_failed", log); return(FILED_CONNECTION_CLOSE); } @@ -1681,11 +1730,11 @@ } /* Run process */ int main(int argc, char **argv) { struct option options[12]; - struct filed_options thread_options; + struct filed_options thread_options = {0}; const char *bind_addr = BIND_ADDR, *newroot = NULL, *log_file = LOG_FILE; FILE *log_fp; uid_t user = 0; int port = PORT, thread_count = THREAD_COUNT; int cache_size = CACHE_SIZE; @@ -1692,13 +1741,10 @@ int init_ret, chroot_ret, setuid_ret, lookup_ret, chdir_ret; int setuid_enabled = 0, daemon_enabled = 0; int ch; int fd; - /* Set default values */ - thread_options.vhosts_enabled = 0; - /* Process arguments */ filed_getopt_long_setopt(&options[0], "port", required_argument, 'p'); filed_getopt_long_setopt(&options[1], "threads", required_argument, 't'); filed_getopt_long_setopt(&options[2], "cache", required_argument, 'c'); filed_getopt_long_setopt(&options[3], "bind", required_argument, 'b'); @@ -1732,11 +1778,11 @@ return(1); } break; case 'r': - newroot = strdup(optarg); + filed_path_translate_set_root(newroot, &thread_options, optarg); break; case 'l': log_file = strdup(optarg); break; case 'd':