示例#1
0
文件: parser.c 项目: proftpd/proftpd
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;
}
示例#2
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;

  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;
}