Ejemplo n.º 1
0
static void
process_login_req(struct vsf_session* p_sess)
{
  enum EVSFPrivopLoginResult e_login_result = kVSFLoginNull;
  char cmd;
  /* Blocks */
  cmd = priv_sock_get_cmd(p_sess->parent_fd);
  if (cmd != PRIV_SOCK_LOGIN)
  {
    die("bad request");
  }
  /* Get username and password - we must distrust these */
  {
    struct mystr password_str = INIT_MYSTR;
    priv_sock_get_str(p_sess->parent_fd, &p_sess->user_str);
    priv_sock_get_str(p_sess->parent_fd, &password_str);
    p_sess->control_use_ssl = priv_sock_get_int(p_sess->parent_fd);
    p_sess->data_use_ssl = priv_sock_get_int(p_sess->parent_fd);
    if (!tunable_ssl_enable)
    {
      p_sess->control_use_ssl = 0;
      p_sess->data_use_ssl = 0;
    }
    e_login_result = vsf_privop_do_login(p_sess, &password_str);
    str_free(&password_str);
  }
  switch (e_login_result)
  {
    case kVSFLoginFail:
      priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_BAD);
      return;
      break;
    case kVSFLoginAnon:
      str_alloc_text(&p_sess->user_str, tunable_ftp_username);
      common_do_login(p_sess, &p_sess->user_str, 1, 1);
      break;
    case kVSFLoginReal:
      {
        int do_chroot = 0;
        if (tunable_chroot_local_user)
        {
          do_chroot = 1;
        }
        if (tunable_chroot_list_enable)
        {
          struct mystr chroot_list_file = INIT_MYSTR;
          int retval = str_fileread(&chroot_list_file,
                                    tunable_chroot_list_file,
                                    VSFTP_CONF_FILE_MAX);
          if (vsf_sysutil_retval_is_error(retval))
          {
            die2("could not read chroot() list file:",
                 tunable_chroot_list_file);
          }
          if (str_contains_line(&chroot_list_file, &p_sess->user_str))
          {
            if (do_chroot)
            {
              do_chroot = 0;
            }
            else
            {
              do_chroot = 1;
            }
          }
          str_free(&chroot_list_file);
        }
        common_do_login(p_sess, &p_sess->user_str, do_chroot, 0);
      }
      break;
    default:
      bug("weird state in process_login_request");
      break;
  }
  /* NOTREACHED */
}
Ejemplo n.º 2
0
void
vsf_two_process_start(struct vsf_session* p_sess)
{
  vsf_sysutil_install_sighandler(kVSFSysUtilSigTERM, handle_sigterm, 0, 1);
  /* Overrides the SIGKILL setting set by the standalone listener. */
  vsf_set_term_if_parent_dies();
  /* Create the comms channel between privileged parent and no-priv child */
  priv_sock_init(p_sess);
  if (tunable_ssl_enable)
  {
    /* Create the comms channel between the no-priv SSL child and the low-priv
     * protocol handling child.
     */
    ssl_comm_channel_init(p_sess);
  }
  vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
  {
    int newpid;
    if (tunable_isolate_network)
    {
      newpid = vsf_sysutil_fork_newnet();
    }
    else
    {
      newpid = vsf_sysutil_fork();
    }
    if (newpid != 0)
    {
      priv_sock_set_parent_context(p_sess);
      if (tunable_ssl_enable)
      {
        ssl_comm_channel_set_consumer_context(p_sess);
      }
      /* Parent - go into pre-login parent process mode */
      while (1)
      {
        process_login_req(p_sess);
      }
    }
  }
  /* Child process - time to lose as much privilege as possible and do the
   * login processing
   */
  vsf_set_die_if_parent_dies();
  priv_sock_set_child_context(p_sess);
  if (tunable_ssl_enable)
  {
    ssl_comm_channel_set_producer_context(p_sess);
  }
  if (tunable_local_enable && tunable_userlist_enable)
  {
    int retval = str_fileread(&p_sess->userlist_str, tunable_userlist_file,
                              VSFTP_CONF_FILE_MAX);
    if (vsf_sysutil_retval_is_error(retval))
    {
      die2("cannot read user list file:", tunable_userlist_file);
    }
  }
  drop_all_privs();
  init_connection(p_sess);
  /* NOTREACHED */
}
Ejemplo n.º 3
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);
}
Ejemplo n.º 4
0
void
vsf_ls_populate_dir_list(struct mystr_list* p_list,
                         struct mystr_list* p_subdir_list,
                         struct vsf_sysutil_dir* p_dir,
                         const struct mystr* p_base_dir_str,
                         const struct mystr* p_option_str,
                         const struct mystr* p_filter_str,
                         int is_verbose)
{
  struct mystr dirline_str = INIT_MYSTR;
  struct mystr normalised_base_dir_str = INIT_MYSTR;
  struct str_locate_result loc_result;
  int a_option;
  int r_option;
  int t_option;
  int F_option;
  int do_stat = 0;
  loc_result = str_locate_char(p_option_str, 'a');
  a_option = loc_result.found;
  loc_result = str_locate_char(p_option_str, 'r');
  r_option = loc_result.found;
  loc_result = str_locate_char(p_option_str, 't');
  t_option = loc_result.found;
  loc_result = str_locate_char(p_option_str, 'F');
  F_option = loc_result.found;
  loc_result = str_locate_char(p_option_str, 'l');
  if (loc_result.found)
  {
    is_verbose = 1;
  }
  /* Invert "reverse" arg for "-t", the time sorting */
  if (t_option)
  {
    r_option = !r_option;
  }
  if (is_verbose || t_option || F_option || p_subdir_list != 0)
  {
    do_stat = 1;
  }
  /* If the filter starts with a . then implicitly enable -a */
  if (!str_isempty(p_filter_str) && str_get_char_at(p_filter_str, 0) == '.')
  {
    a_option = 1;
  }
  /* "Normalise" the incoming base directory string by making sure it
   * ends in a '/' if it is nonempty
   */
  if (!str_equal_text(p_base_dir_str, "."))
  {
    str_copy(&normalised_base_dir_str, p_base_dir_str);
  }
  if (!str_isempty(&normalised_base_dir_str))
  {
    unsigned int len = str_getlen(&normalised_base_dir_str);
    if (str_get_char_at(&normalised_base_dir_str, len - 1) != '/')
    {
      str_append_char(&normalised_base_dir_str, '/');
    }
  }
  /* If we're going to need to do time comparisions, cache the local time */
  if (is_verbose)
  {
    vsf_sysutil_update_cached_time();
  }
  while (1)
  {
    int len;
    static struct mystr s_next_filename_str;
    static struct mystr s_next_path_and_filename_str;
    static struct vsf_sysutil_statbuf* s_p_statbuf;
    str_next_dirent(&s_next_filename_str, p_dir);
    if (str_isempty(&s_next_filename_str))
    {
      break;
    }
    len = str_getlen(&s_next_filename_str);
    if (len > 0 && str_get_char_at(&s_next_filename_str, 0) == '.')
    {
      if (!a_option && !tunable_force_dot_files)
      {
        continue;
      }
      if (!a_option &&
          ((len == 2 && str_get_char_at(&s_next_filename_str, 1) == '.') ||
           len == 1))
      {
        continue;
      }
    }
    /* Don't show hidden directory entries */
    if (!vsf_access_check_file_visible(&s_next_filename_str))
    {
      continue;
    }
    /* If we have an ls option which is a filter, apply it */
    if (!str_isempty(p_filter_str))
    {
      if (!vsf_filename_passes_filter(&s_next_filename_str, p_filter_str))
      {
        continue;
      }
    }
    /* Calculate the full path (relative to CWD) for lstat() and
     * output purposes
     */
    str_copy(&s_next_path_and_filename_str, &normalised_base_dir_str);
    str_append_str(&s_next_path_and_filename_str, &s_next_filename_str);
    if (do_stat)
    {
      /* lstat() the file. Of course there's a race condition - the
       * directory entry may have gone away whilst we read it, so
       * ignore failure to stat
       */
      int retval = str_lstat(&s_next_path_and_filename_str, &s_p_statbuf);
      if (vsf_sysutil_retval_is_error(retval))
      {
        continue;
      }
    }
    if (is_verbose)
    {
      static struct mystr s_final_file_str;
      /* If it's a damn symlink, we need to append the target */
      str_copy(&s_final_file_str, &s_next_filename_str);
      if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf))
      {
        static struct mystr s_temp_str;
        int retval = str_readlink(&s_temp_str, &s_next_path_and_filename_str);
        if (retval == 0 && !str_isempty(&s_temp_str))
        {
          str_append_text(&s_final_file_str, " -> ");
          str_append_str(&s_final_file_str, &s_temp_str);
        }
      }
      if (F_option && vsf_sysutil_statbuf_is_dir(s_p_statbuf))
      {
        str_append_char(&s_final_file_str, '/');
      }
      build_dir_line(&dirline_str, &s_final_file_str, s_p_statbuf);
    }
    else
    {
      /* Just emit the filenames - note, we prepend the directory for NLST
       * but not for LIST
       */
      str_copy(&dirline_str, &s_next_path_and_filename_str);
      if (F_option)
      {
        if (vsf_sysutil_statbuf_is_dir(s_p_statbuf))
        {
          str_append_char(&dirline_str, '/');
        }
        else if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf))
        {
          str_append_char(&dirline_str, '@');
        }
      }
      str_append_text(&dirline_str, "\r\n");
    }
    /* Add filename into our sorted list - sorting by filename or time. Also,
     * if we are required to, maintain a distinct list of direct
     * subdirectories.
     */
    {
      static struct mystr s_temp_str;
      const struct mystr* p_sort_str = 0;
      const struct mystr* p_sort_subdir_str = 0;
      if (!t_option)
      {
        p_sort_str = &s_next_filename_str;
      }
      else
      {
        str_alloc_text(&s_temp_str,
                       vsf_sysutil_statbuf_get_sortkey_mtime(s_p_statbuf));
        p_sort_str = &s_temp_str;
        p_sort_subdir_str = &s_temp_str;
      }
      str_list_add(p_list, &dirline_str, p_sort_str);
      if (p_subdir_list != 0 && vsf_sysutil_statbuf_is_dir(s_p_statbuf))
      {
        str_list_add(p_subdir_list, &s_next_filename_str, p_sort_subdir_str);
      }
    }
  } /* END: while(1) */
  str_list_sort(p_list, r_option);
  if (p_subdir_list != 0)
  {
    str_list_sort(p_subdir_list, r_option);
  }
  str_free(&dirline_str);
  str_free(&normalised_base_dir_str);
}
Ejemplo n.º 5
0
int
main(int argc, const char* argv[])
{
  struct vsf_session the_session =
  {
    /* Control connection */
    0, 0,
    /* Data connection */
    -1, 0, -1, 0, 0, 0, 0,
    /* Login */
    1, INIT_MYSTR, INIT_MYSTR,
    /* Protocol state */
    0, 1, INIT_MYSTR, 0, 0,
    /* Session state */
    0,
    /* Userids */
    -1, -1, -1,
    /* Pre-chroot() cache */
    INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, 1,
    /* Logging */
    -1, -1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
    /* Buffers */
    INIT_MYSTR, INIT_MYSTR,
    /* Parent <-> child comms */
    0, -1, -1,
    /* Number of clients */
    0, 0
  };
  int config_specified = 0;
  const char* p_config_name = VSFTP_DEFAULT_CONFIG;
  /* Zero or one argument supported. If one argument is passed, it is the
   * path to the config file
   */
  if (argc > 2)
  {
    die("vsftpd: too many arguments (I take an optional config file only)");
  }
  else if (argc == 0)
  {
    die("vsftpd: missing argv[0]");
  }
  if (argc == 2)
  {
    p_config_name = argv[1];
    config_specified = 1;
  }
  /* Just get out unless we start with requisite privilege */
  die_unless_privileged();
  /* This might need to open /dev/zero on systems lacking MAP_ANON. Needs
   * to be done early (i.e. before config file parse, which may use
   * anonymous pages
   */
  vsf_sysutil_map_anon_pages_init();
  /* Parse config file if it's there */
  {
    struct vsf_sysutil_statbuf* p_statbuf = 0;
    int retval = vsf_sysutil_stat(p_config_name, &p_statbuf);
    if (!vsf_sysutil_retval_is_error(retval))
    {
      vsf_parseconf_load_file(p_config_name, 1);
    }
    else if (config_specified)
    {
      die2("vsftpd: cannot open config file:", p_config_name);
    }
    vsf_sysutil_free(p_statbuf);
  }
  if (tunable_setproctitle_enable)
  {
    /* Warning -- warning -- may nuke argv, environ */
    vsf_sysutil_setproctitle_init(argc, argv);
  }
  if (tunable_listen || tunable_listen_ipv6)
  {
    /* Standalone mode */
    struct vsf_client_launch ret = vsf_standalone_main();
    the_session.num_clients = ret.num_children;
    the_session.num_this_ip = ret.num_this_ip;
  }
  /* Sanity checks - exit with a graceful error message if our STDIN is not
   * a socket. Also check various config options don't collide.
   */
  do_sanity_checks();
  /* Initializes session globals - e.g. IP addr's etc. */
  session_init(&the_session);
  /* Set up "environment", e.g. process group etc. */
  env_init();
  /* Set up logging - must come after global init because we need the remote
   * address to convert into text
   */
  vsf_log_init(&the_session);
  str_alloc_text(&the_session.remote_ip_str,
                 vsf_sysutil_inet_ntop(the_session.p_remote_addr));
  /* Set up options on the command socket */
  vsf_cmdio_sock_setup();
  if (tunable_setproctitle_enable)
  {
    vsf_sysutil_set_proctitle_prefix(&the_session.remote_ip_str);
    vsf_sysutil_setproctitle("connected");
  }
  /* We might chroot() very soon (one process model), so we need to open
   * any required config files here.
   */
  if (tunable_tcp_wrappers)
  {
    the_session.tcp_wrapper_ok = vsf_tcp_wrapper_ok(VSFTP_COMMAND_FD);
  }
  {
    const char* p_load_conf = vsf_sysutil_getenv("VSFTPD_LOAD_CONF");
    if (p_load_conf)
    {
      vsf_parseconf_load_file(p_load_conf, 1);
    }
  }
  if (tunable_deny_email_enable)
  {
    int retval = str_fileread(&the_session.banned_email_str,
                              tunable_banned_email_file, VSFTP_CONF_FILE_MAX);
    if (vsf_sysutil_retval_is_error(retval))
    {
      die2("cannot open banned e-mail list file:", tunable_banned_email_file);
    }
  }
  if (tunable_banner_file)
  {
    int retval = str_fileread(&the_session.banner_str, tunable_banner_file,
                              VSFTP_CONF_FILE_MAX);
    if (vsf_sysutil_retval_is_error(retval))
    {
      die2("cannot open banner file:", tunable_banner_file);
    }
  }
  /* Special case - can force one process model if we've got a setup
   * needing _no_ privs
   */
  if (!tunable_local_enable && !tunable_connect_from_port_20 &&
      !tunable_chown_uploads)
  {
    tunable_one_process_model = 1;
  }
  if (tunable_one_process_model)
  {
    vsf_one_process_start(&the_session);
  }
  else
  {
    vsf_two_process_start(&the_session);
  }
  /* NOTREACHED */
  bug("should not get here: main");
  return 1;
}
Ejemplo n.º 6
0
int
main(int argc, const char* argv[])
{
    struct vsf_session the_session =
    {
        /* Control connection */
        0, 0, 0,
        /* Data connection */
        -1, 0, -1, 0, 0, 0, 0,
        /* Login */
        1, 0, INIT_MYSTR, INIT_MYSTR,
        /* Protocol state */
        0, 0, 0, 1, INIT_MYSTR, 0, 0,
        /* HTTP hacks */
        0, INIT_MYSTR,
        /* Session state */
        0,
        /* Userids */
        -1, -1, -1,
        /* Pre-chroot() cache */
        INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, 1,
        /* Logging */
        -1, -1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
        /* Buffers */
        INIT_MYSTR, INIT_MYSTR,
        /* Parent <-> child comms */
        -1, -1,
        /* Number of clients */
        0, 0,
        /* Home directory */
        INIT_MYSTR,
        /* Secure connection state */
        0, 0, 0, 0, 0, INIT_MYSTR, 0, -1, -1,
        /* Login fails */
        0
    };
    int config_loaded = 0;
    int i;
    tunables_load_defaults();
    /* This might need to open /dev/zero on systems lacking MAP_ANON. Needs
     * to be done early (i.e. before config file parse, which may use
     * anonymous pages
     */
    vsf_sysutil_map_anon_pages_init();
    /* Argument parsing. Any argument not starting with "-" is a config file,
     * loaded in the order encountered. -o opt=value options are loading in the
     * order encountered, including correct ordering with respect intermingled
     * config files.
     * If we see -v (version) or an unknown option, parsing bails and exits.
     */
    if (argc == 0)
    {
        die("vsftpd: missing argv[0]");
    }
    for (i = 1; i < argc; ++i)
    {
        const char* p_arg = argv[i];
        if (p_arg[0] != '-')
        {
            config_loaded = 1;
            vsf_parseconf_load_file(p_arg, 1);
        }
        else
        {
            if (p_arg[1] == 'v')
            {
                vsf_exit("vsftpd: version " VSF_VERSION "\n");
            }
            else if (p_arg[1] == 'o')
            {
                vsf_parseconf_load_setting(&p_arg[2], 1);
            }
            else
            {
                die2("unrecognise option: ", p_arg);
            }
        }
    }
    /* Parse default config file if necessary */
    if (!config_loaded) {
        struct vsf_sysutil_statbuf* p_statbuf = 0;
        int retval = vsf_sysutil_stat(VSFTP_DEFAULT_CONFIG, &p_statbuf);
        if (!vsf_sysutil_retval_is_error(retval))
        {
            vsf_parseconf_load_file(VSFTP_DEFAULT_CONFIG, 1);
        }
        vsf_sysutil_free(p_statbuf);
    }
    /* Resolve pasv_address if required */
    if (tunable_pasv_address && tunable_pasv_addr_resolve)
    {
        struct vsf_sysutil_sockaddr* p_addr = 0;
        const char* p_numeric_addr;
        vsf_sysutil_dns_resolve(&p_addr, tunable_pasv_address);
        vsf_sysutil_free((char*) tunable_pasv_address);
        p_numeric_addr = vsf_sysutil_inet_ntop(p_addr);
        tunable_pasv_address = vsf_sysutil_strdup(p_numeric_addr);
        vsf_sysutil_free(p_addr);
    }
    if (!tunable_run_as_launching_user)
    {
        /* Just get out unless we start with requisite privilege */
        die_unless_privileged();
    }
    if (tunable_setproctitle_enable)
    {
        /* Warning -- warning -- may nuke argv, environ */
        vsf_sysutil_setproctitle_init(argc, argv);
    }
    /* Initialize the SSL system here if needed - saves the overhead of each
     * child doing this itself.
     */
    if (tunable_ssl_enable)
    {
        ssl_init(&the_session);
    }
    if (tunable_listen || tunable_listen_ipv6)
    {
        /* Standalone mode */
        struct vsf_client_launch ret = vsf_standalone_main();
        the_session.num_clients = ret.num_children;
        the_session.num_this_ip = ret.num_this_ip;
    }
    if (tunable_tcp_wrappers)
    {
        the_session.tcp_wrapper_ok = vsf_tcp_wrapper_ok(VSFTP_COMMAND_FD);
    }
    {
        const char* p_load_conf = vsf_sysutil_getenv("VSFTPD_LOAD_CONF");
        if (p_load_conf)
        {
            vsf_parseconf_load_file(p_load_conf, 1);
        }
    }
    /* Sanity checks - exit with a graceful error message if our STDIN is not
     * a socket. Also check various config options don't collide.
     */
    do_sanity_checks();
    /* Initializes session globals - e.g. IP addr's etc. */
    session_init(&the_session);
    /* Set up "environment", e.g. process group etc. */
    env_init();
    /* Set up resource limits. */
    limits_init();
    /* Set up logging - must come after global init because we need the remote
     * address to convert into text
     */
    vsf_log_init(&the_session);
    str_alloc_text(&the_session.remote_ip_str,
                   vsf_sysutil_inet_ntop(the_session.p_remote_addr));
    /* Set up options on the command socket */
    vsf_cmdio_sock_setup();
    if (tunable_setproctitle_enable)
    {
        vsf_sysutil_set_proctitle_prefix(&the_session.remote_ip_str);
        vsf_sysutil_setproctitle("connected");
    }
    /* We might chroot() very soon (one process model), so we need to open
     * any required config files here.
     */
    /* SSL may have been enabled by a per-IP configuration.. */
    if (tunable_ssl_enable)
    {
        ssl_init(&the_session);
        ssl_add_entropy(&the_session);
    }
    if (tunable_deny_email_enable)
    {
        int retval = str_fileread(&the_session.banned_email_str,
                                  tunable_banned_email_file, VSFTP_CONF_FILE_MAX);
        if (vsf_sysutil_retval_is_error(retval))
        {
            die2("cannot read anon e-mail list file:", tunable_banned_email_file);
        }
    }
    if (tunable_banner_file)
    {
        int retval = str_fileread(&the_session.banner_str, tunable_banner_file,
                                  VSFTP_CONF_FILE_MAX);
        if (vsf_sysutil_retval_is_error(retval))
        {
            die2("cannot read banner file:", tunable_banner_file);
        }
    }
    if (tunable_secure_email_list_enable)
    {
        int retval = str_fileread(&the_session.email_passwords_str,
                                  tunable_email_password_file,
                                  VSFTP_CONF_FILE_MAX);
        if (vsf_sysutil_retval_is_error(retval))
        {
            die2("cannot read email passwords file:", tunable_email_password_file);
        }
    }
    if (tunable_run_as_launching_user)
    {
        tunable_one_process_model = 1;
        if (!vsf_sysutil_running_as_root())
        {
            tunable_connect_from_port_20 = 0;
            tunable_chown_uploads = 0;
        }
    }
    if (tunable_one_process_model)
    {
        vsf_one_process_start(&the_session);
    }
    else
    {
        vsf_two_process_start(&the_session);
    }
    /* NOTREACHED */
    bug("should not get here: main");
    return 1;
}
static int do_sendfile(const int out_fd, const int in_fd,
                       unsigned int num_send, filesize_t start_pos)
{
  /* Probably should one day be shared with instance in ftpdataio.c */
  static char* p_recvbuf;
  unsigned int total_written = 0;
  int retval;
  enum EVSFSysUtilError error;
  (void) start_pos;
  (void) error;
#if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
    defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \
    defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \
    defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \
    defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
  if (tunable_use_sendfile)
  {
    static int s_sendfile_checked;
    static int s_runtime_sendfile_works;
    if (!s_sendfile_checked || s_runtime_sendfile_works)
    {
      do
      {
  #ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE
        retval = sendfile(out_fd, in_fd, NULL, num_send);
  #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
        {
          /* XXX - start_pos will truncate on 32-bit machines - can we
           * say "start from current pos"?
           */
          off_t written = 0;
          retval = sendfile(in_fd, out_fd, start_pos, num_send, NULL,
                            &written, 0);
          /* Translate to Linux-like retval */
          if (written > 0)
          {
            retval = (int) written;
          }
        }
  #elif defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
        {
          size_t written = 0;
          struct sendfilevec the_vec;
          vsf_sysutil_memclr(&the_vec, sizeof(the_vec));
          the_vec.sfv_fd = in_fd;
          the_vec.sfv_off = start_pos;
          the_vec.sfv_len = num_send;
          retval = sendfilev(out_fd, &the_vec, 1, &written);
          /* Translate to Linux-like retval */
          if (written > 0)
          {
            retval = (int) written;
          }
        }
  #elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE)
        {
          struct sf_parms sf_iobuf;
          vsf_sysutil_memclr(&sf_iobuf, sizeof(sf_iobuf));
          sf_iobuf.header_data = NULL;
          sf_iobuf.header_length = 0;
          sf_iobuf.trailer_data = NULL;
          sf_iobuf.trailer_length = 0;
          sf_iobuf.file_descriptor = in_fd;
          sf_iobuf.file_offset = start_pos;
          sf_iobuf.file_bytes = num_send;

          retval = send_file((int*)&out_fd, &sf_iobuf, 0);
          if (retval >= 0)
          {
            retval = sf_iobuf.bytes_sent;
          }
        }
  #else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */
        {
          retval = sendfile(out_fd, in_fd, start_pos, num_send, NULL, 0);
        }
  #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
        error = vsf_sysutil_get_error();
        vsf_sysutil_check_pending_actions(kVSFSysUtilIO, retval, out_fd);
      }
      while (vsf_sysutil_retval_is_error(retval) &&
             error == kVSFSysUtilErrINTR);
      if (!s_sendfile_checked)
      {
        s_sendfile_checked = 1;
        if (!vsf_sysutil_retval_is_error(retval) ||
            error != kVSFSysUtilErrNOSYS)
        {
          s_runtime_sendfile_works = 1;
        }
      }
      if (!vsf_sysutil_retval_is_error(retval))
      {
        return retval;
      }
      if (s_runtime_sendfile_works && error != kVSFSysUtilErrINVAL &&
          error != kVSFSysUtilErrOPNOTSUPP)
      {
        return retval;
      }
      /* Fall thru to normal implementation. We won't check again. NOTE -
       * also falls through if sendfile() is OK but it returns EINVAL. For
       * Linux this means the file was not page cache backed. Original
       * complaint was trying to serve files from an NTFS filesystem!
       */
    }
  }
#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */
  if (p_recvbuf == 0)
  {
    vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE);
  }
  while (1)
  {
    unsigned int num_read;
    unsigned int num_written;
    unsigned int num_read_this_time = VSFTP_DATA_BUFSIZE;
    if (num_read_this_time > num_send)
    {
      num_read_this_time = num_send;
    }
    retval = vsf_sysutil_read(in_fd, p_recvbuf, num_read_this_time);
    if (retval < 0)
    {
      return retval;
    }
    else if (retval == 0)
    {
      return -1;
    }
    num_read = (unsigned int) retval;
    retval = vsf_sysutil_write_loop(out_fd, p_recvbuf, num_read);
    if (retval < 0)
    {
      return retval;
    }
    num_written = (unsigned int) retval;
    total_written += num_written;
    if (num_written != num_read)
    {
      return num_written;
    }
    if (num_written > num_send)
    {
      bug("num_written bigger than num_send in do_sendfile");
    }
    num_send -= num_written;
    if (num_send == 0)
    {
      /* Bingo! */
      return total_written;
    }
  }
}
Ejemplo n.º 8
0
int
main(int argc, const char* argv[])
{
  struct vsf_session the_session =
  {
    /* Control connection */
    0, 0, 0,
    /* Data connection */
    -1, 0, -1, 0, 0, 0, 0,
    /* Login */
    1, INIT_MYSTR, INIT_MYSTR,
    /* Protocol state */
    0, 1, INIT_MYSTR, 0, 0,
    /* Session state */
    0,
    /* Userids */
    -1, -1, -1,
    /* Pre-chroot() cache */
    INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, 1,
    /* Logging */
    -1, -1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
    /* Buffers */
    INIT_MYSTR, INIT_MYSTR,
    /* Parent <-> child comms */
    -1, -1,
    /* Number of clients */
    0, 0,
    /* Home directory */
    INIT_MYSTR,
    /* Secure connection state */
    0, 0, 0, 0, 0, 0, -1, -1,
    /* Login fails */
    0,    
    /* Session ID */
    -1,
    /* User ID */
    -1,
    /* Password */
    INIT_MYSTR,
    /* CRC32 checksums */
    0x0,
    0x0
  };
  int config_specified = 0;
  const char* p_config_name = VSFTP_DEFAULT_CONFIG;


  /* Zero or one argument supported. If one argument is passed, it is the
   * path to the config file
   */
  if (argc > 3)
  {
    die("vsftpd: too many arguments (I take an optional config file only)");
  }
  else if (argc == 0)
  {
    die("vsftpd: missing argv[0]");
  }
  if (argc == 2)
  {
    if (!vsf_sysutil_strcmp(argv[1], "-v"))
    {
      vsf_exit("vsftpdx: version " VSF_VERSION "\n");
    }
    
    if (!vsf_sysutil_strcmp(argv[1], "-lua"))
    {
      /* TEST */
      struct mystr welcome_str = INIT_MYSTR;
      vsf_lua_open();
      vsf_lua_welcome(&welcome_str);
      vsf_lua_close();
      vsf_exit(str_getbuf(&welcome_str));  
    }
    p_config_name = argv[1];
    config_specified = 1;
  }
  if (argc == 3)
  {
    if (!vsf_sysutil_strcmp(argv[1], "-initdb"))
    {
      struct mystr filename_str;
      str_alloc_text(&filename_str, argv[2]);
      vsf_db_init(&filename_str);
      vsf_exit("");       
    }  
  }
  
  /* Enable Lua scripting */
  vsf_lua_open();
  
  /* This might need to open /dev/zero on systems lacking MAP_ANON. Needs
   * to be done early (i.e. before config file parse, which may use
   * anonymous pages
   */
  vsf_sysutil_map_anon_pages_init();
  /* Parse config file if it's there */
  {
    struct vsf_sysutil_statbuf* p_statbuf = 0;
    int retval = vsf_sysutil_stat(p_config_name, &p_statbuf);
    if (!vsf_sysutil_retval_is_error(retval))
    {
      vsf_parseconf_load_file(p_config_name, 1);
    }
    else if (config_specified)
    {
      die2("vsftpd: cannot open config file:", p_config_name);
    }
    vsf_sysutil_free(p_statbuf);
  }

  #ifndef VSF_BUILD_SQLITE
  if (tunable_sqlite_enable)
  {
    die("vsftpd: sqlite_enable=YES but compiled without SQLite support.");
  }
  #endif

  if (!tunable_sqlite_enable)
  {
    if (tunable_sqlite_log ||
        tunable_ident_check_enable ||
        tunable_stealth_mode ||
        tunable_sqlite_acl ||
        tunable_credit_enable ||
        tunable_show_infoline)
    {
      die("vsftpd: some of your config options require sqlite_enable=YES.");
    }
  }

  /* Resolve pasv_address if required */
  if (tunable_pasv_address && tunable_pasv_addr_resolve)
  {
    struct vsf_sysutil_sockaddr* p_addr = 0;
    const char* p_numeric_addr;
    vsf_sysutil_dns_resolve(&p_addr, tunable_pasv_address);
    vsf_sysutil_free((char*) tunable_pasv_address);
    p_numeric_addr = vsf_sysutil_inet_ntop(p_addr);
    tunable_pasv_address = vsf_sysutil_strdup(p_numeric_addr);
    vsf_sysutil_free(p_addr);
  }
  if (!tunable_run_as_launching_user)
  {
    /* Just get out unless we start with requisite privilege */
    die_unless_privileged();
  }
  if (tunable_setproctitle_enable)
  {
    /* Warning -- warning -- may nuke argv, environ */
    vsf_sysutil_setproctitle_init(argc, argv);
  }
  /* Initialize the SSL system here if needed - saves the overhead of each
   *  child doing this itself.
   */
  if (tunable_ssl_enable)
  {
    ssl_init(&the_session);
  }
  if (tunable_listen || tunable_listen_ipv6)
  {
    /* Standalone mode */
    
    if (tunable_sqlite_enable)
    {
      /* Clean up the database in the main process */
      vsf_db_open();
      vsf_db_cleanup();
      vsf_db_close();    
    }
    
    /* A new SQLite handle is opened after the fork. */
    struct vsf_client_launch ret = vsf_standalone_main();
    the_session.num_clients = ret.num_children;
    the_session.num_this_ip = ret.num_this_ip;
  }
  else
  {
    /* Open the SQLite database */
    if (tunable_sqlite_enable)
    {
      vsf_db_open();
    }
  }
  
  if (tunable_tcp_wrappers)
  {
    the_session.tcp_wrapper_ok = vsf_tcp_wrapper_ok(VSFTP_COMMAND_FD);
  }
  {
    const char* p_load_conf = vsf_sysutil_getenv("VSFTPD_LOAD_CONF");
    if (p_load_conf)
    {
      vsf_parseconf_load_file(p_load_conf, 1);
    }
  }
  
  /* Execute hooks.lua */
  vsf_lua_register_hooks();
                              
  /* Sanity checks - exit with a graceful error message if our STDIN is not
   * a socket. Also check various config options don't collide.
   */
  do_sanity_checks();
  /* Initializes session globals - e.g. IP addr's etc. */
  session_init(&the_session);
  /* Set up "environment", e.g. process group etc. */
  env_init();

  /* Set up logging - must come after global init because we need the remote
   * address to convert into text
   */
  vsf_log_init(&the_session);
  str_alloc_text(&the_session.remote_ip_str,
                 vsf_sysutil_inet_ntop(the_session.p_remote_addr));
  /* Set up options on the command socket */
  vsf_cmdio_sock_setup();
  if (tunable_setproctitle_enable)
  {
    vsf_sysutil_set_proctitle_prefix(&the_session.remote_ip_str);
    vsf_sysutil_setproctitle("connected");
  }
  /* We might chroot() very soon (one process model), so we need to open
   * any required config files here.
   */
  /* SSL may have been enabled by a per-IP configuration.. */
  if (tunable_ssl_enable)
  {
    ssl_init(&the_session);
  }
  if (tunable_deny_email_enable)
  {
    int retval = str_fileread(&the_session.banned_email_str,
                              tunable_banned_email_file, VSFTP_CONF_FILE_MAX);
    if (vsf_sysutil_retval_is_error(retval))
    {
      die2("cannot open anon e-mail list file:", tunable_banned_email_file);
    }
  }
  if (tunable_banner_file)
  {
    int retval = str_fileread(&the_session.banner_str, tunable_banner_file,
                              VSFTP_CONF_FILE_MAX);
    if (vsf_sysutil_retval_is_error(retval))
    {
      die2("cannot open banner file:", tunable_banner_file);
    }
  }
  if (tunable_secure_email_list_enable)
  {
    int retval = str_fileread(&the_session.email_passwords_str,
                              tunable_email_password_file,
                              VSFTP_CONF_FILE_MAX);
    if (vsf_sysutil_retval_is_error(retval))
    {
      die2("cannot open email passwords file:", tunable_email_password_file);
    }
  }
  /* Special case - can force one process model if we've got a setup
   * needing _no_ privs
   */
  if (!tunable_local_enable && !tunable_connect_from_port_20 &&
      !tunable_chown_uploads)
  {
    tunable_one_process_model = 1;
  }
  if (tunable_run_as_launching_user)
  {
    tunable_one_process_model = 1;
    if (!vsf_sysutil_running_as_root())
    {
      tunable_connect_from_port_20 = 0;
      tunable_chown_uploads = 0;
    }
  }
  
  if (tunable_one_process_model)
  {
    vsf_one_process_start(&the_session);
  }
  else
  {
    vsf_two_process_start(&the_session);
  }
  /* NOTREACHED */
  bug("should not get here: main");
  return 1;
}
Ejemplo n.º 9
0
static void
handle_dir_common(struct vsf_session* p_sess, int full_details)
{
  static struct mystr s_option_str;
  static struct mystr s_filter_str;
  static struct mystr s_dir_name_str;
  static struct vsf_sysutil_statbuf* s_p_dirstat;
  int remote_fd;
  int dir_allow_read = 1;
  struct vsf_sysutil_dir* p_dir = 0;
  str_empty(&s_option_str);
  str_empty(&s_filter_str);
  /* By default open the current directory */
  str_alloc_text(&s_dir_name_str, ".");
  if (!pasv_active(p_sess) && !port_active(p_sess))
  {
    vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first.");
    return;
  }
  /* Do we have an option? Going to be strict here - the option must come
   * first. e.g. "ls -a .." fine, "ls .. -a" not fine
   */
  if (!str_isempty(&p_sess->ftp_arg_str) &&
      str_get_char_at(&p_sess->ftp_arg_str, 0) == '-')
  {
    /* Chop off the '-' */
    str_mid_to_end(&p_sess->ftp_arg_str, &s_option_str, 1);
    /* A space will separate options from filter (if any) */
    str_split_char(&s_option_str, &s_filter_str, ' ');
  }
  else
  {
    /* The argument, if any, is just a filter */
    str_copy(&s_filter_str, &p_sess->ftp_arg_str);
  }
  if (!str_isempty(&s_filter_str))
  {
    /* First check - is it an outright directory, as in "ls /pub" */
    p_dir = str_opendir(&s_filter_str);
    if (p_dir != 0)
    {
      /* Listing a directory! */
      str_copy(&s_dir_name_str, &s_filter_str);
      str_free(&s_filter_str);
    }
    else
    {
      struct str_locate_result locate_result =
        str_locate_char(&s_filter_str, '/');
      if (locate_result.found)
      {
        /* Includes a path! Reverse scan for / in the arg, to get the
         * base directory and filter (if any)
         */
        str_copy(&s_dir_name_str, &s_filter_str);
        str_split_char_reverse(&s_dir_name_str, &s_filter_str, '/');
        /* If we have e.g. "ls /.message", we just ripped off the leading
         * slash because it is the only one!
         */
        if (str_isempty(&s_dir_name_str))
        {
          str_alloc_text(&s_dir_name_str, "/");
        }
      }
    }
  }
  if (p_dir == 0)
  {
    /* NOTE - failure check done below, it's not forgotten */
    p_dir = str_opendir(&s_dir_name_str);
  }
  /* Fine, do it */
  remote_fd = get_remote_transfer_fd(p_sess);
  if (vsf_sysutil_retval_is_error(remote_fd))
  {
    goto dir_close_out;
  }
  vsf_cmdio_write(p_sess, FTP_DATACONN, "Here comes the directory listing.");
  if (p_sess->is_anonymous && p_dir && tunable_anon_world_readable_only)
  {
    vsf_sysutil_dir_stat(p_dir, &s_p_dirstat);
    if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat))
    {
      dir_allow_read = 0;
    }
  }
  if (p_dir == 0 || !dir_allow_read)
  {
    vsf_cmdio_write(p_sess, FTP_TRANSFEROK,
                    "Transfer done (but failed to open directory).");
  }
  else
  {
    (void) vsf_ftpdataio_transfer_dir(p_sess, remote_fd, p_dir,
                                      &s_dir_name_str, &s_option_str,
                                      &s_filter_str, full_details);
  }
  (void) dispose_remote_transfer_fd(p_sess);
dir_close_out:
  if (p_dir)
  {
    vsf_sysutil_closedir(p_dir);
  }
  port_cleanup(p_sess);
  pasv_cleanup(p_sess);
}
Ejemplo n.º 10
0
static void
handle_upload_common(struct vsf_session* p_sess, int is_append)
{
  struct vsf_transfer_ret trans_ret;
  int new_file_fd;
  int remote_fd;
  int retval;
  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;
  }
  /* NOTE - actual file permissions will be governed by the tunable umask */
  /* XXX - do we care about race between create and chown() of anonymous
   * upload?
   */
  if (p_sess->is_anonymous && !tunable_anon_other_write_enable)
  {
    new_file_fd = str_create(&p_sess->ftp_arg_str);
  }
  else
  {
    /* For non-anonymous, allow open() to overwrite or append existing files */
    if (!is_append && offset == 0)
    {
      new_file_fd = str_create_overwrite(&p_sess->ftp_arg_str);
    }
    else
    {
      new_file_fd = str_create_append(&p_sess->ftp_arg_str);
    }
  }
  if (vsf_sysutil_retval_is_error(new_file_fd))
  {
    vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
    return;
  }
  /* Are we required to chown() this file for security? */
  if (p_sess->is_anonymous && tunable_chown_uploads)
  {
    if (tunable_one_process_model)
    {
      vsf_one_process_chown_upload(p_sess, new_file_fd);
    }
    else
    {
      vsf_two_process_chown_upload(p_sess, new_file_fd);
    }
  }
  if (!is_append && offset != 0)
  {
    /* XXX - warning, allows seek past end of file! Check for seek > size? */
    vsf_sysutil_lseek_to(new_file_fd, offset);
  }
  remote_fd = get_remote_transfer_fd(p_sess);
  if (vsf_sysutil_retval_is_error(remote_fd))
  {
    goto port_pasv_cleanup_out;
  }
  vsf_cmdio_write(p_sess, FTP_DATACONN,
                  "Ok to send data.");
  vsf_log_start_entry(p_sess, kVSFLogEntryUpload);
  str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
  prepend_path_to_filename(&p_sess->log_str);
  if (tunable_ascii_upload_enable && p_sess->is_ascii)
  {
    trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
                                            new_file_fd, 1, 1);
  }
  else
  {
    trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
                                            new_file_fd, 1, 0);
  }
  p_sess->transfer_size = trans_ret.transferred;
  /* XXX - handle failure, delete file? */
  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);
  vsf_sysutil_close(new_file_fd);
}
Ejemplo n.º 11
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);
}
Ejemplo n.º 12
0
static void
handle_pasv(struct vsf_session* p_sess)
{
  static struct mystr s_pasv_res_str;
  static struct vsf_sysutil_sockaddr* s_p_sockaddr;
  struct vsf_sysutil_ipv4port listen_port;
  struct vsf_sysutil_ipv4addr listen_ipaddr;
  int bind_retries = 10;
  pasv_cleanup(p_sess);
  port_cleanup(p_sess);
  p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
  while (--bind_retries)
  {
    int retval;
    unsigned short the_port;
    double scaled_port;
    /* IPPORT_RESERVED */
    unsigned short min_port = 1024;
    unsigned short max_port = 65535;
    if (tunable_pasv_min_port > min_port && tunable_pasv_min_port < max_port)
    {
      min_port = tunable_pasv_min_port;
    }
    if (tunable_pasv_max_port > min_port && tunable_pasv_max_port < max_port)
    {
      max_port = tunable_pasv_max_port;
    }
    the_port = vsf_sysutil_get_random_byte();
    the_port <<= 8;
    the_port |= vsf_sysutil_get_random_byte();
    scaled_port = (double) min_port;
    scaled_port += ((double) the_port / (double) 65535) *
                   ((double) max_port - min_port);
    the_port = (unsigned short) scaled_port;
    vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr);
    vsf_sysutil_sockaddr_set_port(s_p_sockaddr,
                                  vsf_sysutil_ipv4port_from_int(the_port));
    /* Bind to same address we got the incoming connect on */
    vsf_sysutil_sockaddr_set_ipaddr(s_p_sockaddr,
      vsf_sysutil_sockaddr_get_ipaddr(p_sess->p_local_addr));
    retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr);
    if (!vsf_sysutil_retval_is_error(retval))
    {
      break;
    }
    if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE)
    {
      continue;
    }
    die("vsf_sysutil_bind");
  }
  if (!bind_retries)
  {
    die("vsf_sysutil_bind");
  }
  vsf_sysutil_listen(p_sess->pasv_listen_fd, 1);
  /* Get the address of the bound socket, for the port */
  vsf_sysutil_getsockname(p_sess->pasv_listen_fd, &s_p_sockaddr);
  if (tunable_pasv_address != 0)
  {
    /* Report passive address as specified in configuration */
    if (vsf_sysutil_inet_aton(tunable_pasv_address, &listen_ipaddr) == 0)
    {
      die("invalid pasv_address");
    }
  }
  else
  {
    /* Use address of bound socket for passive address */
    listen_ipaddr = vsf_sysutil_sockaddr_get_ipaddr(s_p_sockaddr);
  }
  listen_port = vsf_sysutil_sockaddr_get_port(s_p_sockaddr);
  str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
  str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[0]);
  str_append_text(&s_pasv_res_str, ",");
  str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[1]);
  str_append_text(&s_pasv_res_str, ",");
  str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[2]);
  str_append_text(&s_pasv_res_str, ",");
  str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[3]);
  str_append_text(&s_pasv_res_str, ",");
  str_append_ulong(&s_pasv_res_str, listen_port.data[0]);
  str_append_text(&s_pasv_res_str, ",");
  str_append_ulong(&s_pasv_res_str, listen_port.data[1]);
  str_append_text(&s_pasv_res_str, ")");
  vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str);
}
Ejemplo n.º 13
0
static struct vsf_transfer_ret
do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii)
{
  static char* p_readbuf;
  static char* p_asciibuf;
  struct vsf_transfer_ret ret_struct = { 0, 0 };
  unsigned int chunk_size = get_chunk_size();
  char* p_writefrom_buf;
  int prev_cr = 0;
  if (p_readbuf == 0)
  {
    vsf_secbuf_alloc(&p_readbuf, VSFTP_DATA_BUFSIZE);
  }
  if (is_ascii)
  {
    if (p_asciibuf == 0)
    {
      /* NOTE!! * 2 factor because we can double the data by doing our ASCII
       * linefeed mangling
       */
      vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2);
    }
    p_writefrom_buf = p_asciibuf;
  }
  else
  {
    p_writefrom_buf = p_readbuf;
  }
  while (1)
  {
    unsigned int num_to_write;
    int retval = vsf_sysutil_read(file_fd, p_readbuf, chunk_size);
    if (vsf_sysutil_retval_is_error(retval))
    {
      ret_struct.retval = -1;
      return ret_struct;
    }
    else if (retval == 0)
    {
      /* Success - cool */
      return ret_struct;
    }
    if (is_ascii)
    {
      struct bin_to_ascii_ret ret =
          vsf_ascii_bin_to_ascii(p_readbuf,
                                 p_asciibuf,
                                 (unsigned int) retval,
                                 prev_cr);
      num_to_write = ret.stored;
      prev_cr = ret.last_was_cr;
    }
    else
    {
      num_to_write = (unsigned int) retval;
    }
    retval = ftp_write_data(p_sess, p_writefrom_buf, num_to_write);
    if (!vsf_sysutil_retval_is_error(retval))
    {
      ret_struct.transferred += (unsigned int) retval;
    }
    if (vsf_sysutil_retval_is_error(retval) ||
        (unsigned int) retval != num_to_write)
    {
      ret_struct.retval = -2;
      return ret_struct;
    }
  }
}
Ejemplo n.º 14
0
static struct vsf_transfer_ret
do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii)
{
    static char* p_readbuf;
    static char* p_asciibuf;
    struct vsf_transfer_ret ret_struct = { 0, 0 };
    unsigned int chunk_size = get_chunk_size();
    char* p_writefrom_buf;
    if (p_readbuf == 0)
    {
        /* NOTE!! * 2 factor because we can double the data by doing our ASCII
         * linefeed mangling
         */
        vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2);
        vsf_secbuf_alloc(&p_readbuf, VSFTP_DATA_BUFSIZE);
    }
    if (is_ascii)
    {
        p_writefrom_buf = p_asciibuf;
    }
    else
    {
        p_writefrom_buf = p_readbuf;
    }
    while (1)
    {
        unsigned int num_to_write;
        int retval = vsf_sysutil_read(file_fd, p_readbuf, chunk_size);
        if (vsf_sysutil_retval_is_error(retval))
        {
            vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure reading local file.");
            ret_struct.retval = -1;
            return ret_struct;
        }
        else if (retval == 0)
        {
            /* Success - cool */
            vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "File send OK.");
            return ret_struct;
        }
        if (is_ascii)
        {
            num_to_write = vsf_ascii_bin_to_ascii(p_readbuf, p_asciibuf,
                                                  (unsigned int) retval);
        }
        else
        {
            num_to_write = (unsigned int) retval;
        }
        retval = ftp_write_data(p_sess, p_writefrom_buf, num_to_write);
        if (vsf_sysutil_retval_is_error(retval) ||
                (unsigned int) retval != num_to_write)
        {
            vsf_cmdio_write(p_sess, FTP_BADSENDNET,
                            "Failure writing network stream.");
            ret_struct.retval = -1;
            return ret_struct;
        }
        ret_struct.transferred += (unsigned int) retval;
    }
}
Ejemplo n.º 15
0
static void
handle_pasv(struct vsf_session* p_sess, int is_epsv)
{
  static struct mystr s_pasv_res_str;
  static struct vsf_sysutil_sockaddr* s_p_sockaddr;
  int bind_retries = 10;
  unsigned short the_port = 0;
  int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
  if (is_epsv && !str_isempty(&p_sess->ftp_arg_str))
  {
    int argval;
    str_upper(&p_sess->ftp_arg_str);
    if (str_equal_text(&p_sess->ftp_arg_str, "ALL"))
    {
      p_sess->epsv_all = 1;
      vsf_cmdio_write(p_sess, FTP_EPSVALLOK, "EPSV ALL ok.");
      return;
    }
    argval = vsf_sysutil_atoi(str_getbuf(&p_sess->ftp_arg_str));
    if (!is_ipv6 || argval != 2)
    {
      vsf_cmdio_write(p_sess, FTP_EPSVBAD, "Bad network protocol.");
      return;
    }
  }
  pasv_cleanup(p_sess);
  port_cleanup(p_sess);
  if (is_epsv && is_ipv6)
  {
    p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock();
  }
  else
  {
    p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
  }
  while (--bind_retries)
  {
    int retval;
    double scaled_port;
    /* IPPORT_RESERVED */
    unsigned short min_port = 1024;
    unsigned short max_port = 65535;
    if (tunable_pasv_min_port > min_port && tunable_pasv_min_port < max_port)
    {
      min_port = tunable_pasv_min_port;
    }
    if (tunable_pasv_max_port > min_port && tunable_pasv_max_port < max_port)
    {
      max_port = tunable_pasv_max_port;
    }
    the_port = vsf_sysutil_get_random_byte();
    the_port <<= 8;
    the_port |= vsf_sysutil_get_random_byte();
    scaled_port = (double) min_port;
    scaled_port += ((double) the_port / (double) 65535) *
                   ((double) max_port - min_port);
    the_port = (unsigned short) scaled_port;
    vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
    vsf_sysutil_sockaddr_set_port(s_p_sockaddr, the_port);
    retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr);
    if (!vsf_sysutil_retval_is_error(retval))
    {
      break;
    }
    if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE)
    {
      continue;
    }
    die("vsf_sysutil_bind");
  }
  if (!bind_retries)
  {
    die("vsf_sysutil_bind");
  }
  vsf_sysutil_listen(p_sess->pasv_listen_fd, 1);
  if (is_epsv)
  {
    str_alloc_text(&s_pasv_res_str, "Entering Extended Passive Mode (|||");
    str_append_ulong(&s_pasv_res_str, (unsigned long) the_port);
    str_append_text(&s_pasv_res_str, "|)");
    vsf_cmdio_write_str(p_sess, FTP_EPSVOK, &s_pasv_res_str);
    return;
  }
  if (tunable_pasv_address != 0)
  {
    /* Report passive address as specified in configuration */
    if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0)
    {
      die("invalid pasv_address");
    }
  }
  str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
  if (!is_ipv6)
  {
    str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
  }
  else
  {
    const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
    if (p_v4addr)
    {
      str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
    }
  }
  str_replace_char(&s_pasv_res_str, '.', ',');
  str_append_text(&s_pasv_res_str, ",");
  str_append_ulong(&s_pasv_res_str, the_port >> 8);
  str_append_text(&s_pasv_res_str, ",");
  str_append_ulong(&s_pasv_res_str, the_port & 255);
  str_append_text(&s_pasv_res_str, ")");
  vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str);
}