Example #1
0
int
vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess)
{
  int remote_fd;
  if (tunable_one_process_model)
  {
    remote_fd = vsf_one_process_get_pasv_fd(p_sess);
  }
  else
  {
    remote_fd = vsf_two_process_get_pasv_fd(p_sess);
  }
  /* Yes, yes, hardcoded bad I know. */
  if (remote_fd == -1)
  {
    vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
                    "Failed to establish connection.");
    return remote_fd;
  }
  else if (remote_fd == -2)
  {
    vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting.");
    vsf_sysutil_close(remote_fd);
    return -1;
  }
  init_data_sock_params(p_sess, remote_fd);
  return remote_fd;
}
Example #2
0
int
vsf_privop_accept_pasv(struct vsf_session* p_sess)
{
  struct vsf_sysutil_sockaddr* p_accept_addr = 0;
  int remote_fd;
  vsf_sysutil_sockaddr_alloc(&p_accept_addr);
  remote_fd = vsf_sysutil_accept_timeout(p_sess->pasv_listen_fd, p_accept_addr,
                                         tunable_accept_timeout);
  if (vsf_sysutil_retval_is_error(remote_fd))
  {
    vsf_sysutil_sockaddr_clear(&p_accept_addr);
    return -1;
  }
  /* SECURITY:
   * Reject the connection if it wasn't from the same IP as the
   * control connection.
   */
  if (!tunable_pasv_promiscuous)
  {
    if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_accept_addr))
    {
      vsf_sysutil_close(remote_fd);
      vsf_sysutil_sockaddr_clear(&p_accept_addr);
      return -2;
    }
  }
  vsf_sysutil_sockaddr_clear(&p_accept_addr);
  return remote_fd;
}
Example #3
0
int
vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess)
{
  int remote_fd;
  struct vsf_sysutil_sockaddr* p_accept_addr = 0;
  vsf_sysutil_sockaddr_alloc(&p_accept_addr);
  remote_fd = vsf_sysutil_accept_timeout(p_sess->pasv_listen_fd, p_accept_addr,
                                         tunable_accept_timeout);
  if (vsf_sysutil_retval_is_error(remote_fd))
  {
    vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
                    "Failed to establish connection.");
    vsf_sysutil_sockaddr_clear(&p_accept_addr);
    return remote_fd;
  }
  /* SECURITY:
   * Reject the connection if it wasn't from the same IP as the
   * control connection.
   */
  if (!tunable_pasv_promiscuous)
  {
    if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_accept_addr))
    {
      vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting.");
      vsf_sysutil_close(remote_fd);
      vsf_sysutil_sockaddr_clear(&p_accept_addr);
      return -1;
    }
  }
  vsf_sysutil_sockaddr_clear(&p_accept_addr);
  init_data_sock_params(p_sess, remote_fd);
  vsf_sysutil_set_lfp(remote_fd);
  return remote_fd;
}
Example #4
0
int
vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess,
                             unsigned short remote_port,
                             int use_port_sockaddr)
{
  static struct vsf_sysutil_sockaddr* p_sockaddr;
  const struct vsf_sysutil_sockaddr* p_connect_to;
  int retval;
  int i;
  int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr);
  int port = 0;
  if (vsf_sysutil_is_port_reserved(remote_port))
  {
    die("Illegal port request");
  }
  if (tunable_connect_from_port_20)
  {
    port = tunable_ftp_data_port;
  }
  vsf_sysutil_activate_reuseaddr(s);
  /* A report of failure here on Solaris, presumably buggy address reuse
   * support? We'll retry.
   */
  for (i = 0; i < 2; ++i)
  {
    double sleep_for;
    vsf_sysutil_sockaddr_clone(&p_sockaddr, p_sess->p_local_addr);
    vsf_sysutil_sockaddr_set_port(p_sockaddr, port);
    retval = vsf_sysutil_bind(s, p_sockaddr);
    if (retval == 0)
    {
      break;
    }
    if (vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE || i == 1)
    {
      die("vsf_sysutil_bind");
    }
    sleep_for = vsf_sysutil_get_random_byte();
    sleep_for /= 256.0;
    sleep_for += 1.0;
    vsf_sysutil_sleep(sleep_for);
  }
  if (use_port_sockaddr)
  {
    p_connect_to = p_sess->p_port_sockaddr;
  }
  else
  {
    vsf_sysutil_sockaddr_set_port(p_sess->p_remote_addr, remote_port);
    p_connect_to = p_sess->p_remote_addr;
  }
  retval = vsf_sysutil_connect_timeout(s, p_connect_to,
                                       tunable_connect_timeout);
  if (vsf_sysutil_retval_is_error(retval))
  {
    vsf_sysutil_close(s);
    s = -1;
  }
  return s;
}
static void
cmd_process_chown(struct vsf_session* p_sess)
{
  int the_fd = priv_sock_recv_fd(p_sess->parent_fd);
  vsf_privop_do_file_chown(p_sess, the_fd);
  vsf_sysutil_close(the_fd);
  priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
}
static void
cmd_process_get_data_sock(struct vsf_session* p_sess)
{
  int sock_fd = vsf_privop_get_ftp_port_sock(p_sess);
  priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
  priv_sock_send_fd(p_sess->parent_fd, sock_fd);
  vsf_sysutil_close(sock_fd);
}
Example #7
0
void
vsf_two_process_start(struct vsf_session* p_sess)
{
  /* 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_async_sighandler(kVSFSysUtilSigCHLD, twoproc_handle_sigchld);
  {
    int newpid = vsf_sysutil_fork();
    if (newpid != 0)
    {
      /* 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_sysutil_close(p_sess->parent_fd);
  if (tunable_ssl_enable)
  {
    vsf_sysutil_close(p_sess->ssl_consumer_fd);
  }
  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 open user list file:", tunable_userlist_file);
    }
  }
  drop_all_privs();
  init_connection(p_sess);
  /* NOTREACHED */
}
Example #8
0
static void
pasv_cleanup(struct vsf_session* p_sess)
{
  if (p_sess->pasv_listen_fd != -1)
  {
    vsf_sysutil_close(p_sess->pasv_listen_fd);
    p_sess->pasv_listen_fd = -1;
  }
}
Example #9
0
void
ssl_comm_channel_set_producer_context(struct vsf_session* p_sess)
{
  if (p_sess->ssl_consumer_fd == -1)
  {
    bug("ssl_consumer_fd already closed");
  }
  vsf_sysutil_close(p_sess->ssl_consumer_fd);
  p_sess->ssl_consumer_fd = -1;
}
Example #10
0
static void
prepare_child(int new_client_sock)
{
    /* We must satisfy the contract: command socket on fd 0, 1, 2 */
    vsf_sysutil_dupfd2(new_client_sock, 0);
    vsf_sysutil_dupfd2(new_client_sock, 1);
    vsf_sysutil_dupfd2(new_client_sock, 2);
    if (new_client_sock > 2)
    {
        vsf_sysutil_close(new_client_sock);
    }
}
Example #11
0
int
vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess)
{
    int retval;
    int remote_fd;
    if (tunable_connect_from_port_20)
    {
        if (tunable_one_process_model)
        {
            remote_fd = vsf_one_process_get_priv_data_sock(p_sess);
        }
        else
        {
            remote_fd = vsf_two_process_get_priv_data_sock(p_sess);
        }
    }
    else
    {
        remote_fd = vsf_sysutil_get_ipsock(p_sess->p_port_sockaddr);
        if (vsf_sysutil_sockaddr_same_family(p_sess->p_port_sockaddr,
                                             p_sess->p_local_addr))
        {
            static struct vsf_sysutil_sockaddr* s_p_addr;
            vsf_sysutil_sockaddr_clone(&s_p_addr, p_sess->p_local_addr);
            retval = vsf_sysutil_bind(remote_fd, s_p_addr);
            if (retval != 0)
            {
                die("vsf_sysutil_bind");
            }
        }
    }
    retval = vsf_sysutil_connect_timeout(remote_fd, p_sess->p_port_sockaddr,
                                         tunable_connect_timeout);
    if (vsf_sysutil_retval_is_error(retval))
    {
        vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
                        "Failed to establish connection.");
        vsf_sysutil_close(remote_fd);
        return -1;
    }
    init_data_sock_params(p_sess, remote_fd);
    return remote_fd;
}
Example #12
0
void
vsf_two_process_login(struct vsf_session* p_sess,
                      const struct mystr* p_pass_str)
{
  char result;
  priv_sock_send_cmd(p_sess->child_fd, PRIV_SOCK_LOGIN);
  priv_sock_send_str(p_sess->child_fd, &p_sess->user_str);
  priv_sock_send_str(p_sess->child_fd, p_pass_str);
  priv_sock_send_int(p_sess->child_fd, p_sess->control_use_ssl);
  priv_sock_send_int(p_sess->child_fd, p_sess->data_use_ssl);
  result = priv_sock_get_result(p_sess->child_fd);
  if (result == PRIV_SOCK_RESULT_OK)
  {
    /* Miracle. We don't emit the success message here. That is left to
     * process_post_login().
     * Exit normally, unless we are remaining as the SSL read / write child.
     */
    if (!p_sess->control_use_ssl)
    {
      vsf_sysutil_exit(0);
    }
    else
    {
      vsf_sysutil_clear_alarm();
      vsf_sysutil_close(p_sess->child_fd);
      if (tunable_setproctitle_enable)
      {
        vsf_sysutil_setproctitle("SSL handler");
      }
      process_ssl_slave_req(p_sess);
    }
    /* NOTREACHED */
  }
  else if (result == PRIV_SOCK_RESULT_BAD)
  {
    /* Continue the processing loop.. */
    return;
  }
  else
  {
    die("priv_sock_get_result");
  }
}
Example #13
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 #14
0
void
ssl_slave(struct vsf_session* p_sess)
{
  struct mystr data_str = INIT_MYSTR;
  str_reserve(&data_str, VSFTP_DATA_BUFSIZE);
  /* Before becoming the slave, clear the alarm for the FTP protocol. */
  vsf_sysutil_clear_alarm();
  /* No need for any further communications with the privileged parent. */
  priv_sock_set_parent_context(p_sess);
  if (tunable_setproctitle_enable)
  {
    vsf_sysutil_setproctitle("SSL handler");
  }
  while (1)
  {
    char cmd = priv_sock_get_cmd(p_sess->ssl_slave_fd);
    int ret;
    if (cmd == PRIV_SOCK_GET_USER_CMD)
    {
      ret = ftp_getline(p_sess, &p_sess->ftp_cmd_str,
                        p_sess->p_control_line_buf);
      priv_sock_send_int(p_sess->ssl_slave_fd, ret);
      if (ret >= 0)
      {
        priv_sock_send_str(p_sess->ssl_slave_fd, &p_sess->ftp_cmd_str);
      }
    }
    else if (cmd == PRIV_SOCK_WRITE_USER_RESP)
    {
      priv_sock_get_str(p_sess->ssl_slave_fd, &p_sess->ftp_cmd_str);
      ret = ftp_write_str(p_sess, &p_sess->ftp_cmd_str, kVSFRWControl);
      priv_sock_send_int(p_sess->ssl_slave_fd, ret);
    }
    else if (cmd == PRIV_SOCK_DO_SSL_HANDSHAKE)
    {
      char result = PRIV_SOCK_RESULT_BAD;
      if (p_sess->data_fd != -1 || p_sess->p_data_ssl != 0)
      {
        bug("state not clean");
      }
      p_sess->data_fd = priv_sock_recv_fd(p_sess->ssl_slave_fd);
      ret = ssl_accept(p_sess, p_sess->data_fd);
      if (ret == 1)
      {
        result = PRIV_SOCK_RESULT_OK;
      }
      else
      {
        vsf_sysutil_close(p_sess->data_fd);
        p_sess->data_fd = -1;
      }
      priv_sock_send_result(p_sess->ssl_slave_fd, result);
    }
    else if (cmd == PRIV_SOCK_DO_SSL_READ)
    {
      str_trunc(&data_str, VSFTP_DATA_BUFSIZE);
      ret = ssl_read_into_str(p_sess, p_sess->p_data_ssl, &data_str);
      priv_sock_send_int(p_sess->ssl_slave_fd, ret);
      priv_sock_send_str(p_sess->ssl_slave_fd, &data_str);
    }
    else if (cmd == PRIV_SOCK_DO_SSL_WRITE)
    {
      priv_sock_get_str(p_sess->ssl_slave_fd, &data_str);
      ret = ssl_write(p_sess->p_data_ssl,
                      str_getbuf(&data_str),
                      str_getlen(&data_str));
      priv_sock_send_int(p_sess->ssl_slave_fd, ret);
    }
    else if (cmd == PRIV_SOCK_DO_SSL_CLOSE)
    {
      char result = PRIV_SOCK_RESULT_BAD;
      if (p_sess->data_fd == -1 && p_sess->p_data_ssl == 0)
      {
        result = PRIV_SOCK_RESULT_OK;
      }
      else
      {
        ret = ssl_data_close(p_sess);
        if (ret == 1)
        {
          result = PRIV_SOCK_RESULT_OK;
        }
        vsf_sysutil_close(p_sess->data_fd);
        p_sess->data_fd = -1;
      }
      priv_sock_send_result(p_sess->ssl_slave_fd, result);
    }
    else
    {
      die("bad request in process_ssl_slave_req");
    }
  }
}
Example #15
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 #16
0
struct vsf_client_launch
vsf_standalone_main(void)
{
    struct vsf_sysutil_sockaddr* p_accept_addr = 0;
    int listen_sock = -1;
    int retval;
    s_ipaddr_size = vsf_sysutil_get_ipaddr_size();
    if (tunable_listen && tunable_listen_ipv6)
    {
        die("run two copies of vsftpd for IPv4 and IPv6");
    }
    if (tunable_background)
    {
        int forkret = vsf_sysutil_fork();
        if (forkret > 0)
        {
            /* Parent, just exit */
            vsf_sysutil_exit(0);
        }
        vsf_sysutil_make_session_leader();
    }
    if (tunable_listen)
    {
        listen_sock = vsf_sysutil_get_ipv4_sock();
    }
    else
    {
        listen_sock = vsf_sysutil_get_ipv6_sock();
    }
    vsf_sysutil_activate_reuseaddr(listen_sock);

    s_p_ip_count_hash = hash_alloc(256, s_ipaddr_size,
                                   sizeof(unsigned int), hash_ip);
    s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
                                 s_ipaddr_size, hash_pid);
    if (tunable_setproctitle_enable)
    {
        vsf_sysutil_setproctitle("LISTENER");
    }
    vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
    vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup);
    if (tunable_listen)
    {
        struct vsf_sysutil_sockaddr* p_sockaddr = 0;
        vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
        vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
        if (!tunable_listen_address)
        {
            vsf_sysutil_sockaddr_set_any(p_sockaddr);
        }
        else
        {
            if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr))
            {
                die2("bad listen_address: ", tunable_listen_address);
            }
        }
        retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
        vsf_sysutil_free(p_sockaddr);
        if (vsf_sysutil_retval_is_error(retval))
        {
            die("could not bind listening IPv4 socket");
        }
    }
    else
    {
        struct vsf_sysutil_sockaddr* p_sockaddr = 0;
        vsf_sysutil_sockaddr_alloc_ipv6(&p_sockaddr);
        vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
        if (!tunable_listen_address6)
        {
            vsf_sysutil_sockaddr_set_any(p_sockaddr);
        }
        else
        {
            struct mystr addr_str = INIT_MYSTR;
            const unsigned char* p_raw_addr;
            str_alloc_text(&addr_str, tunable_listen_address6);
            p_raw_addr = vsf_sysutil_parse_ipv6(&addr_str);
            str_free(&addr_str);
            if (!p_raw_addr)
            {
                die2("bad listen_address6: ", tunable_listen_address6);
            }
            vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr);
        }
        retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
        vsf_sysutil_free(p_sockaddr);
        if (vsf_sysutil_retval_is_error(retval))
        {
            die("could not bind listening IPv6 socket");
        }
    }
    vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
    vsf_sysutil_sockaddr_alloc(&p_accept_addr);
    while (1)
    {
        struct vsf_client_launch child_info;
        void* p_raw_addr;
        int new_child;
        int new_client_sock;
        vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
        vsf_sysutil_unblock_sig(kVSFSysUtilSigHUP);
        new_client_sock = vsf_sysutil_accept_timeout(
                              listen_sock, p_accept_addr, 0);
        vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
        vsf_sysutil_block_sig(kVSFSysUtilSigHUP);
        if (vsf_sysutil_retval_is_error(new_client_sock))
        {
            continue;
        }
        ++s_children;
        child_info.num_children = s_children;
        child_info.num_this_ip = 0;
        p_raw_addr = vsf_sysutil_sockaddr_get_raw_addr(p_accept_addr);
        child_info.num_this_ip = handle_ip_count(p_raw_addr);
        new_child = vsf_sysutil_fork_failok();
        if (new_child != 0)
        {
            /* Parent context */
            vsf_sysutil_close(new_client_sock);
            if (new_child > 0)
            {
                hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr);
            }
            else
            {
                /* fork() failed, clear up! */
                --s_children;
                drop_ip_count(p_raw_addr);
            }
            /* Fall through to while() loop and accept() again */
        }
        else
        {
            /* Child context */
            vsf_sysutil_close(listen_sock);
            prepare_child(new_client_sock);
            /* By returning here we "launch" the child process with the same
             * contract as xinetd would provide.
             */
            return child_info;
        }
    }
}
Example #17
0
static void
common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
                int do_chroot, int anon)
{
  int was_anon = anon;
  const struct mystr* p_orig_user_str = p_user_str;
  int newpid;
  vsf_sysutil_install_null_sighandler(kVSFSysUtilSigCHLD);
  /* Tells the pre-login child all is OK (it may exit in response) */
  priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
  if (!p_sess->control_use_ssl)
  {
    (void) vsf_sysutil_wait();
  }
  else
  {
    p_sess->ssl_slave_active = 1;
  }
  /* Absorb the SIGCHLD */
  vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
  /* Handle loading per-user config options */
  handle_per_user_config(p_user_str);
  /* Set this before we fork */
  p_sess->is_anonymous = anon;
  vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, twoproc_handle_sigchld);
  newpid = vsf_sysutil_fork(); 
  if (newpid == 0)
  {
    struct mystr guest_user_str = INIT_MYSTR;
    struct mystr chroot_str = INIT_MYSTR;
    struct mystr chdir_str = INIT_MYSTR;
    struct mystr userdir_str = INIT_MYSTR;
    unsigned int secutil_option = VSF_SECUTIL_OPTION_USE_GROUPS;
    /* Child - drop privs and start proper FTP! */
    vsf_sysutil_close(p_sess->parent_fd);
    if (tunable_ssl_enable)
    {
      vsf_sysutil_close(p_sess->ssl_slave_fd);
    }
    if (tunable_guest_enable && !anon)
    {
      /* Remap to the guest user */
      str_alloc_text(&guest_user_str, tunable_guest_username);
      p_user_str = &guest_user_str;
      if (!tunable_virtual_use_local_privs)
      {
        anon = 1;
        do_chroot = 1;
      }
    }
    if (do_chroot)
    {
      secutil_option |= VSF_SECUTIL_OPTION_CHROOT;
    }
    if (!anon)
    {
      secutil_option |= VSF_SECUTIL_OPTION_CHANGE_EUID;
    }
    calculate_chdir_dir(was_anon, &userdir_str, &chroot_str, &chdir_str,
                        p_user_str, p_orig_user_str);
    vsf_secutil_change_credentials(p_user_str, &userdir_str, &chroot_str,
                                   0, secutil_option);
    if (!str_isempty(&chdir_str))
    {
      (void) str_chdir(&chdir_str);
    }
    str_free(&guest_user_str);
    str_free(&chroot_str);
    str_free(&chdir_str);
    str_free(&userdir_str);
    /* Guard against the config error of having the anonymous ftp tree owned
     * by the user we are running as
     */
    if (was_anon && vsf_sysutil_write_access("/"))
    {
      die("vsftpd: refusing to run with writable anonymous root");
    }
    p_sess->is_anonymous = anon;
    process_post_login(p_sess);
    bug("should not get here: common_do_login");
  }
  /* Parent */
  if (tunable_ssl_enable)
  {
    vsf_sysutil_close(p_sess->ssl_consumer_fd);
    /* Keep the SSL slave fd around so we can shutdown() upon exit */
  }
  vsf_priv_parent_postlogin(p_sess);
  bug("should not get here in common_do_login");
}
Example #18
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);
}
Example #19
0
struct vsf_client_launch
vsf_standalone_main(void)
{
  struct vsf_sysutil_sockaddr* p_sockaddr = 0;
  struct vsf_sysutil_ipv4addr listen_ipaddr;
  int listen_sock = vsf_sysutil_get_ipv4_sock();
  int retval;
  s_p_ip_count_hash = hash_alloc(256, sizeof(struct vsf_sysutil_ipv4addr),
                                 sizeof(unsigned int), hash_ip);
  s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
                               sizeof(struct vsf_sysutil_ipv4addr), hash_pid);
  if (tunable_setproctitle_enable)
  {
    vsf_sysutil_setproctitle("LISTENER");
  }
  vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0);
  vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup);

  vsf_sysutil_activate_reuseaddr(listen_sock);
  vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
  vsf_sysutil_sockaddr_set_port(
      p_sockaddr, vsf_sysutil_ipv4port_from_int(tunable_listen_port));
  if (!tunable_listen_address ||
      vsf_sysutil_inet_aton(tunable_listen_address, &listen_ipaddr) == 0)
  {
    listen_ipaddr = vsf_sysutil_sockaddr_get_any();
  }
  vsf_sysutil_sockaddr_set_ipaddr(p_sockaddr, listen_ipaddr);
  retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
  
  vsf_sysutil_free(p_sockaddr);

  if (vsf_sysutil_retval_is_error(retval))
  {
    die("could not bind listening socket");
  }
  vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);

  while (1)
  {
    struct vsf_client_launch child_info;
    static struct vsf_sysutil_sockaddr* p_accept_addr;
    int new_child;
    struct vsf_sysutil_ipv4addr ip_addr;
    /* NOTE - wake up every 10 seconds to make sure we notice child exit
     * in a timely manner (the sync signal framework race)
     */
    int new_client_sock = vsf_sysutil_accept_timeout(
        listen_sock, &p_accept_addr, 10);
    if (s_reload_needed)
    {
      s_reload_needed = 0;
      do_reload();
    }
    if (vsf_sysutil_retval_is_error(new_client_sock))
    {
      continue;
    }
    ip_addr = vsf_sysutil_sockaddr_get_ipaddr(p_accept_addr);
    ++s_children;
    child_info.num_children = s_children;
    child_info.num_this_ip = handle_ip_count(&ip_addr);
    new_child = vsf_sysutil_fork_failok();
    if (new_child != 0)
    {
      /* Parent context */
      vsf_sysutil_close(new_client_sock);
      if (new_child > 0)
      {
        hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, (void*)&ip_addr);
      }
      else
      {
        /* fork() failed, clear up! */
        --s_children;
        drop_ip_count(&ip_addr);
      }
      /* Fall through to while() loop and accept() again */
    }
    else
    {
      /* Child context */
      vsf_sysutil_close(listen_sock);
      prepare_child(new_client_sock);
      /* By returning here we "launch" the child process with the same
       * contract as xinetd would provide.
       */
      return child_info;
    }
  }
}