config_rec *pr_parser_config_ctxt_open(const char *name) { config_rec *c = NULL, *parent = *parser_curr_config; pool *c_pool = NULL, *parent_pool = NULL; xaset_t **set = NULL; if (!name) { errno = EINVAL; return NULL; } if (parent) { parent_pool = parent->pool; set = &parent->subset; } else { parent_pool = (*parser_curr_server)->pool; set = &(*parser_curr_server)->conf; } /* Allocate a sub-pool for this config_rec. * * Note: special exception for <Global> configs: the parent pool is * 'global_config_pool' (a pool just for that context), not the pool of the * parent server. This keeps <Global> config recs from being freed * prematurely, and helps to avoid memory leaks. */ if (strncasecmp(name, "<Global>", 9) == 0) { if (!global_config_pool) { global_config_pool = make_sub_pool(permanent_pool); pr_pool_tag(global_config_pool, "<Global> Pool"); } parent_pool = global_config_pool; } c_pool = make_sub_pool(parent_pool); pr_pool_tag(c_pool, "sub-config pool"); c = (config_rec *) pcalloc(c_pool, sizeof(config_rec)); if (!*set) { pool *set_pool = make_sub_pool(parent_pool); *set = xaset_create(set_pool, NULL); (*set)->pool = set_pool; } xaset_insert(*set, (xasetmember_t *) c); c->pool = c_pool; c->set = *set; c->parent = parent; c->name = pstrdup(c->pool, name); if (parent) { if (parent->config_type == CONF_DYNDIR) c->flags |= CF_DYNAMIC; } add_config_ctxt(c); return c; }
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; }
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; char buf[PR_TUNABLE_BUFFER_SIZE+1]; 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); } memset(buf, '\0', sizeof(buf)); while (pr_parser_read_line(buf, sizeof(buf)-1) != 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) { 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); errno = EPERM; return -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 == 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); } 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', sizeof(buf)); } /* Pop this configuration stream from the stack. */ remove_config_source(); pr_fsio_close(fh); destroy_pool(tmp_pool); return 0; }