Beispiel #1
0
static int is_tmp_file(const char *file) {
  register unsigned int i;

  for (i = 0; tmpfile_patterns[i]; i++) {
    if (pr_fnmatch(tmpfile_patterns[i], file, PR_FNM_PERIOD) == 0) {
      return TRUE;
    }
  }

  return FALSE;
}
Beispiel #2
0
int pr_netaddr_fnmatch(pr_netaddr_t *na, const char *pattern, int flags) {

  /* Note: I'm still not sure why proftpd bundles an fnmatch(3)
   * implementation rather than using the system library's implementation.
   * Needs looking into.
   *
   * The FNM_CASEFOLD flag is a GNU extension; perhaps the bundled
   * implementation was added to make that flag available on other platforms.
   */
  int match_flags = PR_FNM_NOESCAPE|PR_FNM_CASEFOLD;

  if (!na || !pattern) {
    errno = EINVAL;
    return -1;
  }

  if (flags & PR_NETADDR_MATCH_DNS) {
    const char *dnsstr = pr_netaddr_get_dnsstr(na);

    if (pr_fnmatch(pattern, dnsstr, match_flags) == 0) {
      pr_trace_msg(trace_channel, 6, "DNS name '%s' matches pattern '%s'",
        dnsstr, pattern);
      return TRUE;
    }
  }

  if (flags & PR_NETADDR_MATCH_IP) {
    const char *ipstr = pr_netaddr_get_ipstr(na);

    if (pr_fnmatch(pattern, ipstr, match_flags) == 0) {
      pr_trace_msg(trace_channel, 6, "DNS name '%s' matches pattern '%s'",
        ipstr, pattern);
      return TRUE;
    }
  }

  pr_trace_msg(trace_channel, 4, "addr %s does not match pattern '%s'",
    pr_netaddr_get_ipstr(na), pattern);
  return FALSE;
}
Beispiel #3
0
static int parse_wildcard_config_path(pool *p, const char *path,
    unsigned int depth) {
  register unsigned int i;
  int res, xerrno;
  pool *tmp_pool;
  array_header *globbed_dirs = NULL;
  const char *component = NULL, *parent_path = NULL, *suffix_path = NULL;
  struct stat st;
  size_t path_len, component_len;
  char *name_pattern = NULL;
  void *dirh = NULL;
  struct dirent *dent = NULL;

  if (depth > PR_PARSER_INCLUDE_MAX_DEPTH) {
    pr_log_pri(PR_LOG_WARNING, "error: resolving wildcard pattern in '%s' "
      "exceeded maximum filesystem depth (%u)", path,
      (unsigned int) PR_PARSER_INCLUDE_MAX_DEPTH);
    errno = EINVAL;
    return -1;
  }

  path_len = strlen(path);
  if (path_len < 2) {
    pr_trace_msg(trace_channel, 7, "path '%s' too short to be wildcard path",
      path);

    /* The first character must be a slash, and we need at least one more
     * character in the path as a glob character.
     */
    errno = EINVAL;
    return -1;
  }

  tmp_pool = make_sub_pool(p);
  pr_pool_tag(tmp_pool, "Include sub-pool");

  /* We need to find the first component of the path which contains glob
   * characters.  We then use the path up to the previous component as the
   * parent directory to open, and the glob-bearing component as the filter
   * for directories within the parent.
   */

  component = path + 1;
  while (TRUE) {
    int last_component = FALSE;
    char *ptr;

    pr_signals_handle();

    ptr = strchr(component, '/');
    if (ptr != NULL) {
      component_len = ptr - component;

    } else {
      component_len = strlen(component);
      last_component = TRUE;
    }

    if (memchr(component, (int) '*', component_len) != NULL ||
        memchr(component, (int) '?', component_len) != NULL ||
        memchr(component, (int) '[', component_len) != NULL) {

      name_pattern = pstrndup(tmp_pool, component, component_len);

      if (parent_path == NULL) {
        parent_path = pstrndup(tmp_pool, "/", 1);
      }

      if (ptr != NULL) {
        suffix_path = pstrdup(tmp_pool, ptr + 1);
      }

      break;
    }

    if (parent_path != NULL) {
      parent_path = pdircat(tmp_pool, parent_path,
        pstrndup(tmp_pool, component, component_len), NULL);

    } else {
      parent_path = pstrndup(tmp_pool, "/", 1);
    }

    if (last_component) {
      break;
    }

    component = ptr + 1;
  }

  if (name_pattern == NULL) {
    pr_trace_msg(trace_channel, 4,
      "unable to process invalid, non-globbed path '%s'", path);
    errno = ENOENT;
    return -1;
  }

  pr_fs_clear_cache2(parent_path);
  if (pr_fsio_lstat(parent_path, &st) < 0) {
    xerrno = errno;

    pr_log_pri(PR_LOG_WARNING,
      "error: failed to check configuration path '%s': %s", parent_path,
      strerror(xerrno));

    destroy_pool(tmp_pool);
    errno = xerrno;
    return -1;
  }

  if (S_ISLNK(st.st_mode) &&
      !(parser_include_opts & PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS)) {
    pr_log_pri(PR_LOG_WARNING,
      "error: cannot read configuration path '%s': Symbolic link", parent_path);
    destroy_pool(tmp_pool);
    errno = ENOTDIR;
    return -1;
  }

  pr_log_pri(PR_LOG_DEBUG,
    "processing configuration directory '%s' using pattern '%s', suffix '%s'",
    parent_path, name_pattern, suffix_path);

  dirh = pr_fsio_opendir(parent_path);
  if (dirh == NULL) {
    pr_log_pri(PR_LOG_WARNING,
      "error: unable to open configuration directory '%s': %s", parent_path,
      strerror(errno));
    destroy_pool(tmp_pool);
    errno = EINVAL;
    return -1;
  }

  globbed_dirs = make_array(tmp_pool, 0, sizeof(char *));

  while ((dent = pr_fsio_readdir(dirh)) != NULL) {
    pr_signals_handle();

    if (strncmp(dent->d_name, ".", 2) == 0 ||
        strncmp(dent->d_name, "..", 3) == 0) {
      continue;
    }

    if (parser_include_opts & PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES) {
      if (is_tmp_file(dent->d_name) == TRUE) {
        pr_trace_msg(trace_channel, 19,
          "ignoring temporary file '%s' found in directory '%s'", dent->d_name,
          parent_path);
        continue;
      }
    }

    if (pr_fnmatch(name_pattern, dent->d_name, PR_FNM_PERIOD) == 0) {
      pr_trace_msg(trace_channel, 17,
        "matched '%s' path with wildcard pattern '%s'", dent->d_name,
        name_pattern);

      *((char **) push_array(globbed_dirs)) = pdircat(tmp_pool, parent_path,
        dent->d_name, suffix_path, NULL);
    }
  }

  pr_fsio_closedir(dirh);

  if (globbed_dirs->nelts == 0) {
    pr_log_pri(PR_LOG_WARNING,
      "error: no matches found for wildcard directory '%s'", path);
    destroy_pool(tmp_pool);
    errno = ENOENT;
    return -1;
  }

  depth++;

  qsort((void *) globbed_dirs->elts, globbed_dirs->nelts, sizeof(char *),
    config_filename_cmp);

  for (i = 0; i < globbed_dirs->nelts; i++) {
    const char *globbed_dir;

    globbed_dir = ((const char **) globbed_dirs->elts)[i];
    res = parse_config_path2(p, globbed_dir, depth);
    if (res < 0) {
      xerrno = errno;

      pr_trace_msg(trace_channel, 7, "error parsing wildcard path '%s': %s",
        globbed_dir, strerror(xerrno));

      destroy_pool(tmp_pool);
      errno = xerrno;
      return -1;
    }
  }

  destroy_pool(tmp_pool);
  return 0;
}
Beispiel #4
0
int parse_config_path2(pool *p, const char *path, unsigned int depth) {
  struct stat st;
  int have_glob;
  void *dirh;
  struct dirent *dent;
  array_header *file_list;
  char *dup_path, *ptr;
  pool *tmp_pool;

  if (p == NULL ||
      path == NULL ||
      (depth > PR_PARSER_INCLUDE_MAX_DEPTH)) {
    errno = EINVAL;
    return -1;
  }

  if (pr_fs_valid_path(path) < 0) {
    errno = EINVAL;
    return -1;
  }

  have_glob = pr_str_is_fnmatch(path);
  if (have_glob) {
    /* Even though the path may be valid, it also may not be a filesystem
     * path; consider custom FSIO modules.  Thus if the path does not start
     * with a slash, it should not be treated as having globs.
     */
    if (*path != '/') {
      have_glob = FALSE;
    }
  }

  pr_fs_clear_cache2(path);

  if (have_glob) {
    pr_trace_msg(trace_channel, 19, "parsing '%s' as a globbed path", path);
  }

  if (!have_glob &&
      pr_fsio_lstat(path, &st) < 0) {
    return -1;
  }

  /* If path is not a glob pattern, and is a symlink OR is not a directory,
   * then use the normal parsing function for the file.
   */
  if (have_glob == FALSE &&
      (S_ISLNK(st.st_mode) ||
       !S_ISDIR(st.st_mode))) {
    int res, xerrno;

    PRIVS_ROOT
    res = pr_parser_parse_file(p, path, NULL, 0);
    xerrno = errno;
    PRIVS_RELINQUISH

    errno = xerrno;
    return res;
  }

  tmp_pool = make_sub_pool(p);
  pr_pool_tag(tmp_pool, "Include sub-pool");

  /* Handle the glob/directory. */
  dup_path = pstrdup(tmp_pool, path);

  ptr = strrchr(dup_path, '/');

  if (have_glob) {
    int have_glob_dir;

    /* Note that we know, by definition, that ptr CANNOT be null here; dup_path
     * is a duplicate of path, and the first character (if nothing else) of
     * path MUST be a slash, per earlier checks.
     */
    *ptr = '\0';

    /* We just changed ptr, thus we DO need to check whether the now-modified
     * path contains fnmatch(3) characters again.
     */
    have_glob_dir = pr_str_is_fnmatch(dup_path);
    if (have_glob_dir) {
      const char *glob_dir;

      if (parser_include_opts & PR_PARSER_INCLUDE_OPT_IGNORE_WILDCARDS) {
        pr_log_pri(PR_LOG_WARNING, "error: wildcard patterns not allowed in "
          "configuration directory name '%s'", dup_path);
        destroy_pool(tmp_pool);
        errno = EINVAL;
        return -1;
      }

      *ptr = '/';
      glob_dir = pstrdup(p, dup_path);
      destroy_pool(tmp_pool);

      return parse_wildcard_config_path(p, glob_dir, depth);
    }

    ptr++;

    /* Check the directory component. */
    pr_fs_clear_cache2(dup_path);
    if (pr_fsio_lstat(dup_path, &st) < 0) {
      int xerrno = errno;

      pr_log_pri(PR_LOG_WARNING,
        "error: failed to check configuration path '%s': %s", dup_path,
        strerror(xerrno));

      destroy_pool(tmp_pool);
      errno = xerrno;
      return -1;
    }

    if (S_ISLNK(st.st_mode) &&
        !(parser_include_opts & PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS)) {
      pr_log_pri(PR_LOG_WARNING,
        "error: cannot read configuration path '%s': Symbolic link", path);
      destroy_pool(tmp_pool);
      errno = ENOTDIR;
      return -1;
    }

    if (have_glob_dir == FALSE &&
        pr_str_is_fnmatch(ptr) == FALSE) {
      pr_log_pri(PR_LOG_WARNING,
        "error: wildcard pattern required for file '%s'", ptr);
      destroy_pool(tmp_pool);
      errno = EINVAL;
      return -1;
    }
  }

  pr_log_pri(PR_LOG_DEBUG, "processing configuration directory '%s'", dup_path);

  dirh = pr_fsio_opendir(dup_path);
  if (dirh == NULL) {
    pr_log_pri(PR_LOG_WARNING,
      "error: unable to open configuration directory '%s': %s", dup_path,
      strerror(errno));
    destroy_pool(tmp_pool);
    errno = EINVAL;
    return -1;
  }

  file_list = make_array(tmp_pool, 0, sizeof(char *));

  while ((dent = pr_fsio_readdir(dirh)) != NULL) {
    pr_signals_handle();

    if (strncmp(dent->d_name, ".", 2) == 0 ||
        strncmp(dent->d_name, "..", 3) == 0) {
      continue;
    }

    if (parser_include_opts & PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES) {
      if (is_tmp_file(dent->d_name) == TRUE) {
        pr_trace_msg(trace_channel, 19,
          "ignoring temporary file '%s' found in directory '%s'", dent->d_name,
          dup_path);
        continue;
      }
    }

    if (have_glob == FALSE ||
        (ptr != NULL &&
         pr_fnmatch(ptr, dent->d_name, PR_FNM_PERIOD) == 0)) {
      *((char **) push_array(file_list)) = pdircat(tmp_pool, dup_path,
        dent->d_name, NULL);
    }
  }

  pr_fsio_closedir(dirh);

  if (file_list->nelts) {
    register unsigned int i;

    qsort((void *) file_list->elts, file_list->nelts, sizeof(char *),
      config_filename_cmp);

    for (i = 0; i < file_list->nelts; i++) {
      int res, xerrno;
      char *file;

      file = ((char **) file_list->elts)[i];

      /* Make sure we always parse the files with root privs.  The
       * previously parsed file might have had root privs relinquished
       * (e.g. by its directive handlers), but when we first start up,
       * we have root privs.  See Bug#3855.
       */
      PRIVS_ROOT
      res = pr_parser_parse_file(tmp_pool, file, NULL, 0);
      xerrno = errno;
      PRIVS_RELINQUISH

      if (res < 0) {
        pr_log_pri(PR_LOG_WARNING,
          "error: unable to open parse file '%s': %s", file,
          strerror(xerrno));
      }
    }
  }

  destroy_pool(tmp_pool);
  return 0;
}
static pr_fh_t *counter_get_fh(pool *p, const char *path) {
  struct counter_fh *iter, *cfh = NULL;
  const char *abs_path;

  /* Find the CounterFile handle to use for the given path, if any. */
  if (counter_fhs == NULL) {
    errno = ENOENT;
    return NULL;
  }

  if (session.chroot_path) {
    abs_path = counter_abs_path(p, path, FALSE);

  } else {
    abs_path = path;
  }

  /* In order to handle globs, we do two passes.  On the first pass,
   * we look for the closest-matching glob area.  On the second pass,
   * we look for any closest-matching non-glob area.  This means that
   * exact matches override glob matches (as they should).
   */

  for (iter = (struct counter_fh *) counter_fhs->xas_list; iter;
     iter = iter->next) {
    pr_signals_handle();

    if (!iter->isglob) {
      continue;
    }

    if (cfh == NULL) {
      /* Haven't found anything matching yet. */
      if (pr_fnmatch(iter->area, abs_path, 0) == 0) {
        cfh = iter;
      }

    } else {
      /* Have a previous match.  Is this a closer matching area? */
      if (iter->arealen > cfh->arealen &&
          pr_fnmatch(iter->area, abs_path, 0) == 0) {
        cfh = iter;
      }
    }
  }

  for (iter = (struct counter_fh *) counter_fhs->xas_list; iter;
     iter = iter->next) {

    if (iter->isglob) {
      continue;
    }

    if (cfh == NULL) {
      /* Haven't found anything matching yet. */
      if (strncmp(iter->area, abs_path, iter->arealen) == 0) {
        cfh = iter;
      }

    } else {
      /* Have a previous match.  Is this a closer matching area? */
      if (iter->arealen > cfh->arealen &&
          strncmp(iter->area, abs_path, iter->arealen) == 0) {
        cfh = iter;
      }
    }
  }

  if (cfh != NULL) {
    (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION,
      "using CounterFile '%s' covering area '%s' for path '%s'",
      cfh->fh->fh_path, cfh->area, path);
    return cfh->fh;
  }

  errno = ENOENT;
  return NULL;
}
Beispiel #6
0
pr_namebind_t *pr_namebind_find(const char *name, pr_netaddr_t *addr,
    unsigned int port, unsigned char skip_inactive) {
  pr_ipbind_t *ipbind = NULL;
  pr_namebind_t *namebind = NULL;

  if (name == NULL ||
      addr == NULL) {
    errno = EINVAL;
    return NULL;
  }

  /* First, find an active ipbind for the given addr/port */
  ipbind = pr_ipbind_find(addr, port, skip_inactive);

  if (ipbind == NULL) {
    pr_netaddr_t wildcard_addr;
    int addr_family;

    /* If not found, look for the wildcard address. */

    addr_family = pr_netaddr_get_family(addr);
    pr_netaddr_clear(&wildcard_addr);
    pr_netaddr_set_family(&wildcard_addr, addr_family);
    pr_netaddr_set_sockaddr_any(&wildcard_addr);

    ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE);
#ifdef PR_USE_IPV6
    if (ipbind == FALSE &&
        addr_family == AF_INET6 &&
        pr_netaddr_use_ipv6()) {

      /* No IPv6 wildcard address found; try the IPv4 wildcard address. */
      pr_netaddr_clear(&wildcard_addr);
      pr_netaddr_set_family(&wildcard_addr, AF_INET);
      pr_netaddr_set_sockaddr_any(&wildcard_addr);

      ipbind = pr_ipbind_find(&wildcard_addr, port, FALSE);
    }
#endif /* PR_USE_IPV6 */
  }

  if (ipbind == NULL) {
    errno = ENOENT;
    return NULL;
  }

  if (!ipbind->ib_namebinds) {
    return NULL;

  } else {
    register unsigned int i = 0;
    pr_namebind_t **namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;

    for (i = 0; i < ipbind->ib_namebinds->nelts; i++) {
      namebind = namebinds[i];

      /* Skip inactive namebinds */
      if (skip_inactive == TRUE &&
          namebind != NULL &&
          namebind->nb_isactive == FALSE) {
        continue;
      }

      /* At present, this looks for an exactly matching name.  In the future,
       * we may want to have something like Apache's matching scheme, which
       * looks for the most specific domain to the most general.  Note that
       * that scheme, however, is specific to DNS; should any other naming
       * scheme be desired, that sort of matching will be unnecessary.
       */
      if (namebind != NULL &&
          namebind->nb_name != NULL) {

        if (namebind->nb_iswildcard == FALSE) {
          if (strcasecmp(namebind->nb_name, name) == 0)
            return namebind;
          }

        } else {
          int match_flags = PR_FNM_NOESCAPE|PR_FNM_CASEFOLD;

          if (pr_fnmatch(namebind->nb_name, name, match_flags) == 0) {
            pr_trace_msg(trace_channel, 9,
              "matched name '%s' against pattern '%s'", name,
              namebind->nb_name);
            return namebind;
          }

          pr_trace_msg(trace_channel, 9,
            "failed to match name '%s' against pattern '%s'", name,
            namebind->nb_name);
        }
    }
  }

  return NULL;
}