/* Return an array of all supported/known configuration directives. */ static array_header *get_all_directives(pool *p) { array_header *names; conftable *tab; int idx; unsigned int hash; names = make_array(p, 1, sizeof(const char *)); idx = -1; hash = 0; tab = pr_stash_get_symbol2(PR_SYM_CONF, NULL, NULL, &idx, &hash); while (idx != -1) { pr_signals_handle(); if (tab != NULL) { *((const char **) push_array(names)) = pstrdup(p, tab->directive); } else { idx++; } tab = pr_stash_get_symbol2(PR_SYM_CONF, NULL, tab, &idx, &hash); } return names; }
/* Sentinel values: * * cmd_type = 0 * cmd_group = NULL * cmd_class = -1 */ int pr_stash_remove_cmd(const char *cmd_name, module *m, unsigned char cmd_type, const char *cmd_group, int cmd_class) { int count = 0, prev_idx, symtab_idx = 0; size_t cmd_namelen = 0; unsigned int hash; cmdtable *tab = NULL; if (cmd_name == NULL) { errno = EINVAL; return -1; } /* Don't forget to include one for the terminating NUL. */ cmd_namelen = strlen(cmd_name) + 1; hash = sym_type_hash(PR_SYM_CMD, cmd_name, cmd_namelen); symtab_idx = hash % PR_TUNABLE_HASH_TABLE_SIZE; prev_idx = -1; tab = pr_stash_get_symbol2(PR_SYM_CMD, cmd_name, NULL, &prev_idx, &hash); while (tab) { cmdtable *cmd_sym; pr_signals_handle(); /* Note: this works because of a hack: the symbol lookup functions set a * static pointer, cmd_curr_sym, to point to the struct stash just looked * up. cmd_curr_sym will not be NULL if pr_stash_get_symbol2() returns * non-NULL. */ cmd_sym = cmd_curr_sym->ptr.sym_cmd; if ((m == NULL || cmd_curr_sym->sym_module == m) && (cmd_type == 0 || cmd_sym->cmd_type == cmd_type) && (cmd_group == NULL || (cmd_group != NULL && cmd_sym->group != NULL && strcmp(cmd_sym->group, cmd_group) == 0)) && (cmd_class == -1 || cmd_sym->cmd_class == cmd_class)) { xaset_remove(cmd_symbol_table[symtab_idx], (xasetmember_t *) cmd_curr_sym); destroy_pool(cmd_curr_sym->sym_pool); cmd_curr_sym = NULL; tab = NULL; count++; } tab = pr_stash_get_symbol2(PR_SYM_CMD, cmd_name, tab, &prev_idx, &hash); } return count; }
unsigned char command_exists(char *name) { int idx = -1; unsigned int hash = 0; cmdtable *cmdtab; cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, NULL, &idx, &hash); while (cmdtab && cmdtab->cmd_type != CMD) { pr_signals_handle(); cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, cmdtab, &idx, &hash); } return (cmdtab ? TRUE : FALSE); }
int pr_stash_remove_conf(const char *directive_name, module *m) { int count = 0, prev_idx, symtab_idx = 0; size_t directive_namelen = 0; unsigned int hash; conftable *tab = NULL; if (directive_name == NULL) { errno = EINVAL; return -1; } /* Don't forget to include one for the terminating NUL. */ directive_namelen = strlen(directive_name) + 1; hash = sym_type_hash(PR_SYM_CONF, directive_name, directive_namelen); symtab_idx = hash % PR_TUNABLE_HASH_TABLE_SIZE; prev_idx = -1; tab = pr_stash_get_symbol2(PR_SYM_CONF, directive_name, NULL, &prev_idx, &hash); while (tab) { pr_signals_handle(); /* Note: this works because of a hack: the symbol lookup functions set a * static pointer, conf_curr_sym, to point to the struct stash just looked * up. conf_curr_sym will not be NULL if pr_stash_get_symbol2() returns * non-NULL. */ if (m == NULL || conf_curr_sym->sym_module == m) { xaset_remove(conf_symbol_table[symtab_idx], (xasetmember_t *) conf_curr_sym); destroy_pool(conf_curr_sym->sym_pool); conf_curr_sym = NULL; tab = NULL; count++; } tab = pr_stash_get_symbol2(PR_SYM_CONF, directive_name, tab, &prev_idx, &hash); } return count; }
void *pr_stash_get_symbol(pr_stash_type_t sym_type, const char *name, void *prev, int *idx_cache) { return pr_stash_get_symbol2(sym_type, name, prev, idx_cache, NULL); }
int pr_parser_parse_file(pool *p, const char *path, config_rec *start, int flags) { pr_fh_t *fh; struct stat st; struct config_src *cs; cmd_rec *cmd; pool *tmp_pool; char *buf, *report_path; size_t bufsz; if (path == NULL) { errno = EINVAL; return -1; } if (parser_servstack == NULL) { errno = EPERM; return -1; } tmp_pool = make_sub_pool(p ? p : permanent_pool); pr_pool_tag(tmp_pool, "parser file pool"); report_path = (char *) path; if (session.chroot_path) { report_path = pdircat(tmp_pool, session.chroot_path, path, NULL); } if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) { pr_trace_msg(trace_channel, 3, "parsing '%s' configuration", report_path); } fh = pr_fsio_open(path, O_RDONLY); if (fh == NULL) { int xerrno = errno; destroy_pool(tmp_pool); errno = xerrno; return -1; } /* Stat the opened file to determine the optimal buffer size for IO. */ memset(&st, 0, sizeof(st)); if (pr_fsio_fstat(fh, &st) < 0) { int xerrno = errno; pr_fsio_close(fh); destroy_pool(tmp_pool); errno = xerrno; return -1; } if (S_ISDIR(st.st_mode)) { pr_fsio_close(fh); destroy_pool(tmp_pool); errno = EISDIR; return -1; } /* Advise the platform that we will be only reading this file * sequentially. */ pr_fs_fadvise(PR_FH_FD(fh), 0, 0, PR_FS_FADVISE_SEQUENTIAL); /* Check for world-writable files (and later, files in world-writable * directories). * * For now, just warn about these; later, we will be more draconian. */ if (st.st_mode & S_IWOTH) { pr_log_pri(PR_LOG_WARNING, "warning: config file '%s' is world-writable", path); } fh->fh_iosz = st.st_blksize; /* Push the configuration information onto the stack of configuration * sources. */ cs = add_config_source(fh); if (start != NULL) { (void) pr_parser_config_ctxt_push(start); } bufsz = PR_TUNABLE_PARSER_BUFFER_SIZE; buf = pcalloc(tmp_pool, bufsz + 1); while (pr_parser_read_line(buf, bufsz) != NULL) { pr_signals_handle(); cmd = pr_parser_parse_line(tmp_pool, buf, 0); if (cmd == NULL) { continue; } if (cmd->argc) { conftable *conftab; char found = FALSE; cmd->server = *parser_curr_server; cmd->config = *parser_curr_config; conftab = pr_stash_get_symbol2(PR_SYM_CONF, cmd->argv[0], NULL, &cmd->stash_index, &cmd->stash_hash); while (conftab != NULL) { modret_t *mr; pr_signals_handle(); cmd->argv[0] = conftab->directive; pr_trace_msg(trace_channel, 7, "dispatching directive '%s' to module mod_%s", conftab->directive, conftab->m->name); mr = pr_module_call(conftab->m, conftab->handler, cmd); if (mr != NULL) { if (MODRET_ISERROR(mr)) { if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) { pr_log_pri(PR_LOG_WARNING, "fatal: %s on line %u of '%s'", MODRET_ERRMSG(mr), cs->cs_lineno, report_path); destroy_pool(tmp_pool); errno = EPERM; return -1; } pr_log_pri(PR_LOG_WARNING, "warning: %s on line %u of '%s'", MODRET_ERRMSG(mr), cs->cs_lineno, report_path); } } if (!MODRET_ISDECLINED(mr)) { found = TRUE; } conftab = pr_stash_get_symbol2(PR_SYM_CONF, cmd->argv[0], conftab, &cmd->stash_index, &cmd->stash_hash); } if (cmd->tmp_pool) { destroy_pool(cmd->tmp_pool); } if (found == FALSE) { register unsigned int i; char *name; size_t namelen; int non_ascii = FALSE; /* I encountered a case where a particular configuration file had * what APPEARED to be a valid directive, but the parser kept reporting * that the directive was unknown. I now suspect that the file in * question had embedded UTF8 characters (spaces, perhaps), which * would appear as normal spaces in e.g. UTF8-aware editors/terminals, * but which the parser would rightly refuse. * * So to indicate that this might be the case, check for any non-ASCII * characters in the "unknown" directive name, and if found, log * about them. */ name = cmd->argv[0]; namelen = strlen(name); for (i = 0; i < namelen; i++) { if (!isascii((int) name[i])) { non_ascii = TRUE; break; } } if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) { pr_log_pri(PR_LOG_WARNING, "fatal: unknown configuration directive " "'%s' on line %u of '%s'", name, cs->cs_lineno, report_path); if (non_ascii) { pr_log_pri(PR_LOG_WARNING, "fatal: malformed directive name " "'%s' (contains non-ASCII characters)", name); } else { array_header *directives, *similars; directives = get_all_directives(tmp_pool); similars = pr_str_get_similars(tmp_pool, name, directives, 0, PR_STR_FL_IGNORE_CASE); if (similars != NULL && similars->nelts > 0) { unsigned int nelts; const char **names, *msg; names = similars->elts; nelts = similars->nelts; if (nelts > 4) { nelts = 4; } msg = "fatal: Did you mean:"; if (nelts == 1) { msg = pstrcat(tmp_pool, msg, " ", names[0], NULL); } else { for (i = 0; i < nelts; i++) { msg = pstrcat(tmp_pool, msg, "\n ", names[i], NULL); } } pr_log_pri(PR_LOG_WARNING, "%s", msg); } } destroy_pool(tmp_pool); errno = EPERM; return -1; } pr_log_pri(PR_LOG_WARNING, "warning: unknown configuration directive " "'%s' on line %u of '%s'", name, cs->cs_lineno, report_path); if (non_ascii) { pr_log_pri(PR_LOG_WARNING, "warning: malformed directive name " "'%s' (contains non-ASCII characters)", name); } } } destroy_pool(cmd->pool); memset(buf, '\0', bufsz); } /* Pop this configuration stream from the stack. */ remove_config_source(); pr_fsio_close(fh); destroy_pool(tmp_pool); return 0; }
END_TEST START_TEST (stash_get_symbol2_test) { int res; void *sym; conftable conftab; cmdtable cmdtab, hooktab; authtable authtab; sym = pr_stash_get_symbol2(0, NULL, NULL, NULL, NULL); fail_unless(sym == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %d (%s)", errno, strerror(errno)); sym = pr_stash_get_symbol2(0, "foo", NULL, NULL, NULL); fail_unless(sym == NULL, "Failed to handle bad type argument"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL, got %d (%s)", errno, strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_CONF, "foo", NULL, NULL, NULL); fail_unless(sym == NULL, "Failed to handle nonexistent CONF symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_CMD, "foo", NULL, NULL, NULL); fail_unless(sym == NULL, "Failed to handle nonexistent CMD symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_AUTH, "foo", NULL, NULL, NULL); fail_unless(sym == NULL, "Failed to handle nonexistent AUTH symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_HOOK, "foo", NULL, NULL, NULL); fail_unless(sym == NULL, "Failed to handle nonexistent HOOK symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); memset(&conftab, 0, sizeof(conftab)); conftab.directive = pstrdup(p, "foo"); res = pr_stash_add_symbol(PR_SYM_CONF, &conftab); fail_unless(res == 0, "Failed to add CONF symbol: %s", strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_CONF, "foo", NULL, NULL, NULL); fail_unless(sym != NULL, "Failed to get CONF symbol: %s", strerror(errno)); fail_unless(sym == &conftab, "Expected %p, got %p", &conftab, sym); sym = pr_stash_get_symbol2(PR_SYM_CONF, "foo", sym, NULL, NULL); fail_unless(sym == NULL, "Unexpectedly found CONF symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); memset(&cmdtab, 0, sizeof(cmdtab)); cmdtab.command = pstrdup(p, "foo"); res = pr_stash_add_symbol(PR_SYM_CMD, &cmdtab); fail_unless(res == 0, "Failed to add CMD symbol: %s", strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_CMD, "foo", NULL, NULL, NULL); fail_unless(sym != NULL, "Failed to get CMD symbol: %s", strerror(errno)); fail_unless(sym == &cmdtab, "Expected %p, got %p", &cmdtab, sym); sym = pr_stash_get_symbol2(PR_SYM_CMD, "foo", sym, NULL, NULL); fail_unless(sym == NULL, "Unexpectedly found CMD symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); memset(&authtab, 0, sizeof(authtab)); authtab.name = pstrdup(p, "foo"); res = pr_stash_add_symbol(PR_SYM_AUTH, &authtab); fail_unless(res == 0, "Failed to add AUTH symbol: %s", strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_AUTH, "foo", NULL, NULL, NULL); fail_unless(sym != NULL, "Failed to get AUTH symbol: %s", strerror(errno)); fail_unless(sym == &authtab, "Expected %p, got %p", &authtab, sym); sym = pr_stash_get_symbol2(PR_SYM_AUTH, "foo", sym, NULL, NULL); fail_unless(sym == NULL, "Unexpectedly found AUTH symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); memset(&hooktab, 0, sizeof(hooktab)); hooktab.command = pstrdup(p, "foo"); res = pr_stash_add_symbol(PR_SYM_HOOK, &hooktab); fail_unless(res == 0, "Failed to add HOOK symbol: %s", strerror(errno)); sym = pr_stash_get_symbol2(PR_SYM_HOOK, "foo", NULL, NULL, NULL); fail_unless(sym != NULL, "Failed to get HOOK symbol: %s", strerror(errno)); fail_unless(sym == &hooktab, "Expected %p, got %p", &hooktab, sym); sym = pr_stash_get_symbol2(PR_SYM_HOOK, "foo", sym, NULL, NULL); fail_unless(sym == NULL, "Unexpectedly found HOOK symbol"); fail_unless(errno == ENOENT, "Failed to set errno to ENOENT, got %d (%s)", errno, strerror(errno)); }
int pr_parser_parse_file(pool *p, const char *path, config_rec *start, int flags) { pr_fh_t *fh; struct stat st; struct config_src *cs; cmd_rec *cmd; pool *tmp_pool; char *report_path; if (path == NULL) { errno = EINVAL; return -1; } tmp_pool = make_sub_pool(p ? p : permanent_pool); pr_pool_tag(tmp_pool, "parser file pool"); report_path = (char *) path; if (session.chroot_path) report_path = pdircat(tmp_pool, session.chroot_path, path, NULL); if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) pr_trace_msg(trace_channel, 3, "parsing '%s' configuration", report_path); fh = pr_fsio_open(path, O_RDONLY); if (fh == NULL) { int xerrno = errno; destroy_pool(tmp_pool); errno = xerrno; return -1; } /* Stat the opened file to determine the optimal buffer size for IO. */ memset(&st, 0, sizeof(st)); if (pr_fsio_fstat(fh, &st) < 0) { int xerrno = errno; pr_fsio_close(fh); destroy_pool(tmp_pool); errno = xerrno; return -1; } if (S_ISDIR(st.st_mode)) { pr_fsio_close(fh); destroy_pool(tmp_pool); errno = EISDIR; return -1; } /* Check for world-writable files (and later, files in world-writable * directories). * * For now, just warn about these; later, we will be more draconian. */ if (st.st_mode & S_IWOTH) { pr_log_pri(PR_LOG_WARNING, "warning: config file '%s' is world-writable", path); } fh->fh_iosz = st.st_blksize; /* Push the configuration information onto the stack of configuration * sources. */ cs = add_config_source(fh); if (start) add_config_ctxt(start); while ((cmd = pr_parser_parse_line(tmp_pool)) != NULL) { pr_signals_handle(); if (cmd->argc) { conftable *conftab; char found = FALSE; cmd->server = *parser_curr_server; cmd->config = *parser_curr_config; conftab = pr_stash_get_symbol2(PR_SYM_CONF, cmd->argv[0], NULL, &cmd->stash_index, &cmd->stash_hash); while (conftab) { modret_t *mr; pr_signals_handle(); cmd->argv[0] = conftab->directive; pr_trace_msg(trace_channel, 7, "dispatching directive '%s' to module mod_%s", conftab->directive, conftab->m->name); mr = pr_module_call(conftab->m, conftab->handler, cmd); if (mr != NULL) { if (MODRET_ISERROR(mr)) { if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) { pr_log_pri(PR_LOG_WARNING, "fatal: %s on line %u of '%s'", MODRET_ERRMSG(mr), cs->cs_lineno, report_path); exit(1); } else { pr_log_pri(PR_LOG_WARNING, "warning: %s on line %u of '%s'", MODRET_ERRMSG(mr), cs->cs_lineno, report_path); } } } if (!MODRET_ISDECLINED(mr)) { found = TRUE; } conftab = pr_stash_get_symbol2(PR_SYM_CONF, cmd->argv[0], conftab, &cmd->stash_index, &cmd->stash_hash); } if (cmd->tmp_pool) destroy_pool(cmd->tmp_pool); if (!found) { if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) { pr_log_pri(PR_LOG_WARNING, "fatal: unknown configuration directive " "'%s' on line %u of '%s'", cmd->argv[0], cs->cs_lineno, report_path); exit(1); } else { pr_log_pri(PR_LOG_WARNING, "warning: unknown configuration directive " "'%s' on line %u of '%s'", cmd->argv[0], cs->cs_lineno, report_path); } } } destroy_pool(cmd->pool); } /* Pop this configuration stream from the stack. */ remove_config_source(); pr_fsio_close(fh); destroy_pool(tmp_pool); return 0; }
static array_header *get_sql_filters(pool *p, const char *query_name) { register unsigned int i; cmdtable *sql_cmdtab = NULL; cmd_rec *sql_cmd = NULL; modret_t *sql_res = NULL; array_header *sql_data = NULL; const char **values = NULL; array_header *sql_filters = NULL; sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_lookup", NULL, NULL, NULL); if (sql_cmdtab == NULL) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "unable to execute SQLNamedQuery '%s': mod_sql not loaded", query_name); errno = EPERM; return NULL; } sql_cmd = pr_cmd_alloc(p, 2, "sql_lookup", query_name); sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd); if (sql_res == NULL || MODRET_ISERROR(sql_res)) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "error processing SQLNamedQuery '%s'; check mod_sql logs for details", query_name); errno = EPERM; return NULL; } sql_data = sql_res->data; pr_trace_msg(trace_channel, 9, "SQLNamedQuery '%s' returned item count %d", query_name, sql_data->nelts); if (sql_data->nelts == 0) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "SQLNamedQuery '%s' returned no values", query_name); errno = ENOENT; return NULL; } if (sql_data->nelts % 2 == 1) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "SQLNamedQuery '%s' returned odd number of values (%d), " "expected even number", query_name, sql_data->nelts); errno = EINVAL; return NULL; } values = sql_data->elts; sql_filters = make_array(p, 0, sizeof(struct geoip_filter)); for (i = 0; i < sql_data->nelts; i += 2) { const char *filter_name, *pattern = NULL; struct geoip_filter *filter; filter_name = values[i]; pattern = values[i+1]; filter = make_filter(p, filter_name, pattern); if (filter == NULL) { pr_trace_msg(trace_channel, 3, "unable to use '%s %s' as filter: %s", filter_name, pattern, strerror(errno)); continue; } *((struct geoip_filter **) push_array(sql_filters)) = filter; } return sql_filters; }