コード例 #1
0
ファイル: support.c プロジェクト: netdna/proftpd
mode_t file_mode(const char *path) {
  struct stat sbuf;
  mode_t res = 0;

  if (path == NULL) {
    return res;
  }

  pr_fs_clear_cache2(path);
  if (pr_fsio_lstat(path, &sbuf) != -1) {
    if (S_ISLNK(sbuf.st_mode)) {
      res = _symlink(path, (ino_t) 0, 0);

      if (res == 0) {
	/* a dangling symlink, but it exists to rename or delete. */
	res = sbuf.st_mode;
      }

    } else {
      res = sbuf.st_mode;
    }
  }

  return res;
}
コード例 #2
0
ファイル: support.c プロジェクト: netdna/proftpd
/* Return the mode (including the file type) of the file pointed to by symlink
 * PATH, or 0 if it doesn't exist. Catch symlink loops using LAST_INODE and
 * RCOUNT.
 */
static mode_t _symlink(const char *path, ino_t last_inode, int rcount) {
  char buf[PR_TUNABLE_PATH_MAX + 1];
  struct stat sbuf;
  int i;

  if (++rcount >= 32) {
    errno = ELOOP;
    return 0;
  }

  memset(buf, '\0', sizeof(buf));

  i = pr_fsio_readlink(path, buf, sizeof(buf) - 1);
  if (i == -1)
    return (mode_t)0;
  buf[i] = '\0';

  pr_fs_clear_cache2(buf);
  if (pr_fsio_lstat(buf, &sbuf) != -1) {
    if (sbuf.st_ino && (ino_t) sbuf.st_ino == last_inode) {
      errno = ELOOP;
      return 0;
    }

    if (S_ISLNK(sbuf.st_mode)) {
      return _symlink(buf, (ino_t) sbuf.st_ino, rcount);
    }

    return sbuf.st_mode;
  }

  return 0;
}
コード例 #3
0
ファイル: support.c プロジェクト: predever/proftpd
mode_t file_mode(const char *path) {
  struct stat st;
  mode_t mode = 0;

  if (path == NULL) {
    errno = EINVAL;
    return mode;
  }

  pr_fs_clear_cache2(path);
  if (pr_fsio_lstat(path, &st) >= 0) {
    if (S_ISLNK(st.st_mode)) {
      mode = _symlink(path, (ino_t) 0, 0);
      if (mode == 0) {
	/* a dangling symlink, but it exists to rename or delete. */
	mode = st.st_mode;
      }

    } else {
      mode = st.st_mode;
    }
  }

  return mode;
}
コード例 #4
0
ファイル: support.c プロジェクト: predever/proftpd
/* Return the mode (including the file type) of the file pointed to by symlink
 * PATH, or 0 if it doesn't exist. Catch symlink loops using LAST_INODE and
 * RCOUNT.
 */
static mode_t _symlink(const char *path, ino_t last_inode, int rcount) {
  char buf[PR_TUNABLE_PATH_MAX + 1];
  struct stat st;
  int i;

  if (++rcount >= PR_FSIO_MAX_LINK_COUNT) {
    errno = ELOOP;
    return 0;
  }

  memset(buf, '\0', sizeof(buf));

  i = pr_fsio_readlink(path, buf, sizeof(buf) - 1);
  if (i < 0) {
    return (mode_t) 0;
  }
  buf[i] = '\0';

  pr_fs_clear_cache2(buf);
  if (pr_fsio_lstat(buf, &st) >= 0) {
    if (st.st_ino > 0 &&
        (ino_t) st.st_ino == last_inode) {
      errno = ELOOP;
      return 0;
    }

    if (S_ISLNK(st.st_mode)) {
      return _symlink(buf, (ino_t) st.st_ino, rcount);
    }

    return st.st_mode;
  }

  return 0;
}
コード例 #5
0
ファイル: mkhome.c プロジェクト: proftpd/proftpd
static int create_dir(const char *dir, uid_t uid, gid_t gid,
    mode_t mode) {
  mode_t prev_mask;
  struct stat st;
  int res = -1;

  pr_fs_clear_cache2(dir);
  res = pr_fsio_stat(dir, &st);

  if (res == -1 &&
      errno != ENOENT) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "error checking '%s': %s", dir,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  /* The directory already exists. */
  if (res == 0) {
    pr_trace_msg(trace_channel, 8, "'%s' already exists", dir);
    pr_log_debug(DEBUG3, "CreateHome: '%s' already exists", dir);
    return 0;
  }

  /* The given mode is absolute, not subject to any Umask setting. */
  prev_mask = umask(0);

  if (pr_fsio_mkdir(dir, mode) < 0) {
    int xerrno = errno;

    umask(prev_mask);
    pr_log_pri(PR_LOG_WARNING, "error creating '%s': %s", dir,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  umask(prev_mask);

  if (pr_fsio_chown(dir, uid, gid) < 0) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "error setting ownership of '%s': %s", dir,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  pr_trace_msg(trace_channel, 8, "directory '%s' created", dir);
  pr_log_debug(DEBUG6, "CreateHome: directory '%s' created", dir);
  return 0;
}
コード例 #6
0
ファイル: mkhome.c プロジェクト: proftpd/proftpd
/* Walk along a path, making sure that all directories in that path exist,
 * creating them if necessary.
 */
static int create_path(pool *p, const char *path, const char *user,
    uid_t dir_uid, gid_t dir_gid, mode_t dir_mode,
    uid_t dst_uid, gid_t dst_gid, mode_t dst_mode) {
  char *currpath = NULL, *tmppath = NULL;
  struct stat st;

  pr_fs_clear_cache2(path);
  if (pr_fsio_stat(path, &st) == 0) {
    /* Path already exists, nothing to be done. */
    errno = EEXIST;
    return -1;
  }

  /* The special-case values of -1 for dir UID/GID mean that the destination
   * UID/GID should be used for the parent directories.
   */

  if (dir_uid == (uid_t) -1) {
    dir_uid = dst_uid;
  }

  if (dir_gid == (gid_t) -1) {
    dir_gid = dst_gid;
  }

  pr_trace_msg(trace_channel, 5, "creating home directory '%s' for user '%s'",
    path, user);
  pr_log_debug(DEBUG3, "creating home directory '%s' for user '%s'", path,
    user);
  tmppath = pstrdup(p, path);

  currpath = "/";
  while (tmppath && *tmppath) {
    char *currdir = strsep(&tmppath, "/");
    currpath = pdircat(p, currpath, currdir, NULL);

    /* If tmppath is NULL, we are creating the last part of the path, so we
     * use the configured mode, and chown it to the given UID and GID.
     */
    if (tmppath == NULL ||
        (*tmppath == '\0')) {
      create_dir(currpath, dst_uid, dst_gid, dst_mode);

    } else { 
      create_dir(currpath, dir_uid, dir_gid, dir_mode);
    }

    pr_signals_handle();
  }

  pr_trace_msg(trace_channel, 5, "home directory '%s' created", path);
  pr_log_debug(DEBUG3, "home directory '%s' created", path);
  return 0;
}
コード例 #7
0
ファイル: mod_copy.c プロジェクト: laoflch/proftpd
static int create_dir(const char *dir) {
  struct stat st;
  int res = -1;

  pr_fs_clear_cache2(dir);
  res = pr_fsio_stat(dir, &st);

  if (res < 0 &&
      errno != ENOENT) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, MOD_COPY_VERSION ": error checking '%s': %s",
      dir, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  /* The directory already exists. */
  if (res == 0) {
    pr_trace_msg(trace_channel, 9, "path '%s' already exists", dir);
    return 1;
  }

  if (pr_fsio_mkdir(dir, 0777) < 0) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, MOD_COPY_VERSION ": error creating '%s': %s",
      dir, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  pr_log_debug(DEBUG6, MOD_COPY_VERSION ": directory '%s' created", dir);
  return 0;
}
コード例 #8
0
ファイル: mod_copy.c プロジェクト: laoflch/proftpd
static int create_path(pool *p, const char *path) {
  struct stat st;
  char *curr_path, *dup_path; 
 
  pr_fs_clear_cache2(path);
  if (pr_fsio_stat(path, &st) == 0) {
    return 0;
  }
 
  dup_path = pstrdup(p, path);

  curr_path = "/"; 
  while (dup_path &&
         *dup_path) {
    char *curr_dir;
    int res;
    cmd_rec *cmd;
    pool *sub_pool;

    pr_signals_handle();

    curr_dir = strsep(&dup_path, "/");
    curr_path = pdircat(p, curr_path, curr_dir, NULL);

    /* Dispatch fake C_MKD command, e.g. for mod_quotatab */
    sub_pool = pr_pool_create_sz(p, 64);
    cmd = pr_cmd_alloc(sub_pool, 2, pstrdup(sub_pool, C_MKD),
      pstrdup(sub_pool, curr_path));
    cmd->arg = pstrdup(cmd->pool, curr_path);
    cmd->cmd_class = CL_DIRS|CL_WRITE;

    pr_response_clear(&resp_list);
    pr_response_clear(&resp_err_list);

    res = pr_cmd_dispatch_phase(cmd, PRE_CMD, 0);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG3, MOD_COPY_VERSION
        ": creating directory '%s' blocked by MKD handler: %s", curr_path,
        strerror(xerrno));

      pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
      pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
      pr_response_clear(&resp_err_list);

      destroy_pool(sub_pool);

      errno = xerrno;
      return -1;
    }

    res = create_dir(curr_path);
    if (res < 0) {
      pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
      pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
      pr_response_clear(&resp_err_list);

      destroy_pool(sub_pool);
      return -1;
    }

    pr_cmd_dispatch_phase(cmd, POST_CMD, 0);
    pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
    pr_response_clear(&resp_list);
    destroy_pool(sub_pool);
  }

  return 0;
}
コード例 #9
0
ファイル: mod_copy.c プロジェクト: laoflch/proftpd
static int copy_paths(pool *p, const char *from, const char *to) {
  struct stat st;
  int res;
  xaset_t *set;

  set = get_dir_ctxt(p, (char *) to);
  res = pr_filter_allow_path(set, to);
  switch (res) {
    case 0:
      break;

    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": path '%s' denied by PathAllowFilter", to);
      errno = EPERM;
      return -1;

    case PR_FILTER_ERR_FAILS_DENY_FILTER:
      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": path '%s' denied by PathDenyFilter", to);
      errno = EPERM;
      return -1;
  }

  /* Check whether from is a file, a directory, a symlink, or something
   * unsupported.
   */
  res = pr_fsio_lstat(from, &st);
  if (res < 0) {
    int xerrno = errno;

    pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error checking '%s': %s", from,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }
   
  if (S_ISREG(st.st_mode)) { 
    char *abs_path;

    pr_fs_clear_cache2(to);
    res = pr_fsio_stat(to, &st);
    if (res == 0) {
      unsigned char *allow_overwrite;

      allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
      if (allow_overwrite == NULL ||
          *allow_overwrite == FALSE) {
        pr_log_debug(DEBUG6,
          MOD_COPY_VERSION ": AllowOverwrite permission denied for '%s'", to);
        errno = EACCES;
        return -1;
      }
    }

    res = pr_fs_copy_file(from, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error copying file '%s' to '%s': %s", from, to, strerror(xerrno));

      errno = xerrno;
      return -1;
    }

    pr_fs_clear_cache2(to);
    if (pr_fsio_stat(to, &st) < 0) {
      pr_trace_msg(trace_channel, 3,
        "error stat'ing '%s': %s", to, strerror(errno));
    }

    /* Write a TransferLog entry as well. */
    abs_path = dir_abs_path(p, to, TRUE);

    if (session.sf_flags & SF_ANON) {
      xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
        (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a',
        session.anon_user, 'c', "_");

    } else {
      xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
        (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r',
        session.user, 'c', "_");
    }

  } else if (S_ISDIR(st.st_mode)) {
    res = create_path(p, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error creating path '%s': %s", to, strerror(xerrno));

      errno = xerrno;
      return -1;
    }

    res = copy_dir(p, from, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error copying directory '%s' to '%s': %s", from, to,
        strerror(xerrno));

      errno = xerrno;
      return -1;
    }

  } else if (S_ISLNK(st.st_mode)) {
    pr_fs_clear_cache2(to);
    res = pr_fsio_stat(to, &st);
    if (res == 0) {
      unsigned char *allow_overwrite;

      allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
      if (allow_overwrite == NULL ||
          *allow_overwrite == FALSE) {
        pr_log_debug(DEBUG6, MOD_COPY_VERSION
          ": AllowOverwrite permission denied for '%s'", to);
        errno = EACCES;
        return -1;
      }
    }

    res = copy_symlink(p, from, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error copying symlink '%s' to '%s': %s", from, to, strerror(xerrno));

      errno = xerrno;
      return -1;
    }

  } else {
    pr_log_debug(DEBUG7, MOD_COPY_VERSION
      ": unsupported file type for '%s'", from);
    errno = EINVAL;
    return -1;
  }

  return 0;
}
コード例 #10
0
ファイル: mod_copy.c プロジェクト: laoflch/proftpd
static int copy_dir(pool *p, const char *src_dir, const char *dst_dir) {
  DIR *dh = NULL;
  struct dirent *dent = NULL;
  int res = 0;
  pool *iter_pool = NULL;

  dh = opendir(src_dir);
  if (dh == NULL) {
    pr_log_pri(PR_LOG_WARNING, MOD_COPY_VERSION
      ": error reading directory '%s': %s", src_dir, strerror(errno));
    return -1;
  }

  while ((dent = readdir(dh)) != NULL) {
    struct stat st;
    char *src_path, *dst_path;

    pr_signals_handle();

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

    if (iter_pool != NULL) {
      destroy_pool(iter_pool);
    }

    iter_pool = pr_pool_create_sz(p, 128);
    src_path = pdircat(iter_pool, src_dir, dent->d_name, NULL);
    dst_path = pdircat(iter_pool, dst_dir, dent->d_name, NULL);

    if (pr_fsio_lstat(src_path, &st) < 0) {
      pr_log_debug(DEBUG3, MOD_COPY_VERSION
        ": unable to stat '%s' (%s), skipping", src_path, strerror(errno));
      continue;
    }

    /* Is this path to a directory? */
    if (S_ISDIR(st.st_mode)) {
      if (create_path(iter_pool, dst_path) < 0) {
        res = -1;
        break;
      }

      if (copy_dir(iter_pool, src_path, dst_path) < 0) {
        res = -1;
        break;
      }
      continue;

    /* Is this path to a regular file? */
    } else if (S_ISREG(st.st_mode)) {
      cmd_rec *cmd;

      /* Dispatch fake COPY command, e.g. for mod_quotatab */
      cmd = pr_cmd_alloc(iter_pool, 4, pstrdup(iter_pool, "SITE"),
        pstrdup(iter_pool, "COPY"), pstrdup(iter_pool, src_path),
        pstrdup(iter_pool, dst_path));
      cmd->arg = pstrcat(iter_pool, "COPY ", src_path, " ", dst_path, NULL);
      cmd->cmd_class = CL_WRITE;

      pr_response_clear(&resp_list);
      pr_response_clear(&resp_err_list);

      if (pr_cmd_dispatch_phase(cmd, PRE_CMD, 0) < 0) {
        int xerrno = errno;

        pr_log_debug(DEBUG3, MOD_COPY_VERSION
          ": COPY of '%s' to '%s' blocked by COPY handler: %s", src_path,
          dst_path, strerror(xerrno));

        pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
        pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
        pr_response_clear(&resp_err_list);

        errno = xerrno;
        res = -1;
        break;

      } else {
        if (pr_fs_copy_file(src_path, dst_path) < 0) {
          pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
          pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
          pr_response_clear(&resp_err_list);

          res = -1;
          break;

        } else {
          char *abs_path;
          
          pr_cmd_dispatch_phase(cmd, POST_CMD, 0);
          pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
          pr_response_clear(&resp_list);

          /* Write a TransferLog entry as well. */

          pr_fs_clear_cache2(dst_path);
          pr_fsio_stat(dst_path, &st);

          abs_path = dir_abs_path(p, dst_path, TRUE);

          if (session.sf_flags & SF_ANON) {
            xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
               (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a',
               session.anon_user, 'c', "_");

          } else {
            xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
              (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r',
              session.user, 'c', "_");
          }
        }
      }

      continue;

    /* Is this path a symlink? */
    } else if (S_ISLNK(st.st_mode)) {
      if (copy_symlink(iter_pool, src_path, dst_path) < 0) {
        res = -1;
        break;
      }
      continue;

    /* All other file types are skipped */
    } else {
      pr_log_debug(DEBUG3, MOD_COPY_VERSION ": skipping supported file '%s'",
        src_path);
      continue;
    }
  }

  if (iter_pool != NULL) {
    destroy_pool(iter_pool);
  }

  closedir(dh);
  return res;
}
コード例 #11
0
ファイル: parser.c プロジェクト: proftpd/proftpd
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;
}
コード例 #12
0
ファイル: parser.c プロジェクト: proftpd/proftpd
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;
}