Index: README ================================================================== --- README +++ README @@ -69,14 +69,23 @@ 4. Differing HTTP semantics (CFLAGS, -DFILED_NONBLOCK_HTTP=1) It is possible that some HTTP clients may not process the HTTP stream being delivered if they cannot write to the HTTP stream itself. This has not been observed yet, but it is possible. If these semantics are needed (and they should not be) then they can be enabled with this - flag at the cost of performance + flag at the cost of performance. + + 5. Differing chroot() semantics (CFLAGS, -DFILED_FAKE_CHROOT=1) + In some cases it is desirable to mangle paths with a path prefix + rather than call chroot() at startup. This is less secure and slower + and should be generally avoided -- however it may be necessary to do. + In these cases the executable may be compiled with the + FILED_FAKE_CHROOT C preprocessor macro defined and instead of calling + chroot() all HTTP requests will have the root suffix specified as the + argument to the "-r" or "--root" option prepended to them. - 5. MIME Types (MIMETYPES) - For single-file convience "filed" compiles the mapping of file + 6. MIME Types (MIMETYPES) + For single-file convenience "filed" compiles the mapping of file extensions (the string in the filename following its last dot (".")) into the executable. This mapping comes from a file in the format of type1 type1_extension1 type1_extension2... type2 type2_extension1 type2_extension2... ... Index: filed.c ================================================================== --- filed.c +++ filed.c @@ -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':