예제 #1
0
파일: fsio.c 프로젝트: UIKit0/proftpd
END_TEST

START_TEST (fs_clean_path2_test) {
  char res[PR_TUNABLE_PATH_MAX+1], *path, *expected;

  res[sizeof(res)-1] = '\0';
  path = "test.txt";
  pr_fs_clean_path2(path, res, sizeof(res)-1, 0);
  fail_unless(strcmp(res, path) == 0, "Expected cleaned path '%s', got '%s'",
    path, res);

  res[sizeof(res)-1] = '\0';
  path = "/./test.txt";
  pr_fs_clean_path2(path, res, sizeof(res)-1, 0);

  expected = "/test.txt";
  fail_unless(strcmp(res, expected) == 0,
    "Expected cleaned path '%s', got '%s'", expected, res);

  res[sizeof(res)-1] = '\0';
  path = "test.d///test.txt";
  pr_fs_clean_path2(path, res, sizeof(res)-1, 0);

  expected = "test.d/test.txt";
  fail_unless(strcmp(res, expected) == 0,
    "Expected cleaned path '%s', got '%s'", expected, res);

  res[sizeof(res)-1] = '\0';
  path = "/test.d///test.txt";
  pr_fs_clean_path2(path, res, sizeof(res)-1,
    PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH);

  expected = "/test.d/test.txt";
  fail_unless(strcmp(res, expected) == 0,
    "Expected cleaned path '%s', got '%s'", expected, res);

}
예제 #2
0
파일: support.c 프로젝트: Nakor78/proftpd
/* Performs chroot-aware handling of symlinks. */
int dir_readlink(pool *p, const char *path, char *buf, size_t bufsz,
    int flags) {
  int is_abs_dst, clean_flags, len, res = -1;
  size_t chroot_pathlen = 0, adj_pathlen = 0;
  char *dst_path, *adj_path;
  pool *tmp_pool;

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

  if (bufsz == 0) {
    return 0;
  }

  len = pr_fsio_readlink(path, buf, bufsz);
  if (len < 0) {
    return -1;
  }

  if (len == 0 ||
      len == bufsz) {
    /* If we read nothing in, OR if the given buffer was completely
     * filled WITHOUT terminating NUL, there's really nothing we can/should
     * be doing.
     */
    return len;
  }

  is_abs_dst = FALSE;
  if (*buf == '/') {
    is_abs_dst = TRUE;
  }

  if (session.chroot_path != NULL) {
    chroot_pathlen = strlen(session.chroot_path);
  }

  if (chroot_pathlen <= 1) {
    char *ptr;

    if (is_abs_dst == TRUE ||
        !(flags & PR_DIR_READLINK_FL_HANDLE_REL_PATH)) {
      return len;
    }

    /* Since we have a relative destination path, we will concat it
     * with the source path's directory, then clean up that path.
     */
    ptr = strrchr(path, '/');
    if (ptr != NULL &&
        ptr != path) {
      char *parent_dir;

      tmp_pool = make_sub_pool(p);
      pr_pool_tag(tmp_pool, "dir_readlink pool");

      parent_dir = pstrndup(tmp_pool, path, (ptr - path));
      dst_path = pdircat(tmp_pool, parent_dir, buf, NULL);

      adj_pathlen = bufsz + 1;
      adj_path = pcalloc(tmp_pool, adj_pathlen);

      res = pr_fs_clean_path2(dst_path, adj_path, adj_pathlen-1, 0);
      if (res == 0) {
        pr_trace_msg("fsio", 19,
          "cleaned symlink path '%s', yielding '%s'", dst_path, adj_path);
        dst_path = adj_path;
      }

      pr_trace_msg("fsio", 19,
        "adjusted relative symlink path '%s', yielding '%s'", buf, dst_path);

      memset(buf, '\0', bufsz);
      sstrncpy(buf, dst_path, bufsz);
      len = strlen(buf);
      destroy_pool(tmp_pool);
    }

    return len;
  }

  if (is_abs_dst == FALSE) {
    /* If we are to ignore relative destination paths, return now. */
    if (!(flags & PR_DIR_READLINK_FL_HANDLE_REL_PATH)) {
      return len;
    }
  }

  if (is_abs_dst == TRUE &&
      len < chroot_pathlen) {
    /* If the destination path length is shorter than the chroot path,
     * AND the destination path is absolute, then by definition it CANNOT
     * point within the chroot.
     */
    return len;
  }

  tmp_pool = make_sub_pool(p);
  pr_pool_tag(tmp_pool, "dir_readlink pool");

  dst_path = pstrdup(tmp_pool, buf);
  if (is_abs_dst == FALSE) {
    char *ptr;

    /* Since we have a relative destination path, we will concat it
     * with the source path's directory, then clean up that path.
     */

    ptr = strrchr(path, '/');
    if (ptr != NULL &&
        ptr != path) {
      char *parent_dir;

      parent_dir = pstrndup(tmp_pool, path, (ptr - path));
      dst_path = pdircat(tmp_pool, parent_dir, dst_path, NULL);

    } else {
      dst_path = pdircat(tmp_pool, path, dst_path, NULL);
    }
  }

  adj_pathlen = bufsz + 1;
  adj_path = pcalloc(tmp_pool, adj_pathlen);

  clean_flags = PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH;
  res = pr_fs_clean_path2(dst_path, adj_path, adj_pathlen-1, clean_flags);
  if (res == 0) {
    pr_trace_msg("fsio", 19,
      "cleaned symlink path '%s', yielding '%s'", dst_path, adj_path);
    dst_path = adj_path;

    memset(buf, '\0', bufsz);
    sstrncpy(buf, dst_path, bufsz);
    len = strlen(dst_path);
  }

  if (strncmp(dst_path, session.chroot_path, chroot_pathlen) == 0 &&
      *(dst_path + chroot_pathlen) == '/') {
    char *ptr;

    ptr = dst_path + chroot_pathlen;

    if (is_abs_dst == FALSE &&
        res == 0) {
      /* If we originally had a relative destination path, AND we cleaned
       * that adjusted path, then we should try to re-adjust the path
       * back to being a relative path.  Within reason.
       */
      ptr = pstrcat(tmp_pool, ".", ptr, NULL);
    }

    /* Since we are making the destination path shorter, the given buffer
     * (which was big enough for the original destination path) should
     * always be large enough for this adjusted, shorter version.  Right?
     */
    pr_trace_msg("fsio", 19,
      "adjusted symlink path '%s' for chroot '%s', yielding '%s'",
      dst_path, session.chroot_path, ptr);

    memset(buf, '\0', bufsz);
    sstrncpy(buf, ptr, bufsz);
    len = strlen(buf);
  }

  destroy_pool(tmp_pool);
  return len;
}