示例#1
0
cmd_rec *pr_parser_parse_line(pool *p) {
  register unsigned int i;
  char buf[PR_TUNABLE_BUFFER_SIZE+1], *arg = "", *word = NULL;
  cmd_rec *cmd = NULL;
  pool *sub_pool = NULL;
  array_header *arr = NULL;

  if (p == NULL) {
    errno = EINVAL;
    return NULL;
  }

  memset(buf, '\0', sizeof(buf));
  
  while (pr_parser_read_line(buf, sizeof(buf)-1) != NULL) {
    char *bufp = buf;

    pr_signals_handle();

    /* Build a new pool for the command structure and array */
    sub_pool = make_sub_pool(p);
    pr_pool_tag(sub_pool, "parser cmd subpool");

    cmd = pcalloc(sub_pool, sizeof(cmd_rec));
    cmd->pool = sub_pool;
    cmd->stash_index = -1;
    cmd->stash_hash = 0;

    /* Add each word to the array */
    arr = make_array(cmd->pool, 4, sizeof(char **));
    while ((word = pr_str_get_word(&bufp, 0)) != NULL) {
      char *tmp;

      tmp = get_config_word(cmd->pool, word);

      *((char **) push_array(arr)) = tmp;
      cmd->argc++;
    }

    /* Terminate the array with a NULL. */
    *((char **) push_array(arr)) = NULL;

    /* The array header's job is done, we can forget about it and
     * it will get purged when the command's pool is destroyed.
     */

    cmd->argv = (char **) arr->elts;

    /* Perform a fixup on configuration directives so that:
     *
     *   -argv[0]--  -argv[1]-- ----argv[2]-----
     *   <Option     /etc/adir  /etc/anotherdir>
     *
     *  becomes:
     *
     *   -argv[0]--  -argv[1]-  ----argv[2]----
     *   <Option>    /etc/adir  /etc/anotherdir
     */

    if (cmd->argc &&
        *(cmd->argv[0]) == '<') {
      char *cp = cmd->argv[cmd->argc-1];

      if (*(cp + strlen(cp)-1) == '>' &&
          cmd->argc > 1) {

        if (strncmp(cp, ">", 2) == 0) {
          cmd->argv[cmd->argc-1] = NULL;
          cmd->argc--;

        } else {
          *(cp + strlen(cp)-1) = '\0';
        }

        cp = cmd->argv[0];
        if (*(cp + strlen(cp)-1) != '>') {
          cmd->argv[0] = pstrcat(cmd->pool, cp, ">", NULL);
        }
      }
    }

    if (cmd->argc < 2) {
      arg = pstrdup(cmd->pool, arg);
    }

    for (i = 1; i < cmd->argc; i++) {
      arg = pstrcat(cmd->pool, arg, *arg ? " " : "", cmd->argv[i], NULL);
    }

    cmd->arg = arg;
    return cmd;
  }

  return NULL;
}
示例#2
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;
}
示例#3
0
/* Usage: <IfVersion [!]op version-string|regex> */
MODRET start_ifversion(cmd_rec *cmd) {
  unsigned int ifversion_ctx_count = 1;
  int compared, matched = FALSE, negated = FALSE;
  char buf[PR_TUNABLE_BUFFER_SIZE], *config_line = NULL;
  char *error = NULL, *version_str = NULL, *op_str = NULL;
  size_t op_len;

  if (cmd->argc-1 == 0 ||
      cmd->argc-1 > 2) {
    CONF_ERROR(cmd, "wrong number of parameters");
  }

  if (cmd->argc-1 == 2) {
    op_str = cmd->argv[1];

    if (*op_str == '!' &&
        strlen(op_str) > 1) {
      negated = TRUE;
      op_str++;
    }

    op_len = strlen(op_str);
    version_str = cmd->argv[2];

  } else {
    /* Assume that if only a version-string was supplied, the operator
     * is intended to be the equality operator.
     */
    op_str = "=";
    op_len = 1;
    version_str = cmd->argv[1];
  }

  switch (*op_str) {
    case '=':
      if (*version_str != '/') {
        /* Normal equality comparison */
        compared = compare_version(cmd->tmp_pool, version_str, &error);
        if (error != NULL) {
          CONF_ERROR(cmd, error);
        }

        matched = (compared == 0);
        break;
      }

      /* Otherwise, it's a regular expression */
      if (version_str[strlen(version_str)-1] != '/') {
        CONF_ERROR(cmd, "Missing terminating '/' of regular expression");
      }

      /* Fall through to the next case in order to handle/evaluate the
       * regular expression.  Be sure to remove the bracketing '/' characters
       * for the regex compilation.
       */
      version_str[strlen(version_str)-1] = '\0';
      version_str++;

    case '~': 
      /* Regular expression */
      matched = match_version(cmd->tmp_pool, version_str, &error);
      if (error != NULL) {
        CONF_ERROR(cmd, error);
      }

      break;

    case '<':
      compared = compare_version(cmd->tmp_pool, version_str, &error);
      if (error != NULL) {
        CONF_ERROR(cmd, error);
      }

      if (compared == -1 ||
          (op_len == 2 && compared == 0)) {
        matched = TRUE;
      }

      break;

    case '>':
      compared = compare_version(cmd->tmp_pool, version_str, &error);
      if (error != NULL) {
        CONF_ERROR(cmd, error);
      }

      if (compared == 1 ||
          (op_len == 2 && compared == 0)) {
        matched = TRUE;
      }

      break;

    default:
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown comparison operator '",
        op_str, "'", NULL));
  } 

  if ((matched && !negated) ||
      (!matched && negated)) {
    pr_log_debug(DEBUG3, "%s: using '%s %s' section at line %u",
      cmd->argv[0], cmd->argv[1], cmd->argv[2], pr_parser_get_lineno());
      return PR_HANDLED(cmd);
  }

  pr_log_debug(DEBUG3, "%s: skipping '%s %s' section at line %u",
    cmd->argv[0], cmd->argv[1], cmd->argv[2], pr_parser_get_lineno());

  while (ifversion_ctx_count > 0 &&
         (config_line = pr_parser_read_line(buf, sizeof(buf))) != NULL) {
    pr_signals_handle();

    if (strncasecmp(config_line, "<IfVersion", 10) == 0) {
      ifversion_ctx_count++;
    }

    if (strcasecmp(config_line, "</IfVersion>") == 0) {
      ifversion_ctx_count--;
    }
  }

  /* If there are still unclosed <IfVersion> sections, signal an error.
   */
  if (ifversion_ctx_count > 0) {
    CONF_ERROR(cmd, "unclosed <IfVersion> section");
  }

  return PR_HANDLED(cmd);
}