void vsf_parseconf_load_file(const char* p_filename) { 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"); } if (!s_strings_copied) { s_strings_copied = 1; /* A minor hack to make sure all strings are malloc()'ed so we can free * them at some later date. Specifically handles strings embedded in the * binary. */ copy_string_settings(); } retval = str_fileread(&config_file_str, p_filename, VSFTP_CONF_FILE_MAX); if (vsf_sysutil_retval_is_error(retval)) { die("cannot open config file"); } 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) == '#') { continue; } /* Split into name=value pair */ str_split_char(&config_setting_str, &config_value_str, '='); handle_config_setting(&config_setting_str, &config_value_str); } str_free(&config_file_str); str_free(&config_setting_str); str_free(&config_value_str); }
void install_str_setting(const char* p_value, const char** p_storage) { char* p_curr_val = (char*) *p_storage; if (p_curr_val != 0) { vsf_sysutil_free(p_curr_val); } if (p_value != 0) { p_value = vsf_sysutil_strdup(p_value); } *p_storage = p_value; }
static void copy_string_settings(void) { const struct parseconf_str_setting* p_str_setting = parseconf_str_array; while (p_str_setting->p_setting_name != 0) { if (*p_str_setting->p_variable != 0) { *p_str_setting->p_variable = vsf_sysutil_strdup(*p_str_setting->p_variable); } p_str_setting++; } }
int main(int argc, const char* argv[]) { struct vsf_session the_session = { /* Control connection */ 0, 0, 0, 0, 0, /* Data connection */ -1, 0, -1, 0, 0, 0, 0, /* Login */ 1, 0, INIT_MYSTR, INIT_MYSTR, /* Protocol state */ 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, 0, 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, /* write_enable */ 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 = -1; if (tunable_banned_email_file) { 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 = -1; if (tunable_email_password_file) { 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; }
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); }
const char* str_strdup(const struct mystr* p_str) { return vsf_sysutil_strdup(str_getbuf(p_str)); }
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; }