#include "config.h" #include #include #include #include #include #include "global.h" #include "alternative.h" #include "libstr.h" #include "libfs.h" #ifdef HAVE_REWRITE #include "rewrite.h" #endif #define MAX_INPUT KILOBYTE typedef struct type_line { char *key, *value, *file; int linenr; struct type_line *next; } t_line; bool quiet = false; int read_file(char *config_file, t_line **config, t_line **aliases, bool handle_include); t_line *last_result(t_line *config) { if (config != NULL) { while (config->next != NULL) { config = config->next; } } return config; } int add_result(t_line **config, char *key, char *value, char *file, int linenr) { t_line *new; if (*config == NULL) { if ((new = *config = (t_line*)malloc(sizeof(t_line))) == NULL) { return -1; } } else { new = last_result(*config); if ((new->next = (t_line*)malloc(sizeof(t_line))) == NULL) { return -1; } new = new->next; } new->next = NULL; new->key = key; new->value = value; new->file = file; new->linenr = linenr; return 0; } t_line *search_key(t_line *config, char *key) { t_line *result = NULL; while (config != NULL) { if (strcasecmp(config->key, key) == 0) { if (config->value == NULL) { printf("'%s' on line %d in '"CONFIG_DIR"/%s' requires a parameter.\n", config->key, config->linenr, config->file); exit(EXIT_FAILURE); } else if (add_result(&result, config->key, config->value, config->file, config->linenr) != 0) { return NULL; } } config = config->next; } return result; } t_line *in_result(char *value, t_line *result) { while (result != NULL) { if (strcmp(result->value, value) == 0) { return result; } result = result->next; } return NULL; } void dispose_result(t_line *config, bool free_content) { t_line *prev; while (config != NULL) { prev = config; config = config->next; if (free_content) { free(prev->key); } free(prev); } } #ifdef DEBUG void print_file(t_line *line) { while (line != NULL) { printf("%3d:%-25s [%s] = [%s]\n", line->linenr, line->file, line->key, line->value); line = line->next; } } #endif int read_directory(char *dir, t_line **config, t_line **aliases) { t_filelist *filelist, *file; char *path; int retval = 0; if ((filelist = read_filelist(dir)) == NULL) { return -1; } file = filelist = sort_filelist(filelist); while (file != NULL) { if (strcmp(file->name, "..") != 0) { if ((path = make_path(dir, file->name)) != NULL) { if (file->is_dir) { retval = read_directory(path, config, aliases); free(path); } else { retval = read_file(path, config, aliases, false); } if (retval == -1) { break; } } else { retval = -1; break; } } file = file->next; } remove_filelist(filelist); return retval; } int read_file(char *config_file, t_line **config, t_line **aliases, bool handle_include) { FILE *fp; char line[MAX_INPUT + 1], *data, *value; bool is_alias; int linenr = 0, retval = 0; if (quiet == false) { printf("Reading %s\n", config_file); } if ((fp = fopen(config_file, "r")) == NULL) { perror(config_file); return -1; } line[MAX_INPUT] = '\0'; while (fgets(line, MAX_INPUT, fp) != NULL) { linenr++; data = strip_line(line); if ((data[0] == '#') || (data[0] == '\0')) { continue; } if (handle_include && (strncasecmp(data, "include ", 8) == 0)) { switch (is_directory(data + 8)) { case no: retval = read_file(strdup(data + 8), config, aliases, false); break; case yes: retval = read_directory(data + 8, config, aliases); break; default: retval = -1; } } else { if (strncmp(data, "set ", 4) == 0) { is_alias = true; data += 4; } else { is_alias = false; } data = strdup(data); split_configline(data, &data, &value); if (is_alias) { retval = add_result(aliases, data, value, config_file, linenr); } else { retval = add_result(config, strlower(data), value, config_file, linenr); } } if (retval == -1) { break; } } fclose(fp); return retval; } int read_config_file(char *config_file, t_line **config) { t_line *aliases = NULL, *alias, *line; char *new_value; int retval; if ((retval = read_file(config_file, config, &aliases, true)) == -1) { return -1; } /* Replace the aliasses */ line = *config; while (line != NULL) { alias = aliases; while (alias != NULL) { if (line->value != NULL) { if (str_replace(line->value, alias->key, alias->value, &new_value) > 0) { line->value = new_value; } } alias = alias->next; } line = line->next; } dispose_result(aliases, true); return retval; } bool is_ip_address(char *str) { char *digit; int digits = 0, value; while (str != NULL) { split_string(str, &digit, &str, '.'); value = str2int(digit); if ((value < 0) || (value > 255)) { return false; } digits++; } return (digits == 4); } int find_duplicates(t_line *config, char *key, char *config_dir) { t_line *haystack, *needles, *needle; int errors = 0; haystack = needles = search_key(config, key); while (needles != NULL) { if ((needle = in_result(needles->value, needles->next)) != NULL) { printf("Duplicate %s found on line %d in '%s/%s'.\n", key, needle->linenr, config_dir, needle->file); errors++; } needles = needles->next; } dispose_result(haystack, false); return errors; } int check_main_config(char *config_dir) { int errors = 0; t_line *config = NULL, *haystack, *needles, *needle; char *item, *rest, *info; bool inside_section, has_dot; if (quiet == false) { printf("Using %s\n", config_dir); } if (chdir(config_dir) == -1) { perror(config_dir); return 1; } /* Read the configuration file */ config = NULL; if (read_config_file(strdup("httpd.conf"), &config) != 0) { return 1; } /* Find duplicate ids */ errors += find_duplicates(config, "BindingId", config_dir); errors += find_duplicates(config, "FastCGIid", config_dir); errors += find_duplicates(config, "RewriteId", config_dir); /* Binding Id check */ haystack = search_key(config, "bindingid"); needles = needle = search_key(config, "requiredbinding"); while (needle != NULL) { rest = strdup(needle->value); while (rest != NULL) { split_string(rest, &item, &rest, ','); if (in_result(item, haystack) == NULL) { printf("RequiredBinding '%s' on line %d in '%s/%s' not found in Binding section.\n", item, needle->linenr, config_dir, needle->file); errors++; } } free(rest); needle = needle->next; } dispose_result(haystack, false); dispose_result(needles, false); /* FastCGI Id check */ haystack = search_key(config, "fastcgiid"); needles = needle = search_key(config, "fastcgi"); while (needle != NULL) { rest = strdup(needle->value); while (rest != NULL) { split_string(rest, &item, &rest, ','); if (in_result(item, haystack) == NULL) { printf("FastCGIid '%s' on line %d in '%s/%s' not found in FastCGIserver section.\n", needle->value, needle->linenr, config_dir, needle->file); errors++; } } free(rest); needle = needle->next; } dispose_result(haystack, false); dispose_result(needles, false); /* Rewrite Id check */ haystack = search_key(config, "rewriteid"); needles = needle = search_key(config, "rewriteurl"); while (needle != NULL) { rest = strdup(needle->value); while (rest != NULL) { split_string(rest, &item, &rest, ','); if (in_result(item, haystack) == NULL) { printf("RewriteURL '%s' on line %d in '%s/%s' not found in UrlRewrite section.\n", item, needle->linenr, config_dir, needle->file); errors++; } } free(rest); needle = needle->next; } dispose_result(haystack, false); dispose_result(needles, false); /* Extension check */ haystack = NULL; needles = needle = search_key(config, "cgiextension"); while (needle != NULL) { rest = strdup(needle->value); while (rest != NULL) { split_string(rest, &item, &rest, ','); if (in_result(item, haystack) == NULL) { add_result(&haystack, needle->key, item, needle->file, needle->linenr); } else { printf("Duplicate extension (%s) found in CGIextension.\n", item); errors++; } } needle = needle->next; } dispose_result(needles, false); needles = needle = search_key(config, "cgihandler"); while (needle != NULL) { #ifdef CYGWIN if ((rest = strstr(needle->value, ":\\")) != NULL) { rest += 2; } else #endif rest = strdup(needle->value); split_string(rest, &info, &rest, ':'); #ifdef CYGWIN info = needle->value; #endif while (rest != NULL) { split_string(rest, &item, &rest, ','); if (in_result(item, haystack) == NULL) { add_result(&haystack, needle->key, item, needle->file, needle->linenr); } else { printf("Duplicate extension (%s) found in CGIhandler %s.\n", item, info); errors++; } } needle = needle->next; } dispose_result(needles, false); dispose_result(haystack, false); /* Default-website hostname check (non-fatal) */ inside_section = false; haystack = config; while (haystack != NULL) { if (strncmp(haystack->key, "virtualhost", 11) == 0) { inside_section = true; } else if (strcmp(haystack->key, "}") == 0) { inside_section = false; } else if (inside_section == false) { if (strcmp(haystack->key, "hostname") == 0) { if (is_ip_address(haystack->value) == false) { printf("Warning: it is wise to use your IP address as the hostname of the default website (line %d in '%s/%s') and give it a blank webpage. By doing so, automated webscanners won't find your possible vulnerable website.\n", haystack->linenr, config_dir, haystack->file); } break; } } haystack = haystack->next; } /* Check for dots in extensios */ haystack = config; while (haystack != NULL) { if ((strcmp(haystack->key, "cgiextension") == 0) || (strcmp(haystack->key, "extension") == 0)) { has_dot = (strchr(haystack->value, '.') != NULL); } else if (strcmp(haystack->key, "cgihandler") == 0) { #ifdef CYGWIN if ((info = strstr(haystack->value, ":\\")) != NULL) { info += 2; } else #endif info = haystack->value; if ((rest = strchr(info, ':')) != NULL) { has_dot = (strchr(rest, '.') != NULL); } else { has_dot = false; } } else { has_dot = false; } if (has_dot) { printf("Extensions should not contain a dot (line %d in '%s/%s')\n", haystack->linenr, config_dir, haystack->file); errors++; } haystack = haystack->next; } dispose_result(config, true); return errors; } #ifdef HAVE_REWRITE void check_url_rewrite(char *config_dir, char **rewrite_id) { t_line *config = NULL; char input[MAX_INPUT + 1], **id, *url, *new_url; t_url_rewrite *url_rewrite, *rewrite = NULL; bool in_rule_section = false; int result; if (chdir(config_dir) == -1) { perror(config_dir); return; } /* Read the configuration file */ config = NULL; if (read_config_file(strdup("httpd.conf"), &config) != 0) { return; } /* Parse the URL rewrite rules */ url_rewrite = NULL; while (config != NULL) { if (in_rule_section) { if ((strcmp(config->key, "}") == 0) && (strcmp(config->value, "") == 0)) { in_rule_section = false; } else if (url_rewrite_setting(config->key, config->value, rewrite) == false) { fprintf(stderr, "UrlRewrite error in %s on line %d.\n", config->file, config->linenr); return; } } else if ((strcmp(config->key, "urlrewrite") == 0) && (strcmp(config->value, "{") == 0)) { if (url_rewrite == NULL) { url_rewrite = rewrite = (t_url_rewrite*)malloc(sizeof(t_url_rewrite)); } else { rewrite->next = (t_url_rewrite*)malloc(sizeof(t_url_rewrite)); rewrite = rewrite->next; } rewrite->rewrite_id = NULL; rewrite->rewrite_rule = NULL; rewrite->next = NULL; in_rule_section = true; } config = config->next; } if (url_rewrite == NULL) { printf("No URL rewrite rules found.\n"); return; } if (rewrite_rules_oke(url_rewrite) == false) { return; } if (*rewrite_id == NULL) { printf("No errors found in URL rewrite rules.\n"); return; } else id = rewrite_id; do { if (find_url_rewrite(*id, url_rewrite) == false) { printf("RewriteID '%s' not found.\n", *id); return; } id++; } while (*id != NULL); /* Start testing */ input[MAX_INPUT] = '\0'; printf("\n===[ URL rewrite tester\n"); printf("Use empty input to leave the program.\n\nurl: "); while (fgets(input, MAX_INPUT, stdin) != NULL) { url = remove_spaces(input); if (strcmp(url, "") == 0) { printf("bye!\n\n"); break; } id = rewrite_id; while (*id != NULL) { if ((result = rewrite_url(url, *id, url_rewrite, &new_url)) == RW_ERROR) { perror("rewrite_url()"); } if (new_url != NULL) { url = new_url; } if (result == RW_DENY_ACCESS) { url = "(403 Forbidden)"; break; } id++; } if (new_url != NULL) { printf("new: %s\n\n", new_url); free(new_url); } else { printf("old: %s\n\n", url); } printf("url: "); } } #endif void show_help(char *wigwam) { printf("Usage: %s [options]\n", wigwam); printf("Options: -c : path to where the configrationfiles are located.\n"); printf(" -h: show this information and exit.\n"); printf(" -q: don't print the test results.\n"); #ifdef HAVE_REWRITE printf(" -r [ ...]: test URL rewrite rule(s).\n"); #endif printf(" -v: show version and exit.\n"); } int main(int argc, char *argv[]) { int i, errors_found = 0; char *config_dir = CONFIG_DIR; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-c") == 0) { if (++i < argc) { config_dir = argv[i]; } else { fprintf(stderr, "Specify a directory.\n"); return EXIT_FAILURE; } } else if (strcmp(argv[i], "-h") == 0) { show_help(argv[0]); return EXIT_SUCCESS; #ifdef HAVE_REWRITE } else if (strcmp(argv[i], "-r") == 0) { check_url_rewrite(config_dir, argv + i + 1); return EXIT_SUCCESS; #endif } else if (strcmp(argv[i], "-q") == 0) { quiet = true; } else if (strcmp(argv[i], "-v") == 0) { printf("Wigwam v"VERSION"\n"); return EXIT_SUCCESS; } else { fprintf(stderr, "Unknown option. Use '-h' for help.\n"); return EXIT_FAILURE; } } errors_found += check_main_config(config_dir); if ((quiet == false) && (errors_found == 0)) { printf("No non-fatal errors found in the Hiawatha configuration.\n"); } if (errors_found > 0) { return EXIT_FAILURE; } return EXIT_SUCCESS; }