Example #1
0
static void
handle_mdtm(struct vsf_session* p_sess)
{
  static struct vsf_sysutil_statbuf* s_p_statbuf;
  int retval;
  if (!vsf_access_check_file(&p_sess->ftp_arg_str))
  {
    vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
    return;
  }
  retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
  if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
  {
    vsf_cmdio_write(p_sess, FTP_FILEFAIL,
                    "Could not get file modification time.");
  }
  else
  {
    static struct mystr s_mdtm_res_str;
    str_alloc_text(&s_mdtm_res_str,
                   vsf_sysutil_statbuf_get_numeric_date(
                     s_p_statbuf, tunable_use_localtime));
    vsf_cmdio_write_str(p_sess, FTP_MDTMOK, &s_mdtm_res_str);
  }
}
Example #2
0
static void
handle_size(struct vsf_session* p_sess)
{
  /* Note - in ASCII mode, are supposed to return the size after taking into
   * account ASCII linefeed conversions. At least this is what wu-ftpd does in
   * version 2.6.1. Proftpd-1.2.0pre fails to do this.
   * I will not do it because it is a potential I/O DoS.
   */
  static struct vsf_sysutil_statbuf* s_p_statbuf;
  int retval;
  if (!vsf_access_check_file(&p_sess->ftp_arg_str))
  {
    vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
    return;
  }
  retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
  if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
  {
    vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Could not get file size.");
  }
  else
  {
    static struct mystr s_size_res_str;
    str_alloc_filesize_t(&s_size_res_str,
                         vsf_sysutil_statbuf_get_size(s_p_statbuf));
    vsf_cmdio_write_str(p_sess, FTP_SIZEOK, &s_size_res_str);
  }
}
Example #3
0
void
vsf_privop_do_file_chown(struct vsf_session* p_sess, int fd)
{
  static struct vsf_sysutil_statbuf* s_p_statbuf;
  vsf_sysutil_fstat(fd, &s_p_statbuf);
  /* Do nothing if it is already owned by the desired user. */
  if (vsf_sysutil_statbuf_get_uid(s_p_statbuf) ==
      p_sess->anon_upload_chown_uid)
  {
    return;
  }
  /* Drop it like a hot potato unless it's a regular file owned by
   * the the anonymous ftp user
   */
  if (p_sess->anon_upload_chown_uid == -1 ||
      !vsf_sysutil_statbuf_is_regfile(s_p_statbuf) ||
      (vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->anon_ftp_uid &&
       vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->guest_user_uid))
  {
    die("invalid fd in cmd_process_chown");
  }
  /* SECURITY! You need an OS which strips SUID/SGID bits on chown(),
   * otherwise a compromise of the FTP user will lead to compromise of
   * the "anon_upload_chown_uid" user (think chmod +s).
   */
  vsf_sysutil_fchown(fd, p_sess->anon_upload_chown_uid, -1);
}
Example #4
0
int
str_fileread(struct mystr* p_str, const char* p_filename, unsigned int maxsize)
{
  int fd;
  int retval = 0;
  filesize_t size;
  char* p_sec_buf = 0;
  struct vsf_sysutil_statbuf* p_stat = 0;
  /* In case we fail, make sure we return an empty string */
  str_empty(p_str);
  fd = vsf_sysutil_open_file(p_filename, kVSFSysUtilOpenReadOnly);
  if (vsf_sysutil_retval_is_error(fd))
  {
    return fd;
  }
  vsf_sysutil_fstat(fd, &p_stat);
  if (vsf_sysutil_statbuf_is_regfile(p_stat))
  {
    size = vsf_sysutil_statbuf_get_size(p_stat);
    if (size > maxsize)
    {
      size = maxsize;
    }
    vsf_secbuf_alloc(&p_sec_buf, (unsigned int) size);

    retval = vsf_sysutil_read_loop(fd, p_sec_buf, (unsigned int) size);
    if (vsf_sysutil_retval_is_error(retval))
    {
      goto free_out;
    }
    else if ((unsigned int) retval != size)
    {
      die("read size mismatch");
    }
    str_alloc_memchunk(p_str, p_sec_buf, size);
  }
free_out:
  vsf_sysutil_free(p_stat);
  vsf_secbuf_free(&p_sec_buf);
  vsf_sysutil_close(fd);
  return retval;
}
Example #5
0
static void
handle_retr(struct vsf_session* p_sess)
{
  static struct mystr s_mark_str;
  static struct vsf_sysutil_statbuf* s_p_statbuf;
  struct vsf_transfer_ret trans_ret;
  int retval;
  int remote_fd;
  int opened_file;
  int is_ascii = 0;
  filesize_t offset = p_sess->restart_pos;
  p_sess->restart_pos = 0;
  if (!pasv_active(p_sess) && !port_active(p_sess))
  {
    vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first.");
    return;
  }
  if (p_sess->is_ascii && offset != 0)
  {
    vsf_cmdio_write(p_sess, FTP_FILEFAIL,
                    "No support for resume of ASCII transfer.");
    return;
  }
  opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
  if (vsf_sysutil_retval_is_error(opened_file))
  {
    vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
    return;
  }
  vsf_sysutil_fstat(opened_file, &s_p_statbuf);
  /* No games please */
  if (!vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
  {
    /* Note - pretend open failed */
    vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
    goto file_close_out;
  }
  /* Optionally, we'll be paranoid and only serve publicly readable stuff */
  if (p_sess->is_anonymous && tunable_anon_world_readable_only &&
      !vsf_sysutil_statbuf_is_readable_other(s_p_statbuf))
  {
    vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
    goto file_close_out;
  }
  /* Set the download offset (from REST) if any */
  if (offset != 0)
  {
    vsf_sysutil_lseek_to(opened_file, offset);
  }
  remote_fd = get_remote_transfer_fd(p_sess);
  if (vsf_sysutil_retval_is_error(remote_fd))
  {
    goto port_pasv_cleanup_out;
  }
  vsf_log_start_entry(p_sess, kVSFLogEntryDownload);
  str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
  prepend_path_to_filename(&p_sess->log_str);
  str_alloc_text(&s_mark_str, "Opening ");
  if (tunable_ascii_download_enable && p_sess->is_ascii)
  {
    str_append_text(&s_mark_str, "ASCII");
    is_ascii = 1;
  }
  else
  {
    str_append_text(&s_mark_str, "BINARY");
  }
  str_append_text(&s_mark_str, " mode data connection for ");
  str_append_str(&s_mark_str, &p_sess->ftp_arg_str);
  str_append_text(&s_mark_str, " (");
  str_append_filesize_t(&s_mark_str,
                        vsf_sysutil_statbuf_get_size(s_p_statbuf));
  str_append_text(&s_mark_str, " bytes).");
  vsf_cmdio_write_str(p_sess, FTP_DATACONN, &s_mark_str);
  trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
                                          opened_file, 0, is_ascii);
  p_sess->transfer_size = trans_ret.transferred;
  retval = dispose_remote_transfer_fd(p_sess);
  /* Log _after_ the blocking dispose call, so we get transfer times right */
  if (trans_ret.retval == 0 && retval == 0)
  {
    vsf_log_do_log(p_sess, 1);
  }
  else
  {
    vsf_log_do_log(p_sess, 0);
  }
port_pasv_cleanup_out:
  port_cleanup(p_sess);
  pasv_cleanup(p_sess);
file_close_out:
  vsf_sysutil_close(opened_file);
}
Example #6
0
void
vsf_parseconf_load_file(const char* p_filename, int errs_fatal)
{
  struct mystr config_file_str = INIT_MYSTR;
  struct mystr config_setting_str = INIT_MYSTR;
  struct mystr config_value_str = INIT_MYSTR;
  unsigned int str_pos = 0;
  int retval;
  if (!p_filename)
  {
    p_filename = s_p_saved_filename;
  }
  else
  {
    if (s_p_saved_filename)
    {
      vsf_sysutil_free((char*)s_p_saved_filename);
    }
    s_p_saved_filename = vsf_sysutil_strdup(p_filename);
  }
  if (!p_filename)
  {
    bug("null filename in vsf_parseconf_load_file");
  }
  retval = str_fileread(&config_file_str, p_filename, VSFTP_CONF_FILE_MAX);
  if (vsf_sysutil_retval_is_error(retval))
  {
    if (errs_fatal)
    {
      die2("cannot read config file: ", p_filename);
    }
    else
    {
      str_free(&config_file_str);
      return;
    }
  }
  {
    struct vsf_sysutil_statbuf* p_statbuf = 0;
    retval = vsf_sysutil_stat(p_filename, &p_statbuf);
    /* Security: check current user owns the config file. These are sanity
     * checks for the admin, and are NOT designed to be checks safe from
     * race conditions.
     */
    if (vsf_sysutil_retval_is_error(retval) ||
        vsf_sysutil_statbuf_get_uid(p_statbuf) != vsf_sysutil_getuid() ||
        !vsf_sysutil_statbuf_is_regfile(p_statbuf))
    {
      die("config file not owned by correct user, or not a file");
    }
    vsf_sysutil_free(p_statbuf);
  }
  while (str_getline(&config_file_str, &config_setting_str, &str_pos))
  {
    if (str_isempty(&config_setting_str) ||
        str_get_char_at(&config_setting_str, 0) == '#' ||
        str_all_space(&config_setting_str))
    {
      continue;
    }
    vsf_parseconf_load_setting(str_getbuf(&config_setting_str), errs_fatal);
  }
  str_free(&config_file_str);
  str_free(&config_setting_str);
  str_free(&config_value_str);
}